目的
ML管理したいが、そのためにメールサーバを設定するのはセキュリティとか色々と考慮する必要があり大変。
リレー先メールサーバさえ転送を受け付けてくれれば、発信専門のMLを構築できそう。
ということで、コンテナでサクッと発信専門のMLサーバ構築したので以下にその手順を示す。
注意事項
リレー先メールサーバが転送を受け付けてくれる前提であり、メールサーバ側の設定については関与しない。
発信専門であるため、発信元メアドに送信しても no such address となる。
docker compose 設定
docker-mailman/docker-compose.yaml を参考に設定。
変更点は以下:
- mailman設定関連をローカルフォルダに置くよう /opt/mailman → . に変更
- mailman-web アクセス用の webサーバとして nginx を設定
- nginx 以外は expose しないよう設定
- MTA用の postfix を設定
- postfix の転送信頼アドレスとして mynetwork に各サービスが属するよう IPアドレス空間(172.18.0.0/24)を設定
- SMTP_HOST に postfix サービスを指定
docker-compose.yaml
services:
mailman-core:
image: maxking/mailman-core:0.4 # Use a specific version tag (tag latest is not published)
container_name: mailman-core
hostname: mailman-core
restart: unless-stopped
volumes:
- ./core:/opt/mailman/
# - /opt/mailman/core:/opt/mailman/
stop_grace_period: 30s
links:
- database:database
depends_on:
database:
condition: service_healthy
environment:
- DATABASE_URL=${DATABASE_URL:-postgresql://mailman:mailmanpass@database/mailmandb}
- DATABASE_TYPE=${DATABASE_TYPE:-postgres}
- DATABASE_CLASS=${DATABASE_CLASS:-mailman.database.postgresql.PostgreSQLDatabase}
- HYPERKITTY_API_KEY=${HYPERKITTY_API_KEY:-someapikey}
- MTA=${MTA:-postfix}
#- MM_HOSTNAME=${MM_HOSTNAME:-}
- SMTP_HOST=${SMTP_HOST:-postfix}
- SMTP_PORT=${SMTP_PORT:-25}
- SMTP_HOST_USER=${SMTP_HOST_USER:-}
- SMTP_HOST_PASSWORD=${SMTP_HOST_PASSWORD:-}
- SMTP_SECURE_MODE=${SMTP_SECURE_MODE:-smtp}
- SMTP_VERIFY_HOSTNAME=${SMTP_VERIFY_HOSTNAME:-true}
- SMTP_VERIFY_CERT=${SMTP_VERIFY_CERT:-true}
# ports:
# - "127.0.0.1:8001:8001" # API
# - "127.0.0.1:8024:8024" # LMTP - incoming emails
networks:
- net_mailman
mailman-web:
image: maxking/mailman-web:0.4 # Use a specific version tag (tag latest is not published)
container_name: mailman-web
hostname: mailman-web
restart: unless-stopped
depends_on:
database:
condition: service_healthy
links:
- mailman-core:mailman-core
- database:database
volumes:
- ./web:/opt/mailman-web-data
# - /opt/mailman/web:/opt/mailman-web-data
environment:
- DATABASE_TYPE=${DATABASE_TYPE:-postgres}
- DATABASE_URL=${DATABASE_URL:-postgresql://mailman:mailmanpass@database/mailmandb}
- HYPERKITTY_API_KEY=${HYPERKITTY_API_KEY:-someapikey}
- SECRET_KEY=${SECRET_KEY:-}
- SERVE_FROM_DOMAIN=${SERVE_FROM_DOMAIN:-example.com}
- MAILMAN_ADMIN_USER=${MAILMAN_ADMIN_USER:-admin}
- MAILMAN_ADMIN_EMAIL=${MAILMAN_ADMIN_EMAIL:-admin@example.com}
- SMTP_HOST=${SMTP_HOST:-postfix}
# ports:
# - "127.0.0.1:8000:8000" # HTTP
# - "127.0.0.1:8080:8080" # uwsgi
networks:
- net_mailman
database:
environment:
- POSTGRES_DB=mailmandb
- POSTGRES_USER=mailman
- POSTGRES_PASSWORD=mailmanpass
image: postgres:12-alpine
volumes:
- ./database:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready --dbname mailmandb --username mailman"]
interval: 10s
timeout: 5s
retries: 5
networks:
- net_mailman
postfix:
build: ./postfix
hostname: postfix
container_name: postfix
environment:
- MAIL_DOMAIN=${MAIL_DOMAIN:-example.com}
volumes:
- ./postfix/main.cf:/etc/postfix/main.cf
- ./core/var/data/:/opt/mailman/core/var/data/
networks:
- net_mailman
nginx:
image: nginx:latest
ports:
- ${SERVICE_PORT:-80}:80
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./web:/opt/mailman-web-data
depends_on:
- mailman-web
networks:
- net_mailman
networks:
net_mailman:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.18.0.0/24
.env 設定例
SECRET_KEY=適当に設定
SERVE_FROM_DOMAIN=適当なドメイン名
## SMTPホスト検証をしたくないとき
SMTP_VERIFY_HOSTNAME=false
SMTP_VERIFY_CERT=false
## 管理者アカウント設定
MAILMAN_ADMIN_USER=管理者アカウント
MAILMAN_ADMIN_EMAIL=管理者メアド
mailman-web設定
CSRF検証を無効にするとき
管理者メアドの検証の際に django で mailman-web 稼働サーバのCSRF検証を行おうとするため、これを無効化する。
具体的には、 settings_local.py にて CSRF モジュールを外すよう settings.py 設定を上書きする。
デフォルトの MIDDLEWARE 設定は https://github.com/maxking/docker-mailman/blob/main/web/mailman-web/settings.py を見ればわかる。
./web/settings_local.py:
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'allauth.account.middleware.AccountMiddleware',
'django_mailman3.middleware.TimezoneMiddleware',
'postorius.middleware.PostoriusMiddleware',
)
ALLOWED_HOSTS 設定
Mailman-webのREADME には ALLOWED_HOSTS の設定のためにドメイン名(example.com)を SERVE_FROM_DOMAIN に設定するとあるが、実際はそのまま環境変数を設定しているため効かない。
正しくは ".ドメイン名" とすることでワイルドカードが効くようになる。
refs: DjangoのALLOWED_HOSTSの仕様
設定は上記と同じく settings_local.py に追加設定。
web/settings_local.py:
ALLOWED_HOSTS = [
'.localhost', '127.0.0.1',
'mailman-web',
'.適当なドメイン',
]
MTA(postfix) の設定
コンテナ作成
作り方は Dockerコンテナ内で、Postfixを使ってメール送受信を行う を参考。
postfix/Dockerfile:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
postfix \
# iputils-ping iproute2 net-tools dnsutils \
&& rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
postfix/entrypoint.sh:
#!/bin/sh
## postfix の DNS 参照は resolv.conf でなく postfix 用の resolv.conf を参照するため、サービス実行前にコピー
## refs: https://zenn.dev/flyingbarbarian/articles/5bb1d38b1ada40
cp /etc/resolv.conf /var/spool/postfix/etc/resolv.conf
#postfix start-fg
postfix start
### ログ監視をフォアグラウンド実行してサービス実行を継続
tail -F /var/log/maillog
postfix/main.cf:
postfixコンテナを一旦起動し、デフォルト設定をコピーして編集するとよい。
$ docker build . -t postfix
$ docker run -d --rm --name postfix postfix
$ docker cp postfix:/etc/postfix/main.cf .
デフォルト設定から以下を編集。
docker-compose で指定したネットワークアドレスを mynetworks に指定する。
これを設定しないと mailman-web や mailman-core からのメール転送を受け付けてくれない。
myhostname = postfix
mydomain = 適当なドメイン
mydestination = $myhostname, localhost.$mydomain, localhost
## メールのリレー先
relayhost = [転送先メールサーバ]
## docker composeサービス内のメール転送を受け付けるよう IP範囲を設定
mynetworks = 127.0.0.0/8 172.18.0.0/24
## ログをファイル出力
maillog_file = /var/log/maillog
## for mailman
# Support the default VERP delimiter.
recipient_delimiter = +
unknown_local_recipient_reject_code = 550
owner_request_special = no
transport_maps =
regexp:/opt/mailman/core/var/data/postfix_lmtp
local_recipient_maps =
regexp:/opt/mailman/core/var/data/postfix_lmtp
relay_domains =
regexp:/opt/mailman/core/var/data/postfix_domains
この状態で docker compose サービス内からメール送信できるようになる。
例えば mailman-web サービスから以下のように送信。
mailman-web# python
>>> from smtplib import SMTP
>>> smtp = SMTP('postfix')
>>> smtp.sendmail('from_addr@mailman-web', '適当なメアド', 'test message')
mailman-core 設定
postfix に設定するファイルを仮出力しておく。
$ mkdir -p core/var/data
$ touch ./core/var/data/postfix_lmtp
$ touch ./core/var/data/postfix_domains
nginx設定
nginx/conf.d/default.conf
proxy_pass で設定するか、 uwsgi_pass で設定するかはどちらでもよい。
server {
listen [::]:80;
server_name ML管理用webサーバ;
location /static {
alias /opt/mailman-web-data/static;
autoindex off;
}
location / {
proxy_pass http://mailman-web:8000 ;
# uwsgi_pass mailman-web:8080;
include uwsgi_params;
uwsgi_read_timeout 300;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
mailman-web の転送先アドレス設定
上記設定までで各サービスは動くが、パスワードリセット時のアドレスが
https://mailman-web:8000/accounts/password/reset/key/xxxxxxxxxx/
のようになり、
https://ML管理用サーバ/accounts/password/reset/key/xxxxxxxxxx/
に書き換えないとパスワードリセットができない。
これでは不便のため、nginx設定を以下のように書き換える。
nginx/conf.d/default.conf
server {
location / {
proxy_set_header Host $server_name:$server_port;
}
サービス起動
docker compose up するだけ。
$ docker compose up -d
これでサーバアドレスにアクセスして以下のような mailman3 画面が表示されればOK。