2021年1月16日土曜日

DockerでPostfixをコンテナ化してメールサーバを構築する

自宅のプロキシ/DNS/メールサーバをPacemakerによる冗長構成から、ロードバランサ (ZEVENET) + Dockerコンテナ構成への移行を進めている。

ロードバランサは導入済みなので、最終形として、プロキシ、DNS、メールのそれぞれの機能をDockerのコンテナ化を行うことを計画しており、今回はその第一弾として、メール機能であるPostfixのコンテナ化を行う

最終形として、プロキシ、DNS、メールのそれぞれの機能をDockerのコンテナ化を行うことを計画しており、今回はその第一弾として、メール機能であるPostfixのコンテナ化を行う

環境

今回のDocker環境構築時のソフトウエアバージョンを以下に記載する。

  • DockerホストOS : CentOS 8.2
  • Docer : 19.03.14
  • コンテナ用OSイメージ : centos:latest

Dockerホストとコンテナ間では、サービス提供に必要なポートの紐づけと、ログファイルを出力させるディレクトリの紐づけを行う。以下に簡単に図示しておく。

Postfixコンテナ化手順

1. Dockerのインストール

Dockerのインストールは別記事を参照。CentOS 7にDockerをインストールする内容となっているが、CentOS 8も同様の手順でインストールできる。

Dockerインストール後、Dockerfile等を配置するディレクトリを作成し、以後はそのディレクトリ内にすべてのファイルを配置する。

2. Postfix用の設定ファイルを準備

通常の家庭用のインターネットプロバイダを使っている場合、SMTP (TCP/25ポート) を使って外部へのメール送信はできないようになっている。この仕様をOP25B (Outbound Port 25 Blocking)と呼ぶが、PostfixでOP25Bに対応した設定をすることでメール送信が可能となる。OP25B環境におけるPostfixの設定は以下にて記事にしているで参照いただきたい。

コンテナにする場合も同様の設定を行えばよい。あらかじめ、以下の通り2つの設定ファイルを作成しておく。

main.cf

※★箇所は各環境に合わせて変更すること。

compatibility_level = 2
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix
mail_owner = postfix
inet_interfaces = all
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost
unknown_local_recipient_reject_code = 550
mynetworks = 127.0.0.0/8, 192.168.0.0/16 #★
relayhost = [<メールサーバのFQDN>]:587 #★
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

debug_peer_level = 2
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/sendmail.postfix
newaliases_path = /usr/bin/newaliases.postfix
mailq_path = /usr/bin/mailq.postfix
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /usr/share/doc/postfix/samples
readme_directory = /usr/share/doc/postfix/README_FILES
smtpd_tls_cert_file = /etc/pki/tls/certs/postfix.pem
smtpd_tls_key_file = /etc/pki/tls/private/postfix.key
smtpd_tls_security_level = may
smtp_tls_CApath = /etc/pki/tls/certs
smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
smtp_tls_security_level = may
meta_directory = /etc/postfix
shlib_directory = /usr/lib64/postfix
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/smtp_password
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = plain,login

smtp_password

※★箇所は各環境に合わせて変更すること。

[<メールサーバのFQDN>]:587 <ユーザ名>:<パスワード> #★

3. rsyslogとPostfix起動用のスクリプト (ラッパースクリプト) を作成

Postfixはログ出力にrsyslogの機能を使うため、rsyslogの起動とPostfixの起動という複数のプロセス起動を行う必要がある。複数のプロセス起動を行う場合の作法としては、ラッパースクリプトを作成して実行させるといった手法が定番のようだ。

今回は、以下の内容でstartup.shというスクリプトを作成した。メインとなるプロセス (今回はPostfix) の起動コマンドはexecを付与してPID 1で動作させるようにしている。

#!/bin/bash

/usr/sbin/rsyslogd
exec /usr/sbin/postfix start-fg

4. Dockerホストにログ出力先を作成

今回はコンテナのPostfixが出力したmaillogをDockerホストのディレクトリ出力するよう構成するため、以下の通りあらかじめログ出力先のディレクトリを作成しておく。

mkdir -p /var/log/docker/centos-postfix
chmod 777 /var/log/docker/*

5. Dockerfileを作成

コンテナの構成情報はDockerfileに記述する。いくつか記述内容にポイントがあるので以下表にまとめる。

命令 記述内容 説明
FROM centos コンテナのベースとなるイメージはCentOSを使用する。
ENV TZ タイムゾーンを日本標準時 (Asia/Tokyo) にする。
ENV http_proxy, https_proxy プロキシ環境の場合は、プロキシサーバを指定する。
RUN dnf 必要なパッケージのインストールを行う。Postfixのログはsyslogを使用して出力されるため、rsyslogを追加インストールする点に注意。
RUN sed, mkdir コンテナ環境であってもログを出力できるようにするため、/etc/rsyslog.confの設定ファイルを修正する。maillogの出力先を/var/log/postfix/maillogに変更し、ログの出力をjournald経由ではなくシステムソケットに変更している。また、Dockerホストのボリュームのマウントポイントとなるため、ログ出力先ディレクトリの作成しておく。
COPY - 事前に作成したmain.cfsmtp_passwordの設定ファイルを /etc/postfix/にコピーする。
COPY - プロセス起動用のstartup.sh/にコピーする。
RUN chmod, postmap, postalias 各種設定ファイルの権限設定とPostfixで必要となるDBファイルの作成を行う。
EXPOSE 25 SMTPの25番ポートを記述する。
CMD ["/startup.sh"] startup.shを起動する。[]で囲まずに記述すると/bin/sh -cが付与されてプロセスが実行されてしまうので、通常は必ず[]にてコマンドを囲むこと。

実際のDockerfileは以下の通りとなった。

FROM centos

ENV TZ=Asia/Tokyo \
    http_proxy=http://192.168.33.23:8080 \
    https_proxy=http://192.168.33.23:8080
RUN dnf install postfix cyrus-sasl cyrus-sasl-plain mailx rsyslog -y && \
    sed -i -e 's#/var/log/maillog#/var/log/postfix/maillog#g' \
           -e 's/SysSock.Use="off"/SysSock.Use="on"/g' \
           -e 's/^\(module(load="imjournal"\)/#\1/g' \
           -e 's/^\([ ]*StateFile="imjournal.state\)"/#\1/g' \
           /etc/rsyslog.conf && \
    mkdir /var/log/postfix


COPY main.cf smtp_password /etc/postfix/
COPY startup.sh /
RUN chmod 600 /etc/postfix/smtp_password && \
    postmap /etc/postfix/smtp_password && \
    postalias /etc/aliases && \
    chmod +x /startup.sh

EXPOSE 25
CMD ["/startup.sh"]

6. コンテナイメージを作成

作成したDockerfileを使って、docker buildコマンドにてコンテナイメージを作成する。

# docker build -t centos-postfix:1 .

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos-postfix      1                   9e3991eb23a8        36 hours ago        296MB

7. コンテナを起動

コンテナ起動時には、以下オプションの設定を行う。

設定内容 説明
--name コンテナの名称を設定。今回は「centos-postfix」という名前にする。
-p Dockerホストの通信ポートとコンテナの通信ポートを紐づけを行う。<ホストポート>:<コンテナポート>の書式で記載する。今回はSMTPのポート番号である25番ポートを紐づけるので、25:25で指定する。
-v Dockerホストのディレクトリ/ファイルとコンテナのディレクトリ/ファイルの紐づけを行う。<ホストディレクトリ/ファイル>:<コンテナディレクトリ/ファイル>の書式で記載する。今回はホストの/var/log/docker/centos-postfix/ディレクトリにコンテナの/var/log/postfixを紐づけることで、maillogログを出力させる。

上記をふまえ、以下の通りdocker runコマンドでコンテナの起動を行う。

# docker run -d -p 25:25 --name "centos-postfix" \
-v /var/log/docker/centos-postfix:/var/log/postfix \
centos-postfix:1

# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                          NAMES
f6bb3910351a        centos-postfix:1    "/startup.sh"            28 minutes ago      Up 28 minutes       0.0.0.0:25->25/tcp                             centos-postfix

8. logrotate設定

ホストに出力させたログをローテーションさせるため、logrotateの設定を行う。logrotateは/etc/logrotate.d/に設定ファイルを置けば自動で実行されるようになる。

logrotateの設定のポイントは2つ。

logrotateは元のファイルをリネームして、新規空ファイルを作成するという動作を行うが、この際にログ出力元となるプロセスのリロード等の処理が必要となる。しかし、ホスト側のログローテーション実行とコンテナ側のプロセスのリロードの同期ができないため、copytruncateオプションを付与する。copytruncateオプションを付与すると、元のファイルはリネームではなくコピーしたのち、ログファイルの内容を空にする方式でローテーションが実行される。
※本方式はタイミングによっては、ローテーション中に書き込まれたログがロストする可能性があるので注意。

また、logrotateの仕様により、ログ出力先のディレクトリの権限が777となっている場合、以下メッセージでローテーションが失敗する。このような場合でもログローテーションを実行できるようsu root rootを付与する。

error: skipping "/var/log/docker/centos-postfix/maillog" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.

上記をふまえ、以下のようにdocker-logというファイル名で設定ファイルを作成し、/etc/logrotate.d/に配置した。

# cat /etc/logrotate.d/docker-log
/var/log/docker/*/maillog
/var/log/docker/*/*.log
{
    rotate 7
    daily
    copytruncate
    dateext
    su root root
}

1日経過すると以下の通りログがローテーションされることが確認できた。

# ls -lh /var/log/docker/centos-postfix/
/var/log/docker/centos-postfix/:
-rw-r--r--. 1 root root 263K 12月 20 07:06 maillog
-rw-r--r--. 1 root root 1.2M 12月 20 03:17 maillog-20201220

以上でPostfixのコンテナ化が完了となる。

0 件のコメント:

コメントを投稿

人気の投稿