docker-postfixでDKIM対応送信専用SMTPリレーを立てる


概要

docker-postfixを使用してDockerのコンテナ内リンク(またはローカルネットワークやVPN)からの送信専用SMTPリレー(SMTPサーバ)を構築した際のメモ。

作者公式Dockerイメージをdocker-composeでハンドリングしてDKIMに対応させる。自身の用途としてはdocker-composeで構築したオンプレアプリ類からユーザーへのメール配信用。

なお、実績のないドメインからのメールはSPFもDKIMも関係なく問答無用で迷惑メール扱いされるのが普通で、アカウント云々の文言が含まれる場合は尚更である。ユーザーへの念押しは必須。

※RHEL8互換環境を想定、それ以外の場合は必要に応じて読み替えのこと

DKIM

docker-postfixではDKIMはoptionalのため、鍵は自分で用意する必要がある。今回はOpenDKIMを使用し、ホスト上で鍵を作成してvolumesでマウントする。

パッケージのインストール

DKIM鍵を生成するためにホストにOpenDKIMと依存パッケージをインストールする。

shell
dnf install -y opendkim perl-Getopt-Long
  • OpenDKIMはEPELに収録されている

鍵の生成

shell
mkdir -p /etc/opendkim/keys/<DOMAIN>
opendkim-genkey -b 2048 -d <DOMAIN> -D /etc/opendkim/keys/<DOMAIN> -s <SELECTOR> -v
  • <DOMAIN> は使用するドメイン名( exmaple.com 等)
  • <SELECTOR> はDKIM鍵の識別名のようなもの(例えば mail 等)
    • 指定しないと default になる
  • 実行すると秘密鍵が /etc/opendkim/keys/<DOMAIN>/<SELECTOR>.private に生成され、TXTレコードの内容が /etc/opendkim/keys/<DOMAIN>/<SELECTOR>.txt に出力される

DNS

DKIMレコード

出力された /etc/opendkim/keys/<DOMAIN>/<SELECTOR>.txt を元にDNSにDKIMレコードを追加する。OpenDKIMが出力するテキストはzoneファイルの書式のためbindならそのまま使える。

<SELECTOR>._domainkey IN TXT "v=DKIM1; k=rsa; p=MIIB......"
  • Type: TXT
  • Name: <SELECTOR>._domainkey
  • Value: v=DKIM1; k=rsa; p=MIIB......
    • /etc/opendkim/keys/<DOMAIN>/<SELECTOR>.txt の二重引用符の中身を連結したもの
  • TTL: 適当

LuaDNSにおける設定例

SPFレコード

わかりやすい解説が大量にあるため検索して参照のこと。

<DOMAIN>. IN TXT "v=spf1 +ip4:<GLOBAL_IP>/32 -all"
  • 修飾子 + - ~ ? はその条件に一致した場合の扱いを規定する
    • + : 認証する(省略可)/ - : 認証しない/ ~ : 認証しないが受信推奨/ ? : 認証の有無を評価しない
  • よって設定した条件以外で送る可能性が全くない場合、allディレクティブの修飾子は ~ ではなく厳格な - の方がより安全

LuaDNSにおける設定例

Composeファイル

environmentの項目はdocker-postfixのreadmeを参照のこと。

コンテナ間リンク用設定例

docker-compose.yml
version: '3.8'

services:
  smtp:
    image: panubo/postfix:latest
    container_name: smtp
    hostname: smtp-server
    environment:
      - MAILNAME=<DOMAIN>
      - TZ=Asia/Toyo
      - USE_TLS=no
      - USE_DKIM=yes
      - DKIM_KEYFILE=/etc/opendkim/keys/<DOMAIN>/<SELECTOR>.private
      - DKIM_SELECTOR=<SELECTOR>
    expose:
       #- '25'
       - '587'
    volumes:
      - /etc/opendkim:/etc/opendkim
    networks:
      - container-link
    logging:
      options:
        max-size: "10m"
        max-file: "3"

networks:
  default:
    external:
      name: bridge
  container-link:
    name: docker.internal
  • 各コンテナで指定するSMTPサーバのアドレスはdocker-postfixコンテナのhostnameと待ち受けポートになる
    • この設定例では smtp-server:587 を指定することになる
  • 許可するIP範囲は明示的に指定しないと 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 に設定される
    • コンテナ間リンクで使用する場合はそのままでOKと思われる
  • TLSは標準でON( USE_TLS=yes )のため、不要なら明示的に切る必要がある
    • コンテナ間リンク内で閉じているなら不要と思われる
  • DKIMを有効化し、先の手順で作成したDKIM鍵をマウントして使用する
  • コンテナ間リンクに明示的に命名したbridgeネットワークを使用している
    • 立てたSMTPサーバを使用するコンテナはこのネットワークに繋げること
  • 当然ながらメールにはSMTPのチェーンの始点となる送信元コンテナのhostnameとIPが記載される
  • 内部アクセス専用のため認証はない

その他設定例

ローカルIP 10.0.0.1 のアダプタ上でIPアドレス範囲 10.0.0.0/24 からのアクセスのみ待ち受ける

docker-compose.yml
    environment:
      - MYNETWORKS=10.0.0.0/24
    ports:
      - '10.0.0.1:587:587'

TLSの使用を強制し、Let's Encryptの証明書と鍵を使用(使用するドメインで繋がるようにしておく必要がある)

docker-compose.yml
    environment:
      - USE_TLS=yes
      - TLS_SECURITY_LEVEL=encrypt
      - TLS_KEY=/etc/letsencrypt/live/<DOMAIN>/privkey.pem
      - TLS_CRT=/etc/letsencrypt/live/<DOMAIN>/fullchain.pem
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt

実際のヘッダー

Gmailに送ったもの。

  • s=mail<SELECTOR> のこと
  • Received に送信元コンテナのホスト名とIPが入っている