5 分钟部署完整邮件服务器

原因

  1. 最初的原因是我想好好的使用 emacs 来收邮件
  2. 我本来是想使用 offlineimap 来先下载,然后通过 emacs 进行操作,这样是最快的,但 offlineimap 下载后我不知道如何能做成服务的形式,供不同的设备进行操作。
  3. 接着的原因使 emacs 收邮件慢最大的问题是网络连接慢,比如 gmail 即使翻墙还经常报错
  4. 还有一层慢的原因是邮箱多了,连接方式太多

总之, 在看了各种资料后,觉得还是邮件代收比较靠谱,但代收我又不想使用其他的邮件提供者,大部分基于邮件被拦,邮箱容量,无法完全操控等等原因,因此觉得还是自建吧。

自建最好支持 imap,这样在任何一个客户端中操作后,都会同步到其他的客户端。

于是,我就开始倒腾 mailu1了。

此处大约过了 3 分钟了。

安装

wget https://mailu.io/_downloads/docker-compose.yml
wget https://mailu.io/_downloads/.env

修改.env 中的相关变量,比如邮件保存的地址,,前端,域名,tls 等然后

docker-compose up -d

安装就完成了。为了创建一个管理帐号,先在命令行下手动创建

docker-compose run --rm admin python manage.py admin root example.net password

此处大约又过了 2 分钟了。

结束

安装已经结束了。对于很多人而言,整个系统就已经可以用了,在我这边还有些问题。

nginx 反向代理 ssl 的问题

我使用的使 let’s encrypted 的证书,前端的 nginx 上部署了证书,所以在 front 部分的那个 ssl 就无用途了,但邮件的 143 等端口也要用该证书。443 就更不用说了,这样虽然没有什么问题,但我一台服务器的 443 端口绑了好几个走 https 的域名,因此,后台反射的代理必须地址必须指向 80 的非加密监听地址上。TLS_FLAVOR 部分也仅仅设置成了 mail.由此引发了第二个问题。

nginx 反向代理跳转 url 不正确

前端代理写好后,url 一访问就默认跳转到//localhost 开头的了。而且不带端口。这个问题可以覆盖 front 下的/conf/nginx.conf 文件来解决,就是将 docker image 中的/etc/nginx.conf 中的$host 变量修改为$http_host,这样即使是特殊端口也跳转正确了。

邮件客户端的设置

推荐 imap 的方式,接收服务器和发送服务器都是指定的域名,账户名是用户名带域名后缀的。接收没有什么问题,关键使发送。发送的 SMTP 要求验证,端口为 465 SSL/TLS (outlook2006), evolution 的发送部分为专用端口的 SSL,检查支持的类型为 PLAIN

webmail 等入口

  1. 域名/webmail 为 webmail 入口
  2. 域名/admin 为管理入口

let’s encrypt 证书的问题

outlook2006 未提示,evolution 则提示是否信任证书,wordpress 的 phpmail 更是直接报错(我用的使 docker 镜像部署),这个可能是 let’s encrpt 证书的问题2,未做深究 wordpress 搜索”$phpmailer = new PHPMailer( true );”,然后添加下面的代码3

$phpmailer->SMTPOptions = array(
            'ssl' => array(
                'verify_peer' => false,
                'verify_peer_name' => false,
                'allow_self_signed' => true
            )
        );

win10 emacs 最佳体验

我在win10上运行emacs, 总是出现不响应,比如,打开某个很小的文本文件,或者打开某个目录,然后就没反应了。如果接着按C-g, emacs就白屏了。即使等上一天,也是不响应状态。

最近看到消息,说1709版本的WSL非常不错,正好我的电脑已经升级到这个版本,尝试了下。

之前的半屏无法放大的问题已经没有了。但中文输入法还是无法使用win10 原生的,幸好,我的emacs配置中预防了没有中文输入法的情况。只是相对而言,有些不太方便,我比较习惯用shif来切换中英文。

xming+wsl的emacs简直是绝配。我已经决定将通过mysys2安装的emacs卸载了

cryptsetup 小脚本

过于复杂的密码很容易丢失,即使使用密钥管理器也容易发生误操作或者被迫暴露。而密钥文件其实也并非那么的可靠。

cryptsetup的命令真的很长。我写了个小脚本(crypt.sh)简化下。

#!/bin/bash

KEYFILE=""
HEADFILE=""
KEYFILEOFFSET=0
DEVICE=""
S_POS=0
PAYLOAD=0
ACTION=""
CIPHER="serpent-xts-plain64"
CRYPTSETUP="cryptsetup"
EXTEND=""
BLOCKSIZE=4194304
N=1

function convert() {
local ret=`echo $1 | awk '/[0-9]$/{print $1;next};/[gG]$/{printf "%u\n", $1*(1024*1024*1024);next};/[mM]$/{printf "%u\n", $1*(1024*1024);next};/[kK]$/{printf "%u\n", $1*1024;next}' `
echo "$ret"
}

function get_args() {
  while getopts "k:h:d:p:s:c:eb:n:m:" arg
    do 
      case $arg in
        k)
        KEYFILE=$OPTARG
        ;;
    h)
      HEADFILE=$OPTARG
      ;;
    d)
      DEVICE=$OPTARG
      ;;
    p)
      PAYLOAD=$(($(convert $OPTARG) / 512))
      ;;
    s)
      S_POS=$(convert $OPTARG)
      ;;
    c)
      CIPHER=$OPTARG
      ;;
    e)
      EXTEND="extend"
      ;;
    b)
      BLOCKSIZE=$(convert $OPTARG)
      ;;
    n)
      N=$OPTARG
      ;;
    ?)
      echo "unknow argument"
      exit 1
      ;;
    esac
      done
}

function open() {
  $CRYPTSETUP open --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE $DEVICE `basename $DEVICE`
}

function close() {
  $CRYPTSETUP close `basename $DEVICE`
}

function format() {
  $CRYPTSETUP luksFormat --cipher $CIPHER --key-size 512 --keyfile-size $KEYSIZE --hash sha512  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE --align-payload $PAYLOAD $DEVICE
}

function dump() {
    $CRYPTSETUP luksDump --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET $HEADFILE
}

function extend() {
  dd if=$KEYFILE of=/dev/shm/`basename $KEYFILE` bs=$BLOCKSIZE count=1 skip=1
    HEADFILE=/dev/shm/`basename $KEYFILE`
}

function quit() {
return 0
}

function extendquit() {
rm -rf /dev/shm/`basename $KEYFILE`
}

ACTION=$1
shift
get_args $@
$EXTEND
echo $KEYFILE
echo $HEADFILE
KEYOFFSET=$((`od $KEYFILE -N $N -tu4 -j $S_POS | grep [^0$N] | awk '{print $2}'` * 8))
KEYSIZE=$((BLOCKSIZE - KEYOFFSET))
$ACTION
${EXTEND}quit

使用方法如下

crypt.sh action -k key -h header -d /dev/sdx -p xG -s offset -c cipher -b blocksize -e -n N

脚本只支持key文件的方式。脚本使用key文件的offset* 8位置的数值作为在key文件中的偏移,默认为1个字节,可以通过设置N的值来修改字节的大小,比如N为2就是两个字节的值*8作为最终的偏移,N越大,则密钥文件需要的也越大,因为表示偏移位置的数字范围也越大。我比较喜欢使用随机生成的文件,因此,这个最终的偏移是多少,除了打开文件,具体计算,我也不清楚,但只需要记住初始的值offset就可以了。

blocksize为密钥文件的大小, 单独可能没太大用,但如果将key文件和header文件直接拼接起来,带上参数-e, 则后续的操作(format除外), 就不需要使用header文件了。脚本会将header文件解出来,放到/dev/shm下面,使用完之后,再删除掉它, 这个时候, blocksize就很有用了。

-p 对应的是align-payload 参数,支持偏移,这个值和blocksize, offset 一样, 支持K,M,G这样人类可看的格式。 action可以为 open, close, format, dump 对应的使cryptsetup的 open, close, format,luksDump

这样一来, 主要只用记住密钥文件的偏移即可。

kubernetes的又一次尝试

之前使用docker-swarm,刚开始觉得还不错,但发现经过一段时间后,或者timeout,或者内存过高崩溃,总之,各种各样。

因此,这段时间,我一直使用的是传统的方式,开了一大堆端口号。

这两天又重新拾起了kurbernetes, 使用的环境是ubuntu 16.04, kubeadm的版本是1.7.4。

整个的安装也还是哗哗的,很顺利的结束了。

calico的安装也是哗哗的,很顺利的样子。

pod 同样的结果。

开测!!!

悲剧的挂了,测试静态的页面访问,无问题,一切正常。

但涉及到数据库/redis就timeout了。看了日志,无法连接上。

进入容器,发现无法连接上数据库,redis,过程略,总之,无法连接主机所在的局域网上的主机,当然,除了部署所在的主机除外。

数据库在局域网上,redis也在局域网上。

calico查了许久,不知道如何解决。

iptables 看了下,这个我没有研究过,更加不懂。

总之,经过了一天的研究,反复重装了N次,一直如此。最后没办法了,iptables转发吧,这总可以了吧。

iptables转发很快的配置好了,在主机上测试,竟然不通,该开的都开了啊。

最终发现,这个测试不能在开转发本机上测试,使用其他主机访问转发的端口就无问题,但主机自身不可,泪奔阿。

然后,发现pod中也可以访问数据库了,也可以访问redis了,一切正常了。

从理论上而言,可能要将指定的端口打开,但我将iptables的所有的INPUT, OUTPUT, FORWARD都接受了,还是不可以。只有使用iptables转发指定的端口内容才可以。

总之,目前都正常了。小测了一下,在相同的环境下,和docker bridge的效率几乎不相上下,甚至会更好些。剩下的就看能撑多久了。之前测试时发现网络经常连着连着就不通了。这次还有待时间检验