Ansible を使ったメール送信/転送サーバ構築

# GOAL

>Ansible を使って OS X から RaspberryPi 3 (Rasbian) 上にメールサーバを構築する。ただし送信と受信転送のみでメールボックスは不要。

宅鯖の Web サイト上で管理者のメールアドレスを公開する必要が出てきたので余っている RPi 上にメールサーバを構築した時の方法を記述します。受信したメールは全て普段使っている gmail アドレスに転送するので、まぁ会社の代表電話のようなものですね。

具体的なゴールは以下の3点となります。似たような要件であれば playbook を少し修正して対応可能かと思います。

1. LAN 内 192.168.0.0/16 のPCやサーバから外部にメールを送信する SMTP サーバとする。
2. 管理している複数のサイトの postmaster@foo.com, postmaster@bar.org, postmaster@baz.jp 宛てのメールが全て qux@gmail.com に転送される。
3. 受信メールは全て転送するため mailbox としての機能は必要ない (POP3 や IMAP4 は不要)。

構成対象の RaspberryPi は IP アドレスやホスト名、それに `raspi-config` などの基本的な設定が済んでいるものとします。この文書では IP アドレス 192.168.100.1、ホスト名 garnet02 とします。

```sh
$ cat /etc/hostname
garnet02
$ cat /etc/hosts
...
192.168.100.1 garnet02
```

また OS X 上で Ansible が実行可能な状態で構成されているものとします。まだ Ansible が入っていない場合は以下の URL を参照してください。

* [Install Ansible on Mac OSX](https://valdhaus.co/writings/ansible-mac-osx/)
* [Latest Releases Via Pip](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip)

# 構築手順

メールサーバにする RPi を inventory ファイルに既述します。

```inventory
[mail-server]
192.168.100.1
```

以下の playbook ファイルを作成し `playbook.yml` で保存します。`vars` で定義した変数値は後述する template の内容に挿入されますので適当に変更してください。

```yaml:playbook.yml
- hosts: mail-server
  sudo: yes
  gather_facts: no
  vars:
    hostname: garnet02
    myhostname: mail.foo.com
    mydomain: foo.com
    mydestination: bar.org, baz.jp, {{ hostname }}
    mynetworks: 192.168.0.0/16
    forward_to: qux@gmail.com
  tasks:
    - apt: upgrade=full update_cache=yes
    - apt: name=postfix state=present
    - template: src=main.cf.j2 dest=/etc/postfix/main.cf
    - template: src=aliases.j2 dest=/etc/aliases
    - command: /usr/bin/newaliases
    - command: /etc/init.d/postfix restart
```

`vars` 値は postfix の `main.cf` と同じですが `localhost` など自明な値は template 側に直接記述しています。

| 名前 | 意味 |
|:----------|:-------------|
|myhostname|このサーバのFQDN|
|mydomain|LAN内から外部へ送信する時に付加するドメイン|
|mydestination|宛先ドメインがこのどれかと一致したらこのサーバ宛て(それ以上リレーしない)|
|mynetwork|送り元がここに含まれたら外部へリレーして良い|
|forward_to|受信したメールの転送先メールアドレス|

local から local 宛のメールは xxx@garnet02 のように `hostname` のドメインとなるため、`mydestination` には ``{{ hostname }}`` を付加する必要があります。

テンプレートに使用しているのは以下の 2 ファイル。コメントを省略して最小限の部分だけにスリム化しているので、コメントも必要なら RPi から直接持って来て編集してください。

```jinja:main.cf.j2
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = {{ myhostname }}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, {{ mydestination }}
relayhost = 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ mynetworks }}
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
home_mailbox = Maildir/
```

```jinja:aliases.j2
# See man 5 aliases for format
postmaster:    {{ forward_to }}
```

# 実行

以下を起動して `pi` ユーザの例のパスワードを入力するだけ。

```sh
$ ansible-playbook -i inventory -u pi --ask-pass playbook.yml -vvvv
```

以上。