2023/01/09(月)dovecot に謎のエラー

2023/01/08 18:21 サーバ運営・管理
このメールサーバのみに出る固有のエラーメッセージだが・・・
Jan  8 17:31:43 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 2 pending requests: EOF
Jan  8 17:31:44 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 1 pending requests: EOF
Jan  8 17:31:47 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 2 pending requests: EOF
Jan  8 17:31:47 mx2 syslogd: last message repeated 1 times
Jan  8 17:41:42 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 1 pending requests: EOF
Jan  8 17:41:42 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 2 pending requests: EOF
Jan  8 17:41:42 mx2 dovecot[57447]: auth: Error: auth client 0 disconnected with 1 pending requests: EOF
Jan  8 17:41:42 mx2 syslogd: last message repeated 3 times
Jan  8 17:51:42 mx2 syslogd: last message repeated 1 times
Jan  8 17:51:44 mx2 syslogd: last message repeated 6 times
どうやら、外国のWebサイトにて Apple Mail クライアントの行儀の悪さが原因だという記事を見かけました。
しかしながら、接続元情報が一切出てこない上に、関連付けが出来る他の情報も無いため、断定しきれないですね。

「電子メールの送受信が出来ないという不満が出て来なければ、気にする必要無い」と結論付けていて、それはその通りとも言えますが、原因がハッキリしないエラーメッセージはあまり気分のよいものではないです。

Mac 環境での電子メール設定は、時折問い合わせを受けるものの、問題特定できない内容ばかりで都度悩むのですが、一方で、問い合わせ側の試行錯誤でいつの間にか解決してしまうので、成功事例みたいなものが共有できないんです。

かといって、手本を示すのと、検証の為だけにMac を買うということには絶対にならないので、悩ましいところですね。

2023/01/08(日)whois に障害??

2023/01/08 17:45 サーバ運営・管理
本日 日本時間 17:00 くらいから、こんな感じ:
mx2-root[97]# whois basekernel.co.jp
whois: connect(): Operation timed out
mx2-root[98]# whois basekernel.co.jp
whois: connect(): Operation timed out
メンテナンス情報とか見当たらないし、こういう事態は初めて、、

〔2023/01/08 17:51 追記〕
 キャッシュDNS再起動したら復旧。。orz 何だったのだろう??

2023/01/06(金)fml4 → fml8 へのアップデート

2023/01/06 5:00 サーバ運営・管理
事前調査では、なんだか面倒くさそうな記述が結構多かったんですが、
本家のチュートリアルを見る限り、そんな印象は受けなかったので、本家のチュートリアルを参考に実施しています。
先ずは fml8 を新規インストールします。(2022/12/31(土) fml8 の新規インストール 参照)
fml4 既存MLの fml8 移行の場合は、改めて fml8 で新規ML作成の必要は基本的に無しです。勝手に fml4 環境を上書きされることも無く、fml4 が既存でも問題はありません。

https://www.fml.org/software/fml8/ にて、チュートリアル → レシピ一覧 fml4 のMLを fml8 形式のMLへ変換する のリンクを辿っても閲覧出来ない。
チュートリアルページの下の方に
『II. fml のセットアップ~MLの作成』の項目があって、その下の fml4 のMLを fml8 形式のMLへ変換する
https://www.fml.org/software/fml8/Documentation/ja/tutorial/mergeml.fml4to8.html のほうを参照ください。

とはいえ、本家のチュートリアルどおりでは成功しませんので、下記手順を参考にどうぞ:
※ fml4 → fml8 移行に関しては、root ユーザで実施するのが無難です。
# makefml newdomain example.com /var/mail/example.com/~ml
( makefml newdomain バーチャルドメイン名 バーチャルドメインML 格納ディレクトリ )
既存の fml4 で運用している、バーチャルドメインにおけるML一式を格納しているディレクトリを指定します。
既存環境に上書きされることはなく、fml8 環境設定ディレクトリに対応付けが設定されるのみです。
尚、これは該当バーチャルドメイン上に複数のMLがあっても、最初の一度だけ実施でよいです。
23/01/06 00:43:01 makefml[1766] warn: /var/mail/example.com/~ml already exist. reuse it.
といったメッセージが出ますが、このメッセージは無視して大丈夫です。
次に、
# makefml mergeml ml-name@example.com /var/mail/example.com/~ml/ml-name
# cd /var/mail/example.com/~ml/ml-name
# chown fmladmin *
# chown -R fmladmin var
makefml mergeml にて、既存の fml4 で運用している、バーチャルドメインにおけるメーリングリスト個別ディレクトリを指定します。
makefml mergemlで、fml8 に適応したコンバートが行われます。
ファイル・ディレクトリ所有者が、一部 root になってしまうので、全て fmladmin (fml8 の実行権限ユーザ) に変えます。
postfix の場合、 /etc/mail/aliases /usr/local/etc/postfix/virtualtbl などにML関係の設定をしている場合は、必ず、それらを削除することを忘れないでください。
《※ postfix の設定を変更》
# vi /usr/local/etc/postfix/main.cf

《下記の例のように、alias を追加》
alias_maps = hash:/etc/mail/aliases
             hash:/var/mail/example.com/~ml/etc/mail/aliases

virtual_alias_maps = hash:/usr/local/etc/postfix/virtualtbl/forwardlist
                     hash:/var/mail/example.com/~ml/etc/postfix/virtual
# vi /etc/mail/aliases
《下記を追加》
fmladmin:       root

《追加後、下記を実行》
# newaliases
上記、makefml mergeml の実行で、雛形ファイルが自動生成されますので、それを流用します。
ただし、そのままでは使えないため、一部変更します:
# cd /var/mail/example.com/~ml/etc/postfix
# vi virtual
----- 以下、変更 -----
# example.com is one of $mydestination
# CAUTION: DO NOT REMOVE THE FOLLOWING LINE.
# example.com     example.com         ← コメントアウトまたは削除

### <VIRTUAL test-ml@example.com ML> ###

# 以下の5行に、@realmx.example.jp (postfix main.cf で指定の myhostname で示す実ホスト名)を追加
test-ml@example.com               test-ml=example.com@realmx.example.jp
test-ml-ctl@example.com           test-ml-ctl=example.com@realmx.example.jp
test-ml-request@example.com       test-ml-request=example.com@realmx.example.jp
test-ml-admin@example.com         test-ml-admin=example.com@realmx.example.jp
test-ml-error@example.com         test-ml-error=example.com@realmx.example.jp

### </VIRTUAL test-ml@example.com ML> ###
----- 変更はここまで -----

※ MLごとの個別変更で、題名に通し番号を追加する(makefml mergeml を使用しても、環境によっては引き継がれない設定がある。弊社環境ではこれが発生した)

# cd /var/mail/example.com/~ml/text-ml
# vi config.cf

----- 以下を、変更または追加(位置的には =cut 行の前) -----
article_header_rewrite_rules += rewrite_article_subject_tag
article_subject_tag = [$ml_name:%05d]
----- 変更はここまで -----
※ この設定で[test-ml:12345] みたいなのがメールタイトルの先頭に自動追加されるようになります。

# postmap hash:virtual
# postfix reload
これで、元通り fml8 での運用が出来るようになりました。
このアップデートは、ML利用者側から見れば、特に利点はありません。
fml4 が運営側にとって、維持困難になってきているので、主な目的はその解消といったところですね。

2023/01/04(水)セカンダリメールサーバの再構築

2023/01/04 6:22 サーバ運営・管理
セカンダリメールサーバは通常、利用者に開放しているメールサーバ(通常、プライマリメールサーバと称す)が不意にサービス不能状態になった時や、メンテナンス等で一時停止した際など、外部からの電子メールを代わりに且つ一時的に受け取るメールサーバです。
プライマリメールサーバの負荷が高く、応答が遅い時などもセカンダリメールサーバが活用される場面があります。

最近はセカンダリメールサーバを用意していない組織もあるようですが、弊社ではセカンダリメールサーバを備えています。
セカンダリメールサーバは、実は色々な構成があり、プライマリメールサーバのように、電子メールの発信のみを出来るようにしているものもあります。

弊社では、セカンダリメールサーバから電子メールの発信を出来るようにしていませんが、今回はプライマリメールサーバの負荷軽減目的で、明らかなコンピュータウィルスや、確実な spam メールを捨てる仕組みを設けました。
20230103.png

※ この図は、Postfix やメールサーバの構成をそのまま示した図ではありません。

受信メッセージの流れを中心とすると、上記のように感じかなと考えています。
milter-manager で受信メッセージのコンピュータウィルス検査と spam チェックを行います。
実はこれ、Postfix のオンラインマニュアル等を読んでもどうもよく判らないんです。たぶん、こんな感じかなという想像図。。
「outbound」というのは、プライマリメールサーバなどへ送信する場面を想定しています。

上記セカンダリメールサーバ固有の設定

先ず、spamassassin を milter インタフェースで動作させるために、spamass-milter を導入します。
一応、http://savannah.nongnu.org/projects/spamass-milt/ が公式サイトのようで、ダウンロードも出来ますが、永らく更新されておりません。
Ver 0.4.0 をダウンロードしてインストールしましたが、Ver 4.0.0 の spamassassin との組み合わせでも使えるようです。
# tar xvzf spamass-milter-0.4.0.tar.gz
# cd spamass-milter-0.4.0
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include'
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib'
# ./configure --localstatedir=/var
# gmake
# gmake install
とすると、インストール完了です。
milter 形式だと、サーバ単位での spam 判定になってしまうため、少し緩め閾値で spamassassin の挙動を設定しておきます。
サーバ単位での spamassassin の設定は、/etc/mail/spamassassin/local.cf で設定します:
※ /etc/mail/spamassassin/local.cf の内容(変更部分のみ抜粋):

rewrite_header Subject
report_safe 0
required_score 17.0
経験上、閾値 17.0 では、殆どの spam は通過してしまいます。15.0 未満が妥当ですが、運用しながら、様子見で少しずつ下げていくようにします。

以下、FreeBSD 固有の環境になりますが、下記のスクリプトを /usr/local/etc/rc.d 配下に spamass-milter と言う名前で作成しておきます:
#!/bin/sh

# PROVIDE: spamass-milter
# REQUIRE: LOGIN
# BEFORE: mail
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable spamass-milter:
#
#spamass_milter_enable="YES"
#
# See spamass-milter(8) for flags.
#

. /etc/rc.subr

name=spamass_milter
rcvar=spamass_milter_enable

command=/usr/local/sbin/spamass-milter
required_dirs=/usr/local/share/spamassassin

start_postcmd=start_postcmd
stop_postcmd=stop_postcmd

start_postcmd()
{
  sleep 1

  /usr/sbin/chown ${spamass_milter_socket_owner}:${spamass_milter_socket_group} ${spamass_milter_socket}
  /bin/chmod ${spamass_milter_socket_mode} ${spamass_milter_socket}
}

stop_postcmd()
{
  rm -f ${spamass_milter_socket}
}

load_rc_config $name
: ${spamass_milter_enable="NO"}
: ${spamass_milter_socket="/var/run/spamd/spamass.sock"}
: ${spamass_milter_flags="-f -p ${spamass_milter_socket} ${spamass_milter_localflags}"}
: ${spamass_milter_socket_owner="spamd"}
: ${spamass_milter_socket_group="mailuser"}
: ${spamass_milter_socket_mode="660"}

run_rc_command "$1"
※注:このスクリプトは Postfix のみの対応です。sendmail が MTA だと、不具合起こすと思います。

上記スプリプトに実行権を与え、更に /etc/rc.conf へ
spamass_milter_enable="YES"
の1行を追加しておきます。

ClamAV と、milter-managerも、インストールしておきます。過去記事を参考にしてください。
milter-manager をインストール後、
# /usr/local/sbin/milter-manager -u milter-manager --show-config
とやってみて、
define_milter("clamav-milter") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:44
  milter.connection_spec = "unix:/var/run/clamav/clmilter.sock"
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
  milter.enabled = true
  # default
  milter.fallback_status = "accept"
                  ・
                  ・
                  ・
define_milter("spamass-milter") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:44
  milter.connection_spec = "unix:/var/run/spamd/spamass.sock"
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
  milter.enabled = true
  # default
  milter.fallback_status = "accept"

といった表示がされるのを必ず確認しておきます。

最後に、Postfix 側の設定で、milter と関連するもののみ抜粋すると、
milter_protocol = 6
milter_default_action = accept
milter_mail_macros = {auth_author} {auth_type} {auth_authen}
smtpd_milters = unix:/var/run/milter-manager/milter-manager.sock
non_smtpd_milters = unix:/var/run/milter-manager/milter-manager.sock

transport_maps = hash:/usr/local/etc/postfix/virtualtbl/transport.map
header_checks = pcre:/usr/local/etc/postfix/header_checks
※ /usr/local/etc/postfix/virtualtbl/transport.map の内容:

example.jp                 smtp:[primary1.example.net]
example.net                smtp:[primary2.example.net]

※ /usr/local/etc/postfix/header_checks の内容:

/^From:\s*<>/m                  DISCARD
/^X\-Spam\-Flag:\s+YES/m        DISCARD
/^X\-Virus\-Status:\s+Yes/m     DISCARD

/^X\-Spam\-Level:/m             IGNORE
/^X\-Spam\-Checker\-Version:/m  IGNORE
/^X\-Spam\-Status:\s+No/m       IGNORE
/^X\-Virus\-Scanned:/m          IGNORE
/^X\-Virus\-Status:\s+Clean/m   IGNORE
のような感じ。
# postmap hash:/usr/local/etc/postfix/virtualtbl/transport.map
を忘れずに実施しておきます。

上記の、/usr/local/etc/postfix/header_checks の簡単な説明ですが、
「DISCARD」で終わる行は、電子メールヘッダ部分にて、左辺のパターンが出現したら「配信しました」と送信元に返答しつつ「メッセージを捨てる」という処理を行い、
「IGNORE」で終わる行は、電子メールヘッダ部分にて、左辺のパターンが出現したら「そのヘッダ行は削除して最終配信先へ送る」という処理を行います。
このファイルに記述された内容は、上から下へ順番に処理しますが、「DISCARD」で記述した行は、「そこで打ち切る」という挙動になるらしいです。

この構成で稼働させているが・・・

どうやら、header_checks の設定が上手く反映できていないみたいなのです。
IGNORE で「ヘッダ行削除」の挙動がどうも上手く行っていない模様。
X-Virus.... は上手く行っているが、 X-Spam.... が上手く行かない。。

取り敢えず実害はないのですが、セカンダリメールサーバ経由で受信したメッセージは、
X-Spam... は、受信者から見ると2回出てくることになり、不自然なのでどうにかしたいのです。
バグなのか、設定の問題なのか、、、 困っているところです。

2022/12/31(土)fml8 の新規インストール

fml は、昔は最も多く利用された電子メールによるメーリングリストの配送支援ソフトウェアです。
fml4 は、今となっては運用に難があり、fml8 (作者の趣味・志向で 4 → 8 というビットシフト的バージョンアップを企てている)が、後継ですが、長らくリリース候補版(7.99.1) です。

fml8 インストールの準備

fml8 は、Perl で組まれているため、運用には必然的に下記が必要です:
 perl5-5.36.0_2 (Ports カテゴリ:lang)   ※注:FreeBSD Ports におけるパッケージ表記です。
Perl 5.26 以上が要求されます。

インストールに先立ち、fml8 の運用セキュリティに従うために、実行ユーザ fmladmin を用意します:
vipw でやる場合は、下記の1行を追加します:
fmladmin::2007:3000::0:0:FML Administrator:/home/staff/fmladmin:/bin/csh
また、/etc/group の該当行を下記のように変更しておきます:
mailuser:*:3000:opendkim,milter-manager,postfix,fmladmin
続いて、下記手順でインストールを、必ず root ユーザで実行します:
# passwd fmladmin
# mkdir /home/staff/fmladmin
# chown fmladmin:mailuser /home/staff/fmladmin
# cp fml-7.99.1.tar.gz /usr/local/src
# cd /usr/local/src
# tar xvzf fml-7.99.1.tar.gz
# cd fml-7.99.1
# ./configure --with-fml-owner=fmladmin --with-fml-group=mailuser
# make install

fml8 の新規セットアップ

# vi /usr/local/etc/fml/site_default_config.cf
として、最低限下記2行を変更または追加します:
use_article_mime_component_filter = no
use_article_filter = no
これを設定しないと、ISO-2022-JP 文字コード以外で記述された電子メールを拒絶してしまいます。
昨今では、UTF-8 や UTF-7 でメッセージ交換するのが普通になっているため、今となっては時代遅れになってしまった余計な機能になっています。

○ 収容バーチャルドメインにおけるMLの運用準備
% su
# makefml newdomain example.com /var/mail/example.com/~ml (バーチャルドメイン運用初回のみ)

〔参考 ※バーチャルドメイン削除の場合〕
# makefml rmdomain exmple.com               (バーチャルドメイン上から MLドライバ削除)
○ メーリングリストの新規作成
# su fmladmin
% makefml newml test-ml@example.com           (ML 新規作成)

〔参考 ※メーリングリスト削除の場合〕
% makefml rmml test-ml@example.com                       (ML の削除)
% exit
《※ postfix の設定を変更》
# vi /usr/local/etc/postfix/main.cf

《下記の例のように、alias を追加》
alias_maps = hash:/etc/mail/aliases
             hash:/var/mail/example.com/~ml/etc/mail/aliases

virtual_alias_maps = hash:/usr/local/etc/postfix/virtualtbl/forwardlist
                     hash:/var/mail/example.com/~ml/etc/postfix/virtual
# vi /etc/mail/aliases

《下記を追加》
fmladmin:       root

《追加後、下記を実行》
# newaliases
上記、makefml newml の実行で、雛形ファイルが自動生成されますので、それを流用します。
ただし、そのままでは使えないため、一部変更します:
# cd /var/mail/example.com/~ml/etc/postfix
# vi virtual

----- 以下、変更 -----
# example.com is one of $mydestination
# CAUTION: DO NOT REMOVE THE FOLLOWING LINE.
# example.com     example.com         ← コメントアウトまたは削除

### <VIRTUAL test-ml@example.com ML> ###

# 以下の5行に、@realmx.example.jp (postfix main.cf で指定の myhostname で示す実ホスト名)を追加
test-ml@example.com               test-ml=example.com@realmx.example.jp
test-ml-ctl@example.com           test-ml-ctl=example.com@realmx.example.jp
test-ml-request@example.com       test-ml-request=example.com@realmx.example.jp
test-ml-admin@example.com         test-ml-admin=example.com@realmx.example.jp
test-ml-error@example.com         test-ml-error=example.com@realmx.example.jp

### </VIRTUAL test-ml@example.com ML> ###
----- 変更はここまで -----

※ MLごとの個別変更で、題名に通し番号を追加する(デフォルトでは通し番号はつかない)

# cd /var/mail/example.com/~ml/text-ml
# vi config.cf

----- 以下を、変更または追加(位置的には =cut 行の前) -----
article_header_rewrite_rules += rewrite_article_subject_tag
article_subject_tag = [$ml_name:%05d]
----- 変更はここまで -----
※ この設定で[test-ml:12345] みたいなのがメールタイトルの先頭に自動追加されるようになります。


# postmap hash:virtual
# postfix reload
○ 投稿メンバ兼配送メンバの追加
makefml add test-ml@example.com test@example.net
○ 投稿メンバ兼配送メンバの削除
makefml bye test-ml@example.com test@example.net

2022/12/30(金)メールサーバの中規模改修と基礎知識(8)~ メールサーバ稼動

ここでは、このシリーズの最初で示した
20221223_mailserver_2022enhance.jpg

の構成を稼働させる記事です。ほぼ、FreeBSD固有の手順です。ただ、参考になる部分はあるかもしれません。
(Linux系は、この辺りの仕組みは全く別物なので、読み替えることが出来るスキルが必要になる)
早速、順番に示していきます。作業は root ユーザで全てを実施します。

Postfix と dovecot の起動

先ず、下記のスクリプトファイルを、/usr/local/etc/rc.d ディレクトリ配下に postfix_dovecot というファイル名で作成します。
実行権を与えるのを忘れないようにしてください:
#!/bin/sh
#
# PROVIDE: postfix-dovecot
# REQUIRE: DAEMON NETWORKING openldap
# KEYWORD: shutdown

. /etc/rc.subr

name="postfix_dovecot"
rcvar="postfix_dovecot_enable"

start_cmd="mailserver_start"
stop_cmd="mailserver_stop"
restart_cmd="mailserver_reload"

# start postfix,dovecot, and E-Mail account control server
smtpserv=/usr/local/sbin/postfix
dovecot=/usr/local/sbin/dovecot

mailserver_start()
{
echo ' '
if [ -x $dovecot ]; then
/usr/local/sbin/dovecot -c /usr/local/etc/dovecot/dovecot.conf
echo ' dovecot 2.3.19.1 '
fi

if [ -x $smtpserv ]; then
/usr/local/sbin/postfix start > /dev/null 2>&1
echo ' Postfix 3.7.3 '
fi
}

mailserver_reload()
{
/usr/local/sbin/postfix reload
/usr/local/sbin/dovecot reload
echo ' reloaded postfix and dovecot config '
}

mailserver_stop()
{
/usr/local/sbin/postfix stop
/usr/local/sbin/dovecot stop
echo ' stoped Mail server '
}

load_rc_config $name
run_rc_command "$1" 
次に /etc/rc.conf に下記の2行を追加します
# vi /etc/rc.conf
sendmail_enable="NONE"
postfix_dovecot_enable="YES"
その後で、
# cd /usr/local/etc/rc.d
# ./postfix_dovecot start
として、エラーが表示されないことを確認したら、
で、下記のタスクが動作しているのが確認出来れば、成功です:
# ps -aux | grep postfix
root           32817  ... 13692  -  Ss   03:13         0:00.00 /usr/local/libexec/postfix/master -w
postfix        32818  ... 13704  -  S    03:13         0:00.01 pickup -l -t fifo -u
postfix        32819  ... 13796  -  S    03:13         0:00.01 qmgr -l -t fifo -u
postfix        32820  ... 13840  -  S    03:13         0:00.01 tlsmgr -l -t unix -u
postfix        32821  ... 13648  -  S    03:13         0:00.01 postlogd -l -n postlog -t unix-dgram -u
※ 記事表示スペースの関係上、実際の表示項目を一部省略しています。
# ps -aux | grep dovecot
root        64526 ...   5684  -  Is   02:11         0:00.27 /usr/local/sbin/dovecot -c /usr/local/etc/dovecot/dovecot.conf
dovecot     64528 ...   5368  -  I    02:11         0:00.07 dovecot/anvil
root        64529 ...   5444  -  I    02:11         0:00.04 dovecot/log
root        64530 ...   7808  -  I    02:11         0:00.45 dovecot/config
dovecot     64995 ...   5680  -  I    02:11         0:00.08 dovecot/stats
※ 記事表示スペースの関係上、実際の表示項目を一部省略しています。

ClamAV の起動

先述の記事で、既に起動スクリプト作成までは出来ているはずなので、
/etc/rc.conf に下記の5行を追加します:
# vi /etc/rc.conf
clamav_clamd_enable="YES"
clamav_freshclam_enable="YES"
clamav_milter_enable="YES"
clamav_milter_socket_mode="660"
clamav_milter_socket_group="mailuser"
その後で、先ず、
# cd /usr/local/etc/rc.d
# ./clamav_clamd start
として、エラーが表示されないことを確認したら、
# ps -aux | grep clamav
として、下記のように /usr/local/sbin/clamd が存在することを確認します。
clamav         56967   0.0  7.7 1371840 1291000  -  Is   03:03          2:42.64 /usr/local/sbin/clamd
続いて、同じように、
# ./clamav_freshclam start
# ps -aux | grep clamav
として、下記のように /usr/local/bin/freshclam が存在することを確認、
clamav         57001   0.0  0.2   48820   30412  -  Is   03:20          0:08.24 /usr/local/bin/freshclam --daemon -p /var/run/clamav/freshclam.pid
最後に、
# ./clamav-milter start
# ps -aux | grep clamav
として、下記のように /usr/local/sbin/clamav-milter が存在することを確認します:
clamav         57009   0.0  0.2   92520   29912  -  Ss   03:22          0:03.82 /usr/local/sbin/clamav-milter -c /usr/local/etc/clamav-milter.conf
何らかの問題が発生した場合、/var/log/clamd.log, /var/log/clamav-milter.log,/var/log/freshclam.log,/var/log/maillog などに解決のヒントが記述されているので、解決を試みることになります。

OpenDKIM の起動

先述の記事で、既に起動スクリプト作成までは出来ているはずなので、
/etc/rc.conf に下記の3行を追加します:
# vi /etc/rc.conf
milteropendkim_enable="YES"
milteropendkim_cfgfile="/usr/local/etc/opendkim/opendkim.conf"
milteropendkim_socket="local:/var/run/milteropendkim/dkim-milter"
その後で、先ず、
# cd /usr/local/etc/rc.d
# ./milter-opendkim start
として、エラーが表示されないことを確認したら、
# ps -aux | grep opendkim
として、下記のように /usr/local/sbin/opendkim が存在することを確認します:
opendkim       33260   ... 12776  -  Ss   03:31          0:01.16 /usr/local/sbin/opendkim -l -p local:/var/run/milteropendkim/dkim-milter -u opendkim:mailuser -P /var/run/milteropendkim
※ 記事表示スペースの関係上、実際の表示項目を一部省略しています。

milter-manager の起動

先述の記事で、既に起動スクリプト作成までは出来ているはずなので、
/etc/rc.conf に下記の1行を追加します:
# vi /etc/rc.conf
milter_manager_enable="YES"
これで、起動準備完了なのですが、初めての場合・milter アプリケーションを追加した場合、「miiter-manager が milter アプリケーションを自動検出できるかどうか」を必ず事前確認することを強くお勧めします。
自動検出できるか否かのチェックは、
# /usr/local/sbin/milter-manager -u milter-manager --show-config
で実行でき、この時に表示される内容で、
define_milter("clamav-milter") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:44
  milter.connection_spec = "unix:/var/run/clamav/clmilter.sock"
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
  milter.enabled = true
  # default
  milter.fallback_status = "accept"
		・
		・
       《中略》
		・
		・
define_milter("milter-opendkim") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:44
  milter.connection_spec = "local:/var/run/milteropendkim/dkim-milter"
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
  milter.enabled = true
  # default
  milter.fallback_status = "accept"
といった内容が含まれていることと、
特に milter.enabled = true になっている点、
milter.connection_spec が示す ソケットインタフェースのファイルパスが正しく指示していることを確認します。
上記例のようになっていれば問題ありません。

その後で、先ず、
# cd /usr/local/etc/rc.d
# ./milter-manager start
として、エラーが表示されないことを確認したら、
# ps -aux | grep milter-manager
として、下記のように /usr/local/sbin/clamd が存在することを確認します。
milter-manager 31520   ...  28472  -  S    03:57          0:41.49 /usr/local/sbin/milter-manager --pid-file /var/run/milter-manager/milter-manager.pid --user-name milter-manager --group
root           31518   ...  28072  0- S    03:57          0:05.64 /usr/local/sbin/milter-manager --pid-file /var/run/milter-manager/milter-manager.pid --user-name milter-manager --group
※ 記事表示スペースの関係上、実際の表示項目を一部省略しています。

○ FreeBSD 版 milter-manager における自動検出の仕組み
単純にソースコードからインストールしただけだと、「自動検出」が可能な状態にはなりません。
ですが、milter-manager 日本語版 の説明を隅々まで眺めると、『milter アプリケーションを Ports/Packages でインストールしたことを前提としている』と書かれています。

ということは、/usr/local/etc/rc.d ディレクトリ配下に、決まった形式の起動・終了スクリプトを用意すれば、自動検出が可能になるのでは? 
ということで、試しに設置することにします。
スクリプトの雛形は 各 Ports の files ディレクトリ配下にあり、これを実運用環境に合わせて変更します。
この時、 /etc/rc.conf の変更も必要です。/etc/rc.conf の中に最低限 clamav_clamd_enable="YES" のような一文が無いと、手動で起動・停止スクリプトを実行する際にエラーを吐いて起動しないのです。

ClamAV はこれで自動検出できるようになったのですが、OpenDKIM は自動検出しません。
散々悩んだ挙句、/usr/local/etc/rc.d ディレクトリ配下に設置する、スクリプトファイル名の問題でした。

当初、opendkim_milter というファイル名で作成したのですが、これを opendkim-milier に変えたら自動検出できるようになりました。このあたりの仕組みがブラックボックスなので、悩むところです。
おそらく、各起動スクリプトの3行目 にある PROVIDE 行と実際のファイル名を一致させないと駄目なのかもしれません。
トラブル解決の際の参考になれば幸いです。

SpamAssassion の起動

先述の記事で、既に起動スクリプト作成までは出来ているはずなので、
/etc/rc.conf に下記の1行を追加します:
# vi /etc/rc.conf
spamd_enable="YES"
その後で、先ず、
# cd /usr/local/etc/rc.d
# ./sa-spamd start
として、エラーが表示されないことを確認したら、
# ps -aux | grep spamd
として、下記のように spamd が存在することを確認します。
root           49139   0.0  0.7  162040  124744  -  Ss   04:53         1:13.97 spamd (perl)
spamd          49141   0.0  0.8  162040  127988  -  I    04:53         0:03.52 spamd child (perl)
spamd          49142   0.0  0.7  162040  124816  -  I    04:53         0:00.51 spamd child (perl)

送受信のテスト

テストの前に、テストで使用する受信者のメールボックスディレクトリ直下に .dovecot.sieve のファイル名で、
下記のような sieve スクリプトを用意します。
require ["fileinto","vnd.dovecot.filter"] ;

 filter "spamass.sh" "user@example.com" ;
 # rule:[SpamAssassin]
 if header :is "X-Spam-Status" "Yes" {
    fileinto "spamdrop" ;
    stop ;
 }
 # rule:[Clamav]
 if header :is "X-Virus-Status" "Yes" {
    fileinto "spamdrop" ;
    stop ;
 }
更に、/usr/local/etc/dovecot/sieve-filter ディレクトリ配下に spamass,sh というファイル名で、下記スクリプトを用意します:
#!/bin/sh

randomid=`/usr/bin/od -An -tu4 -N4 /dev/random | /usr/bin/tr -d ' '`
tmpfname1="/var/tmp/_${randomid}_1"
tmpfname2="/var/tmp/_${randomid}_2"

while read line
do
  /bin/cat >> $tmpfname1
done

usernam=`echo -n $1 | /usr/bin/cut -d @ -f 1`
userdom=`echo -n $1 | /usr/bin/cut -d @ -f 2`

if [ -d /var/mail/${userdom}/${usernam}/spamassassin/spamd ]; then
  /usr/local/bin/spamc -u $1 < $tmpfname1 > $tmpfname2

  /bin/cat $tmpfname2
  /bin/rm $tmpfname1
  /bin/rm $tmpfname2
else
  /bin/cat $tmpfname1
  /bin/rm $tmpfname1
fi

exit 0
このスクリプトは、実行権限を付与するのを忘れないでください。
これで、電子メール配信毎に、配信最終段階で、自動的に spamass.sh を実行し、その中で spamassassin による電子メール spam チェックを行い、spamassassin がメールヘッダ部に追加した spam チェック結果により spamdrop ディレクトリ配下にメッセージを隔離する処理を自動振り分けの形で行う挙動になります。

この時、隔離先に指定している spamdrop は、テストで使用する受信者のメールボックスディレクトリ直下に .spamdrop のディレクトリ名で用意しなければなりません。この点がどこにも明記されておらず、ディレクトリを用意しているのに「spamdrop が無い」と、sieve スクリプトでエラーを吐かれるという現象に何時間も悩みました。。

spamassassin の挙動設定や、フィルタのデータは、各受信者のメールボックスディレクトリ直下の spamassassin/spamd ディレクトリ配下の user_prefs などを参照します。

この状態でテスト受信者宛てに電子メールを送信してみて、受信電子メールのメールヘッダに以下のようなヘッダが含まれていれば、成功です:
X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-14) on mx0.example.com
X-Spam-Level: 
X-Spam-Status: No, score=-2.5 required=4.0 tests=BAYES_00,KHOP_HELO_FCRDNS,
	NICE_REPLY_A,SPF_HELO_NONE,SUBJ_ALL_CAPS autolearn=ham
	autolearn_force=no version=4.0.0
また、テスト受信者から、返信か新規に電子メールを送信してみて、下記のようなDKIMヘッダが付いていれば、成功です:
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=example.com; s=mx0;
	t=1672471637; bh=3mQXSVMo2kr2sHu262xQGwzCfRBaMvLhHS2T4KSLTSw=;
	h=Date:Subject:To:References:From:In-Reply-To;
	b=AjxunnUSbCBDAP3yGcB9lCa1Gjys9PYqZ0cup0a0D2QibriW1KpJPxeUKc84e+/aQ
	 Q3FkBGJ42pe0tReKbA/G40SI3+TI07q5Ktdn2M+Zz3WK1vyeQfnyZ8gZEvj2tLiin2
	 9Jc6H5qKeEvIx5AQrvIEAyukmXCuXVjGIbujHF2WZbfOmsqeI5eBvDPYK5QAcdrkM+
	 Ewgc8asLgEIxhOzpMruDEyvJJ8UYJnEPf/hqT5ULWcivOkzvgKL95EEzI42xo3QBd6
	 DgpQ8kjx9KpsNRJv7IYDrmD9aFoP5mvjzTx51f9+K1uKENXimM7d7MB1kaktiNVKtO
	 XB7v6ywP9dQCw==

2022/12/29(木)メールサーバの中規模改修と基礎知識(7)~ postfix

Postfix-logo.png

postfix は、sendmail の代替を目指す、電子メール送受信プログラムです。
記事公開日時点では、3.7.3 が最新バージョンです。

Postfix のインストール

機能説明等は不要と思いますので、早速インストール手順を示してみます。
インストール作業は、全て root ユーザで行います。
Postfix をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
 ・perl5-5.36.0 	 (Ports から カテゴリ:lang)
 ・ca_root_nss-3.86	 (Ports から カテゴリ:security)
 ・autoconf-2.71	 (Ports から カテゴリ:devel)
 ・libltdl-2.4.7	 (Ports から カテゴリ:devel)
 ・libtool-2.4.7	 (Ports から カテゴリ:devel)
 ・pcre2-10.40		 (Ports から カテゴリ:devel)
 ・OpenLDAP 2.6.3 以上	 (これはソースコードからの構築を強く推奨)
 ・dovecot 2.3.19.1 以上 (本記事シリーズでソースコードから構築)
 ・gmake-4.3_2		 (Ports から カテゴリ:devel)
 ・icu-72.1,1		 (Ports から カテゴリ:devel)
上記をインストール出来ていたら、先ず、運用時における Postfix 自体が意図しているセキュリティポリシーに合わせるため、ここで専用のユーザを予めvipw や useradd コマンドで作っておきます。
vipw の場合は、編集画面で、下記の行を追加しておきます:
postfix::2000:2000::0:0:postfix:nonexistent:/usr/sbin/nologin
vipw でユーザ追加した場合は、/etc/group ファイルに下記の行を追加しておきます:
postdrop:*:65001:
更に、/etc/group ファイル内の該当行を下記のように変更しておきます:
mailuser:*:3000:opendkim,milter-manager,postfix
ユーザID、グループIDは、上記例にこだわる必要はありませんが、当然のことながら、ユーザID・グループIDが他と重複しないように注意です。
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd postfix
今度は以下の手順に沿ってインストール作業を行います。Postfix のインストールは configure を使わない独特で且つ汎用的に対応できる形態になっています。
# cp postfix-3.7.3.tar.gz /usr/local/src	 
# cd /usr/local/src	 
# tar xvzf postfix-3.7.3.tar.gz	 
# cd postfix-3.7.3	 
 	 
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include' 	(configure が上手くライブラリを探せないため)
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib'			(configure が上手くライブラリを探せないため)
# unsetenv LD_LIBRARY_PATH' 	 (3.7.x からは、LD_LIBRARY_PATH が設定されているとコンパイルを受け付けない)

※画面上では、説明のために改行していますが、 make の部分は、改行せずに、半角スペースで区切って、一気に入力。
# make -f Makefile.init makefiles 			 (OS等を自動識別してコンパイル)
 'CCARGS=-DHAS_LDAP -DUSE_TLS -DUSE_SASL_AUTH 		 (LDAP,SSL,SASL認証をサポートする指示)
     -DDEF_SASL_SERVER=\"dovecot\" 		 (dovecot SASL を使用する指示 ※2.3.x 以降で必須)
     -DDEF_COMMAND_DIR=\"/usr/local/sbin\" 	 (postfix 各種実行プログラムの格納ディレクトリ)
     -DDEF_CONFIG_DIR=\"/usr/local/etc/postfix\" 	 (postfix 各種設定ファイル格納ディレクトリ)
     -DDEF_DAEMON_DIR=\"/usr/local/libexec/postfix\" (postfix 各種デーモンプログラムの格納ディレクトリ)
     -I/usr/local/include	 
     -I/usr/include/openssl' 			 (openssl のヘッダファイル)
 'AUXLIBS=-L/usr/lib -lssl -lcrypto 			 (SSL ライブラリのある場所)
      -L/usr/local/lib -lldap -llber -licudata -licui18n -licuio -licutest -licutu -licuuc'
  							 (LDAP・icu ライブラリのある場所)
postfix の場合、いわゆる「メール送信」だけを行う Null Client な構成だと、上記 make コマンドに与えるパラメータが異なってくるのですが、今回は、普通のメールサーバの構築を行うため、提示のオプションになります。

続いて、
# make
で、エラーが出ないで終了したことを確認したら、
# make install
で、インストールを実施します。CUIインタフェースですが、対話形式のインストーラとなっています。
[ ] 内で示す文字列は、デフォルト値または、識別している値です。
変更しない場合は、そのままリターンキー押下、変更する場合に設定値を入力してリターンキー押下で次の項目に進みます。
※下記のように設問に対応する。(設問順序は、必ずしもこの順番にはなりません)
Please specify the prefix for installed file names. This is useful
if you are building ready-to-install packages for distribution to
other machines.
install_root: [/]

Please specify a directory for scratch files while installing
Postfix. You must have write permission in this directory.
tempdir: [/usr/local/src/postfix-3.7.3] /tmp

Please specify the destination directory for installed Postfix
configuration files.
config_directory: [/usr/local/etc/postfix]

Please specify the destination directory for installed Postfix
administrative commands. This directory should be in the command
search path of adminstrative users.
command_directory: [/usr/local/sbin]

Please specify the destination directory for installed Postfix
daemon programs. This directory should not be in the command search
path of any users.
daemon_directory: [/usr/local/libexec/postfix]

Please specify the final destination directory for Postfix-writable
data files such as caches or random numbers. This directory should
not be shared with non-Postfix software.
data_directory: [/var/lib/postfix] /var/tmp/postfix

Please specify the destination directory for the Postfix HTML files.
Specify "no" if you do not want to install these files.
html_directory: [no]

Please specify the owner of the Postfix queue. Specify an account
with numerical user ID and group ID values that are not used by any
other accounts on the system.
mail_owner: [postfix]

Please specify the full destination pathname for the installed
Postfix mailq command. This is the Sendmail-compatible mail queue
listing command.
mailq_path: [/usr/bin/mailq] /usr/local/libexec/postfix/mailq

Please specify the destination directory for the Postfix on-line
manual pages. You can no longer specify "no" here.
manpage_directory: [/usr/local/man]

Please specify the full destination pathname for the installed
Postfix newaliases command. This is the Sendmail-compatible command
to build alias databases for the Postfix local delivery agent.
newaliases_path: [/usr/bin/newaliases] /usr/local/libexec/postfix/newaliases

Please specify the destination directory for Postfix queues.
queue_directory: [/var/spool/postfix]

Please specify the destination directory for the Postfix README
files. Specify "no" if you do not want to install these files.
readme_directory: [no] /usr/local/etc/postfix/docs

Please specify the full destination pathname for the installed
Postfix sendmail command. This is the Sendmail-compatible mail
posting interface.
sendmail_path: [/usr/sbin/sendmail] /usr/local/libexec/postfix/sendmail

Please specify the group for mail submission and for queue management
commands. Specify a group name with a numerical group ID that is
not shared with other accounts, not even with the Postfix mail_owner
account. You can no longer specify "no" here.
setgid_group: [postdrop]

Please specify the final destination directory for Postfix
shared-library files.
shlib_directory: [no] 

Please specify the final destination directory for non-executable
files that are shared among multiple Postfix instances, such as
postfix-files, dynamicmaps.cf, as well as the multi-instance template
files main.cf.proto and master.cf.proto.
meta_directory: [/usr/local//etc/postfix] 
もし間違えたら、修正手段は容易されていないので、Ctrl+C で中断し再度
# make install
を行います。

次に、FreeBSD に特化した設定内容として、 /etc/mail/mailer.conf の設定変更を行います。
このファイルの内容を下記のように変更します:
# $FreeBSD: src/etc/mail/mailer.conf,v 1.3 2002/04/05 04:25:12 gshapiro Exp $
#
# Execute the "real" sendmail program, named /usr/libexec/sendmail/sendmail
#
sendmail    /usr/local/libexec/postfix/sendmail
send-mail    /usr/local/libexec/postfix/sendmail
mailq      /usr/local/libexec/postfix/sendmail
newaliases   /usr/local/libexec/postfix/sendmail
hoststat    /usr/local/libexec/postfix/sendmail
# purgestat   /usr/local/libexec/senamail/sendmail

Postfix の設定

○ サーバ証明書の取得とセットアップ
Let's Encrypt の certbot を使用する前提でここに手順を記述しています。
※ 操作するサーバ上で Webサーバが稼動している場合は、必ず一旦停止させること。
 
# certbot certonly --standalone --agree-tos -m user@example.com -d smtp.example.com
# certbot certonly --standalone --agree-tos -m user@example.com -d smtp.example.net
 
# cd /usr/local/etc/postfix
# vi tls_sni.map
tls_sni.map は、上記例をもとにすると、下記内容のように記述します:
※複数行の表示になっているが、
1ドメイン=1行で [対象ドメイン名] [秘密鍵ファイル名] [CA・公開鍵結合ファイル名] の順に、
半角スペースで区切って入力してください。
smtp.example.com /usr/local/etc/letsencrypt/live/smtp.example.com/privkey.pem /usr/local/etc/letsencrypt/live/smtp.example.com/fullchain.pem
smtp.example.net /usr/local/etc/letsencrypt/live/smtp.example.net/privkey.pem /usr/local/etc/letsencrypt/live/smtp.example.net/fullchain.pem 
tls_sni.map の編集を行ったら、
# postmap -F hash:/usr/local/etc/postfix/tls_sni.map
で、 Postfix で処理できるデータベース形式に変換しておきます(tls_sni.map.db が新たに生成されます)。

○ main.cf の設定変更
次に、/usr/local/etc/postfix/main.cf を下記の要領で編集します:
# cd /usr/local/etc/postfix
# cp main.cf main.cf.org
# vi main.cf
※ 最低限の変更部分のみ抜粋:
compatibility_level = 3.6                #挙動の設定
inet_protocols  = all                  #IPv4,IPv6 両方使用
inet_interfaces = all
unknown_address_reject_code = 550

tls_server_sni_maps = hash:/usr/local/etc/postfix/tls_sni.map     #TLS対象ホスト名
smtpd_tls_CApath = /etc/ssl/carts                   #接続相手先のCAルート証明書情報
smtpd_tls_chain_files =
 /usr/local/etc/letsencrypt/live/pmx1.admin-plus.net/privkey.pem,
 /usr/local/etc/letsencrypt/live/pmx1.admin-plus.net/fullchain.pem  #デフォルト適用の証明書情報

smtpd_tls_received_header = yes
smtpd_tls_session_cache_database = hash:/var/tmp/postfix/smtpd_scache #キャッシュデータベース
smtpd_tls_loglevel = 1                         #ログレベルの設定(必要に応じ 1 ~ 3 )
smtpd_tls_security_level = may                     #TLSサポートレベルの設定
smtpd_sasl_auth_enable = yes                      #SASL による SMTP-AUTHサポート
smtpd_sasl_type = dovecot                       #SASL認証機構の指定
smtpd_sasl_path = private/auth                     #SASL認証ソケットの指定

smtpd_sender_restrictions = reject_unknown_sender_domain
smtpd_relay_restrictions =
 permit_mynetworks,
 permit_sasl_authenticated,
 defer_unauth_destination
smtpd_recipient_restrictions =
 permit_mynetworks,
 permit_sasl_authenticated,
 reject_unauth_destination

smtp_tls_CApath = /etc/ssl/carts                    #接続クライアントのCAルート証明書情報
smtp_tls_session_cache_database = hash:/var/tmp/postfix/smtp_scache  #キャッシュデータベース
smtp_tls_security_level = may                     #TLSサポートレベルの設定
smtp_tls_loglevel = 1                         #ログレベルの設定(必要に応じ 1 ~ 3 )

milter_protocol = 6                          #milterプロトコルパージョンを6にする
milter_default_action = accept                     #milter接続失敗時のアクション
milter_mail_macros = {auth_author} {auth_type} {auth_authen}      #milter呼び出し時のパラメータ設定
smtpd_milters = unix:/var/run/milter-manager/milter-manager.sock    #SMTPD 時の milter 呼び出しアクション
non_smtpd_milters = unix:/var/run/milter-manager/milter-manager.sock  #SMTPD 以外の milter 呼び出しアクション

virtual_mailbox_maps = ldap:/usr/local/etc/postfix/ldap-mailbox.cf   #バーチャルユーザ情報
virtual_transport = dovecot                      #バーチャルユーザ配送 LDA指定
dovecot_destination_recipient_limit = 1                #同時配送数(複数メールの同時処理無し設定)
○ master.cf の設定変更
次に、/usr/local/etc/postfix/master.cf を下記の要領で編集します:
# cp master.cf master.cf.org
# vi master.cf
※ 最低限の変更部分のみ抜粋:
smtp    inet n  -  n  -   -  smtpd
smtps    inet n  -  n  -   -  smtpd
   -o smtpd_tls_wrappermode=yes
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_sasl_type=dovecot
   -o smtpd_sasl_path=private/auth
   -o smtpd_sender_restrictions=reject_unknown_sender_domain
   -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
   -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
   -o smtpd_tls_security_level=may

submission inet n  -  n  -   -  smtpd
   -o smtpd_tls_wrappermode=yes
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_sasl_type=dovecot
   -o smtpd_sasl_path=private/auth
   -o smtpd_sender_restrictions=reject_unknown_sender_domain
   -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
   -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
   -o smtpd_tls_security_level=may

tlsmgr   unix -  -  n  1000? 1  tlsmgr
dovecot   unix -  n  n  -   -  pipe
 flags=DRhu user=vmail argv=/usr/local/libexec/dovecot/deliver -f {$sender} -d ${recipient}
master.cf のこの設定で、smtp(port 25)、smtps(port 465)、submission(port 587) の待ち受けを可能にします。

最後に、aliases の設定をします。
# cd /etc/mail
# vi /etc/mail/aliases
aliases の該当行を下記のように変更しておきましょう:
root:	me@example.com
     ↓
root:	user@example.jp
要するに、実在し、受信できる電子メーるアドレスにします。
無断で他人のメールアドレスを記述したり、存在しないメールアドレスを記述してはいけません。
これを変更したら
# newaliases
として、データベースを更新しておきます。aliases.db が直近の時刻に変更されていることを ls -al コマンドなどで確認しておいてください。

Postfix の設定は以上です。

2022/12/28(水)メールサーバの中規模改修と基礎知識(6)~ milter-manager

milter-mamager は、複数のmilter インターフェースアプリケーションを管理し、postfix や sendmail 上の設定を簡略化する目的で使用される電子メール送受信アプリケーションです。
日本語の milter-manager サポートページは、2.1.5 (2019/09/10 リリース)で更新が止まっており(但し、大いに参考にはなる)、FreeBSD の Ports は 2.1.6 (2022/01/13 リリース)で更新が止まっているのですが、milter-manager の最新バージョンはどうやら 2.2.5(2022/12/12 リリース)のようです。

milter-manager の概要

今回は、下手くそ(且つ汚ない)な手書きで描いた、下記のような挙動をさせるために使います。
20221228_miltermanager.png

複数の milter アプリケーションを使用する場合、postfix 側で単純に milter の設定を行えば事足ります。
ですが、実際はたった2つでも設定がちょっと煩雑になる。

加えて、将来的に電子メールサービスの付加機能は増えていく傾向にあると思われるため、そのような状況に先回り対処する目的で開発されたのが、この milter-manager です。

milter アプリケーションは、典型的なデーモン形式の造りになっているのが通常で、ソケット通信で電子メールメッセージの授受を行いますが、TCP/IPによるソケット通信では、サーバ負荷が重くなりがちなのです。
なので、通常は unix ソケット通信による手法で代替し、今回も milter-manager とのやりとりは、全て unix ソケット通信にて動作させています。

milter-manager は、postfix からの一度の呼び出しアクションで、バックグラウンドで複数の milter アプリケーション処理を実行する役目を担います。

milter-manager のインストール準備(1)

ここからは、基本的に root アカウントでの作業となります。
milter-manager をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
 ・perl5-5.36.0 		 (Ports から カテゴリ:lang)
 ・ruby-3.0.5,1 		 (Ports から カテゴリ:lang)
 ・libtool-2.4.7		 (Ports から カテゴリ:devel)
 ・gettext-runtime-0.21	 (Ports から カテゴリ:devel)
 ・gettext-tools-0.21_1	 (Ports から カテゴリ:devel)
 ・libev-4.33,1		 (Ports から カテゴリ:devel)
 ・gmake-4.3_2			 (Ports から カテゴリ:devel)
 ・libxml2-2.10.3_1		 (Ports から カテゴリ:textproc)
 ・glib-2.74.4_2		 (Ports から カテゴリ:devel)
 ・gobject-introspection-1.74.0,1(Ports から カテゴリ:devel)
 ・intltool-0.51.0_1		 (Ports から カテゴリ:textproc)
 ・rubygem-glib2-4.0.3		 (Ports から カテゴリ:devel)
 ・rubygem-rexml-3.2.5		 (Ports から カテゴリ:textproc)
milter-manager も結構依存するものが多いのですが、
これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
ruby で動作させている部分が案外多く、Perl と、何故か gnome ライブラリの一部(glib,gobject) も依存します。
特にに、glib の古いライブラリがサーバ上に残っていると上手く動作しない不具合が確認されています。

更に、インストールに先立ち、milter-manager のセキュリティポリシーに従うため、ユーザ milter-manager を、vipw や useradd コマンドで追加します:

vipw コマンドの場合は、下記の1行を編集画面で追加します。
milter-manager::2005:3000::0:0:milter manager execute user:/home/staff/milter-manager:/usr/sbin/nologin
※ ユーザホームディレクトリを内部処理でワークエリア的に使うため、ホームディレクトリを必ず割り当てる

vipw でユーザ追加した場合、/etc/group に下記の1行を例示のように変更しておきます:
mailuser:*:3000:opendkim,milter-manager
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd milter-manager

milter-manager のインストール準備(2)

先ず、ソースコードの展開を行います。
# cp milter-manager-2.2.5.tar.gz /usr/local/src	 
# cd /usr/local/src	 
# tar xvzf milter-manager-2.2.5.tar.gz	 
# cd milter-manager-2.2.5
実は、実際にソースコードのコンパイルを実施したところ、FreeBSDにおいては、素のソースコードではエラーが発生し、上手く出来ません。
具体的には、configure スクリプト、binding/ruby/test/run-test.sh、Makefile.in の3本の変更が必要なようです。

これらは、patch コマンドを使用するか、手入力修正して、作業実施前にソースコードの修正を行う必要があります。
ここでは、量が少ないのと、用意が面倒なので手入力で修正しまいます。

FreeBSD12/FreeBSD13 においては、以下のように修正することで、milter-manager 2.2.5 のソースコードでコンパイルできるようになります。(確認済)
binding/ruby/test/run-test.sh 1行目
 #!/bin/bash
   ↓
 #!/bin/sh

Makefile.in 516行目
        milter-manager.pc libmilter.pc $(am__append_1)
                          ↓
        milter-manager.pc $(am__append_1)

configure 15177 行目
    RUBY_GLIB2_CFLAGS="-I$ruby_glib2_gem_dir/ext/glib2"
             ↓
    RUBY_GLIB2_CFLAGS="-I$ruby_glib2_gem_dir/lib"

milter-manager のインストール

以下の手順に従ってインストールしていきます:
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include'	(configure が上手くライブラリを探せないため)
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib' 		(configure が上手くライブラリを探せないため)
# unsetenv LD_LIBRARY_PATH	 
# ./configure --localstatedir=/var
configure スクリプトの実行が完了すると、以下のようなサマリーが出力されます:
Configure Result:

  Package Platform        : freebsd
  Package Options         :

  Default Effective User  :
  Default Effective Group :
  Default Socket Group    :
  Default Connection Spec : unix:/var/run/milter-manager/milter-manager.sock
  Default PID File        :

  GLib                    : 2.74.4
  libev                   : yes
  Ruby                    : /usr/local/bin/ruby
    CFLAGS                : -I/usr/local/include/ruby-3.0/amd64-freebsd13 -I/usr/local/include/ruby-3.0/
    LIBS                  : -L/usr/local/lib -fstack-protector-strong -Wl,--compress-debug-sections=zlib -Wl,-E -lruby30 -lm -lpthread -L/usr/local/lib
  Ruby version            : ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c5) [amd64-freebsd13]
  Ruby/GLib2              : -I/usr/local/lib/ruby/gems/3.0/gems/glib2-4.0.3/lib
  Ruby milter PATH        :

  rd2                     : rd2-not-found
  GTK-Doc                 : no

  Cutter                  :
  LCOV                    :
  coverage                :
  coverage report by LCOV :

  Cutter Source Path      : $(top_srcdir)/vendor/cutter-source

  Launchpad PGP key       :
  GPG UID                 : 3F09D1EA68E5F18B4EC8FEEAFF2030C057B9884E
続いて、下記のコマンドを順に実行し、エラーが無ければ、本体のインストールは完了です。
# gmake
# gmake install
最後に下記手順で動作環境を準備して、インストール完了です:
# mkdir /home/staff/milter-manager
# chown milter-manager:mailuser /home/staff/milter-manager
# mkdir /var/run/milter-manager
# chown milter-manager:maliuser /var/run/milter-manager

起動・停止スクリプトの設置(FreeBSD に特化している項目)

このスクリプトは一度作っておくと、バージョンアップの際に再作成の必要はありません。

○ milter-manager の起動スクリプト
/usr/local/etc/rc.d/ ディレクトリ配下に、milter-manager のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh

# PROVIDE: milter-manager
# REQUIRE: LOGIN
# BEFORE: mail
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable milter-manager:
#
#   milter_manager_enable=yes
#

. /etc/rc.subr

name="milter_manager"
rcvar="milter_manager_enable"
extra_commands="reload"

load_rc_config $name
: ${milter_manager_enable="NO"}
: ${milter_manager_pid_file="/var/run/milter-manager/milter-manager.pid"}
if getent passwd milter-manager > /dev/null; then
    : ${milter_manager_user_name="milter-manager"}
else
    : ${milter_manager_user_name="mailnull"}
fi
if getent group milter-manager > /dev/null; then
    : ${milter_manager_group_name="milter-manager"}
else
    : ${milter_manager_group_name="mailuser"}
fi
: ${milter_manager_socket_group_name="mailuser"}
: ${milter_manager_connection_spec=""}
: ${milter_manager_debug="NO"}

command=/usr/local/sbin/milter-manager
pidfile="${milter_manager_pid_file}"
command_args="--pid-file ${milter_manager_pid_file}"
if test -n "${milter_manager_user_name}"; then
    command_args="${command_args} --user-name ${milter_manager_user_name}"
fi
if test -n "${milter_manager_group_name}"; then
    command_args="${command_args} --group-name ${milter_manager_group_name}"
fi
if test -n "${milter_manager_socket_group_name}"; then
    command_args="${command_args} --unix-socket-group ${milter_manager_socket_group_name}"
fi
if test -n "${milter_manager_connection_spec}"; then
    command_args="${command_args} --spec ${milter_manager_connection_spec}"
fi
if test "$milter_manager_debug" = "YES"; then
    command_args="${command_args} --verbose"
else
    command_args="${command_args} --daemon"
fi

run_rc_command "$1"
今回意図した環境では、FreeBSD においては、デフォルト設定(自動検出)で問題なく動作しますが、実際には試行錯誤があったため、別記事でそのあたりを示していきます。

2022/12/27(火)メールサーバの中規模改修と基礎知識(5)~ OpenDKIM

OpenDKIM は、SPFと並んで、電子メール発信元の正当性を保証する技術のひとつです。
公式公開バージョンは、記事公開日時点では、2.10.3 です。一部のLinux ディストリビューションに 2.11.x が提供されているようですが、これは、RC版(Release Candidate:リリース候補の意)の位置づけの状態です。

OpenDKIM の概要

OpenDKIM は、電子メールメッセージを基に検証鍵を電子メールヘッダに埋め込み、DNSに登録した公開鍵を使って検証することで、電子メール発信元の正当性を確認し、電子メール改ざんがされていないことも確認する一助になります。

具体的には、予め送信元DNS情報に下記2つの TXTレコードを公開する形で登録しておき、
20221227_1_opendkim_2022.png

20221227_2_opendkim_2022.png


電子メール送信の際は、下記のように「DKIM-Signature:」というヘッダ行にて、一定のルールで計算された暗号キーを追加する処理を行います。
20221227_3_opendkim_2022.png

受信の際は、このDNS情報を基にメールヘッダに添付されてきたヘッダ暗号キーの検証を行い、この暗号キーに問題が無ければ「メール改ざんが無かった」と見做す、という処理を行うのが OpenDKIM の役割です。

送信の際の「DKIM-Signature:」ヘッダ行追加・受信の際の「DKIM-Signature:」ヘッダ行検証処理は、postfix にて、milter インタフェースを介して行うことになります。

OpenDKIM のインストール準備(1)

この先は、root アカウントにて、一連の作業を行います。
OpenDKIM は、公開鍵の管理にPython と Perlを使用しているため、この2つは必須です。
OpenDKIM をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
 ・perl5-5.36.0 	(Ports から カテゴリ:lang)
 ・python39-3.9.16	(Ports から カテゴリ:lang)
 ・lua54-5.4.4 	(Ports から カテゴリ:lang)
 ・autoconf-2.71	(Ports から カテゴリ:devel)
 ・automake-1.16	(Ports から カテゴリ:devel)
 ・libltdl-2.4.7	(Ports から カテゴリ:devel)
 ・libtool-2.4.7	(Ports から カテゴリ:devel)
 ・gnutls-3.7.8_1	(Ports から カテゴリ:security)
 ・db5-5.3.28_9	(Ports から カテゴリ:databases)  ※ これは BerkeleyDB 5.3.28 です
OpenDKIM 2.10.3 は、OpenSSL 1.0 系までは対応しているものの、OpenSSL 1.1 系には対応していません。
昨今の FreeBSD にて採用されている OpenSSL は1.1 系のため、代替選択として、gnutls のインストールが必要になります。OpenSSL 1.1系対応なら、素直に OpenSSL を使うことで事が足りるのですが。。

更に、インストールに先立ち、OpenDKIM のセキュリティポリシーに従うため、ユーザ opendkim を、vipw や useradd コマンドで追加します:

vipw コマンドの場合は、下記の1行を編集画面で追加します。
opendkim::2006:3000::0:0:OpenDKIM execute user:/var/empty:/usr/sbin/nologin
vipw でユーザ追加した場合、/etc/group に下記の1行を例示のように変更しておきます:
mailuser:*:3000:opendkim
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd opendkim

OpenDKIM のインストール準備(2)

先ず、ソースコードの展開を行います。
# cp opendkim-2.10.3.tar.gz /usr/local/src
# cd /usr/local/src
# tar xvzf opendkim-2.10.3.tar.gz
# cd opendkim-2.10.3
実は、実際にソースコードのコンパイルを実施したところ、FreeBSDにおいては、素のソースコードでは、GnuTLS 使用環境での構築も上手く出来ません。
具体的には、configure スクリプト、libopendkim/tests/Makefile.in、libopendkim/dkim-canon.c、libopendkim/dkim.c、miltertest/miltertest.c、opendkim/tests/Makefile.in、opendkim/opendkim-crypto.c、 opendkim/opendkim-lua.c の8本の変更が必要なようです。

これらは、patch コマンドを使用するか、手入力修正して、作業実施前にソースコードの修正を行う必要があります。
FreeBSD patch コマンド向けの差分ファイルは、https://ctrl.basekernel.ne.jp/rebase/download.html からダウンロードできるようにしてありますので、『opendkim-2.3.10用 FreeBSD 向け Patch ファイル群』をダウンロードしてみて、使えるようであれば、使ってみてください。

おそらく、以下のように実行すると良いと思います(当方では、この手法で出来ることの確認をしていません)
# cp opendkim-2.10.3.patch.gz /usr/local/src/opendkim-2.10.3
# cd /usr/local/src/opendkim-2.10.3
# tar xvzf opendkim-2.10.3.patch.gz
# patch < patch-configure.ac
# patch < patch-libopendkim__tests__Makefile.in
      ・
      ・
# patch < patch-opendkim_opendkim-lua.c

OpenDKIM のインストール

以下の手順で実行していきます:
# cd /usr/local/src/opendkim-2.10.3
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include' 	  (configure が上手くライブラリを探せないため)
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib -L/usr/local/lib/db5'(configure が上手くライブラリを探せないため)
# unsetenv LD_LIBRARY_PATH	 

※以下、説明のために改行していますが、 ./configure の部分は、改行せずに、半角スペース区切りで、一気に入力。
# ./configure --localstatedir=/var 	(unix ソケットなどを格納するディレクトリ)
       --with-gnutls 		(openssl 1.1.x は未サポートのため、gnutls を使用する)
       --with-milter 		(milter インタフェースを構築する)
       --with-db 		(BerkeleyDBをサポートする)
       --with-domain 		(domain 設定をサポートする)
# make
# make install
ここまで、エラー無く出来たら、インストール不具合が1つあるので、下記の要領でファイル修正を行います。
# vi /usr/local/sbin/opendkim-genkey
1行目を下記の要領で変更
#!/usr/bin/perl
    ↓
#!/usr/local/bin/perl
次に設定作業を行います。
# mkdir /usr/local/etc/opendkim
# cd /usr/local/etc/opendkim
# mkdir example.com.keys 	(メールサーバ収容ドメイン毎にディレクトリを作成)
# cd example.com.keys
# /usr/local/sbin/opendkim-genkey --bits=2048 --domain=example.com --selector=sel
 	(--domain:収容ドメイン --selector:任意)
	※ --selector の文字列は公開鍵識別子になるので、適当にせずに何等かの規則を作るとよいでしょう。
	  pop1 とか mx1 とか、メールサーバの付番に対応すると良いと思います。
# cd ../
# chown -R opendkim:mailuser example.com.key
# vi KeyTable		※ 以下のように編集をする(複数ドメインになる場合は、行追加)
sel._example.com example.com:sel:/usr/local/etc/opendkim/example.com.keys/sel.private

# vi SigningTable	※ 以下のように編集をする(複数ドメインになる場合は、行追加)
*@example.com sel._domainkey.example.com

# chown opendkim:mailuser KeyTable
# chown opendkim:mailuser SigningTable
ここで、KeyTable ファイルへの記述方法ですが、
①._②:①:/usr/local/etc/opendkim/②.keys/①.private
(①= --selector への設定文字列 ②= --domain への設定文字列)

SigningTable ファイルへの記述方法は、
*@② ①._domainkey.②
(①= --selector への設定文字列 ②= --domain への設定文字列) のようにします。

次に、上記で /usr/local/sbin/opendkim-genkey を実行した時に生成された、sel.txt の内容と、取り扱いポリシーをDNSへ登録します。(--selector の指定により、拡張子 .txt は変わりませんが、ファイル名は変わります。)

生成された、sel.txt の内容を見ると、テキスト形式にて、
sel._domainkey  IN      TXT     ( "v=DKIM1; k=rsa; "         "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAulH2mMsm8eP7bpAmlXI9EE5TQ8tjWBaMDaSP34zB+0+LLjJhbi9dhGV9T5reppaxaU7lzokGaHJX+l9X4sA53G9PlpzQ0JwsGO5Z1E9LaN1CDijqtGNQWr7bdLpy4AwzfhDThY25rwzMg4Wt4LK74Ba7Q5a2M9m0d/gEqs6ZWrK/6DiQ1qnzU7JTm13SZRBvnNhmLBMnSu1sz3"
          "Hc8nE+02oAJC04xCjI8XUowu+sxyJQm7OY97MOvOt9HAXt/jbBVyG1scJTggjYsH1JcfC1xbE1VnNiWSkpid2P+4bXy/GIG90nZ7We4KNMshXEby/FbUZPPERHW1ntCyga58rPJQIDAQAB" )  ;
----- DKIM key sel0 for example.jp
のような内容になっています。この例に沿うと、example.jp のDNSゾーンに、
 ・name : sel._domainkey.example.jp.
 ・type : TXT
 ・content:"v=DKIM1; .... 以下 .... yga58rPJQIDAQAB" まで
を登録します。もう一つ、example.jp のDNSゾーンに
 ・name : _adsp._domainkey.example.jp.
 ・type : TXT
 ・content:"dkim=unknown"
を登録します。このパラメータの意味は下記のとおりです:
unknown該当ドメインからの送信メールには、メール作成者署名(DKIM)がある場合と無い場合がある
all該当ドメインからの送信メールには、必ずメール作成者署名(DKIM)がある
もしメール作成者署名(DKIM)が得られない場合の処置は受信側の任意。(通常は配送する)
discardable該当ドメインからの送信メールには、必ずメール作成者署名(DKIM)がある。
もしメール作成者署名(DKIM)が得られない場合は、受信者はそのメールを破棄することが望まれる
日本国内の電子メール環境を鑑みた場合、ここは「unknown」が無難と思います。

起動・停止スクリプトの設置(FreeBSD に特化している項目)

このスクリプトは一度作っておくと、バージョンアップの際に再作成の必要はありません。

○ OpenDKIM の起動スクリプト
/usr/local/etc/rc.d/ ディレクトリ配下に、milter-opendkim のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh

# PROVIDE: milter-opendkim
# REQUIRE: DAEMON
# BEFORE: mail
# KEYWORD: shutdown

# Define these milteropendkim_* variables in one of these files:
#       /etc/rc.conf
#       /etc/rc.conf.local
#       /etc/rc.conf.d/milteropendkim
#
# milteropendkim_enable (bool):   Set to "NO" by default.
#                             Set it to "YES" to enable dkim-milter
# milteropendkim_uid (str):       Set username to run milter.
# milteropendkim_gid (str):       Set group to run milter.
# milteropendkim_profiles (list): Set to "" by default.
#                             Define your profiles here.
# milteropendkim_cfgfile (str):   Configuration file. See opendkim.conf(5)
#
# milteropendkim_${profile}_* :   Variables per profile.
#                             Sockets must be different from each other.
#
# milteropendkim_socket_perms (str):
#                                 Permissions for local|unix socket.
#
#  all parameters below now can be set in opendkim.conf(5).
# milteropendkim_socket (str):    Path to the milter socket.
# milteropendkim_domain (str):    Domainpart of From: in mails to sign.
# milteropendkim_key (str):       Path to the private key file to sign with.
# milteropendkim_selector (str):  Selector to use when signing
# milteropendkim_alg (str):       Algorithm to use when signing
# milteropendkim_flags (str):     Flags passed to start command.

. /etc/rc.subr

name="milteropendkim"
rcvar=milteropendkim_enable

extra_commands="reload"
start_precmd="dkim_prepcmd"
start_postcmd="dkim_start_postcmd"
stop_postcmd="dkim_postcmd"
command="/usr/local/sbin/opendkim"
_piddir="/var/run/milteropendkim"
pidfile="${_piddir}/pid"
sig_reload="USR1"

load_rc_config $name

#
# DO NOT CHANGE THESE DEFAULT VALUES HERE
#
: ${milteropendkim_enable:="NO"}
: ${milteropendkim_uid:="opendkim"}
: ${milteropendkim_gid:="mailuser"}
: ${milteropendkim_cfgfile:="/usr/local/etc/opendkim/opendkim.conf"}
: ${milteropendkim_socket_perms:="0775"}

# Options other than above can be set with $milteropendkim_flags.
# see dkim-milter documentation for detail.

extra_commands="reload"
start_precmd="dkim_prepcmd"
start_postcmd="dkim_start_postcmd"
stop_postcmd="dkim_cleansockets"
command="/usr/local/sbin/opendkim"
sig_reload="USR1"

dkim_cleansockets()
{
    case ${milteropendkim_socket%:*} in
    local|unix)
        rm -f "${milteropendkim_socket#*:}"
        ;;
    esac
}

dkim_get_pidfile()
{
        if get_pidfile_from_conf PidFile ${milteropendkim_cfgfile#-x }; then
                pidfile="$_pidfile_from_conf"
        else
                pidfile="/var/run/milteropendkim/${profile:-pid}"
        fi
}

dkim_prepcmd()
{
    dkim_cleansockets
    dkim_get_pidfile
    if [ ! -d "$(dirname "$pidfile")" ]; then
        mkdir "$(dirname "$pidfile")"
    fi
    case ${milteropendkim_socket%:*} in
    local|unix)
        socketfile=${milteropendkim_socket#*:}
        install -d -o ${milteropendkim_uid%:*} -g $milteropendkim_gid \
            -m ${milteropendkim_socket_perms} \
               ${pidfile%/*} ${socketfile%/*}
        ;;
    esac
}

dkim_start_postcmd()
{
    case ${milteropendkim_socket%:*} in
    local|unix)
        # postcmd is executed too fast and socket is not created before checking...
        sleep 1
        chmod -f ${milteropendkim_socket_perms} ${milteropendkim_socket#*:}
        ;;
    esac
}

if [ -n "$2" ]; then
    profile="$2"
    if [ -n "${milteropendkim_profiles}" ]; then
        pidfile="${_piddir}/${profile}.pid"
        eval milteropendkim_enable="\${milteropendkim_${profile}_enable:-${milteropendkim_enable}}"
        eval milteropendkim_socket="\${milteropendkim_${profile}_socket:-}"
        eval milteropendkim_socket_perms="\${milteropendkim_${profile}_socket_perms:-}"
        if [ -z "${milteropendkim_socket}" ];then
            echo "You must define a socket (milteropendkim_${profile}_socket)"
            exit 1
        fi
        eval milteropendkim_cfgfile="\${milteropendkim_${profile}_cfgfile:-${milteropendkim_cfgfile}}"
        eval milteropendkim_domain="\${milteropendkim_${profile}_domain:-${milteropendkim_domain}}"
        eval milteropendkim_key="\${milteropendkim_${profile}_key:-${milteropendkim_key}}"
        eval milteropendkim_selector="\${milteropendkim_${profile}_selector:-${milteropendkim_selector}}"
        eval milteropendkim_alg="\${milteropendkim_${profile}_alg:-${milteropendkim_alg}}"
        eval milteropendkim_flags="\${milteropendkim_${profile}_flags:-${milteropendkim_flags}}"
        if [ -f "${milteropendkim_cfgfile}" ];then
            milteropendkim_cfgfile="-x ${milteropendkim_cfgfile}"
        else
            milteropendkim_cfgfile=""
        fi
        if [ -n "${milteropendkim_socket}" ];then
            _socket_prefix="-p"
        fi
        if [ -n "${milteropendkim_uid}" ];then
            _uid_prefix="-u"
            if [ -n "${milteropendkim_gid}" ];then
                milteropendkim_uid=${milteropendkim_uid}:${milteropendkim_gid}
            fi
        fi
        if [ -n "${milteropendkim_domain}" ];then
            milteropendkim_domain="-d ${milteropendkim_domain}"
        fi
        if [ -n "${milteropendkim_key}" ];then
            milteropendkim_key="-k ${milteropendkim_key}"
        fi
        if [ -n "${milteropendkim_selector}" ];then
            milteropendkim_selector="-s ${milteropendkim_selector}"
        fi
        if [ -n "${milteropendkim_alg}" ];then
            milteropendkim_alg="-S ${milteropendkim_alg}"
        fi
        dkim_get_pidfile
        command_args="-l ${_socket_prefix} ${milteropendkim_socket} ${_uid_prefix} ${milteropendkim_uid} -P ${pidfile} ${milteropendkim_cfgfile} 
${milteropendkim_domain} ${milteropendkim_key} ${milteropendkim_selector} ${milteropendkim_alg}"
    else
        echo "$0: extra argument ignored"
    fi
else
    if [ -n "${milteropendkim_profiles}" ] && [ -n "$1" ]; then
        if [ "$1" != "restart" ]; then
            for profile in ${milteropendkim_profiles}; do
                echo "===> milteropendkim profile: ${profile}"
                /usr/local/etc/rc.d/milter-opendkim $1 ${profile}
                retcode="$?"
                if [ "${retcode}" -ne 0 ]; then
                    failed="${profile} (${retcode}) ${failed:-}"
                else
                    success="${profile} ${success:-}"
                fi
            done
            exit 0
        else
            restart_precmd=""
        fi
    else
        if [ -f "${milteropendkim_cfgfile}" ];then
            milteropendkim_cfgfile="-x ${milteropendkim_cfgfile}"
        else
            milteropendkim_cfgfile=""
        fi
        if [ -n "${milteropendkim_socket}" ];then
            _socket_prefix="-p"
        fi
        if [ -n "${milteropendkim_uid}" ];then
            _uid_prefix="-u"
            if [ -n "${milteropendkim_gid}" ];then
                milteropendkim_uid=${milteropendkim_uid}:${milteropendkim_gid}
            fi
        fi
        if [ -n "${milteropendkim_domain}" ];then
            milteropendkim_domain="-d ${milteropendkim_domain}"
        fi
        if [ -n "${milteropendkim_key}" ];then
            milteropendkim_key="-k ${milteropendkim_key}"
        fi
        if [ -n "${milteropendkim_selector}" ];then
            milteropendkim_selector="-s ${milteropendkim_selector}"
        fi
        if [ -n "${milteropendkim_alg}" ];then
            milteropendkim_alg="-S ${milteropendkim_alg}"
        fi
        dkim_get_pidfile
        command_args="-l ${_socket_prefix} ${milteropendkim_socket} ${_uid_prefix} ${milteropendkim_uid} -P ${pidfile} ${milteropendkim_cfgfile} 
${milteropendkim_domain} ${milteropendkim_key} ${milteropendkim_selector} ${milteropendkim_alg}"
    fi
fi
run_rc_command "$1"
OpenDKIM の設定は、これで終わりです。

2022/12/26(月)メールサーバの中規模改修と基礎知識(4)~ clamAV

clamav_logo.png

clamAV は、サーバサイドでの使用を想定したコンピュータウィルス検出ソフトウェアです。
2022/11/23 に最新版 Ver 1.0.0 がリリースされており、Ver 0.103.7 が旧版の安定バージョンとして 2022/07/26 にリリースされています。

ClamAV の概要

送信・受信共に電子メールメッセージを検査させるため、postfix で、milter インタフェースを介した動作とします。
milter は、インターネット黎明期から Unix 系で使用されてきた Sendmail(電子メール交換サーバソフトウェア) で「拡張機能」として初めて出現し、「Sendmail Content Management API」の短縮名称とされています。

つまり、電子メール送受信に際して、さまざまな拡張機能を提供する仕組みなのですが、「これ」といった仕様書がなく、過去に開発されたインタフェース手順を、ソースコードレベルで継承・踏襲しているといった感じのようです。

この「付加機能提供インタフェース」である、milter を利用して、電子メールメッセージにコンピュータウィルスが含まれているか否かを検査し、結果を反映するのが clamAV の役割です。
混同されがちなものに「コンピュータウィルス」と「spam」があります。前者は、使用しているパソコンや情報機器に具体的な障害を与えるものを含むもの(具体的には添付ファイルや障害を与えるコンテンツへのリンク)、「spam」は嫌がらせ目的や不快感を繰り返し助長するメッセージを主に指します。

なので、コンピュータウィルスの検査で、spam を実用的に検出するのは難しいものがあり、ClamAV と SpamAssassin を併用するのは、そういった観点があります。

ClamAV のインストール

インストール作業は、root アカウントで行います。
clamAV をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
他のソフトウェアと比較すると、事前に必要なものが多いですが、これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
 ・perl5-5.36.0 	 (Ports から カテゴリ:lang)
 ・autoconf-2.71	 (Ports から カテゴリ:devel)
 ・libltdl-2.4.7	 (Ports から カテゴリ:devel)
 ・libtool-2.4.7	 (Ports から カテゴリ:devel)
 ・pcre2-10.40		 (Ports から カテゴリ:devel)
 ・cmake-core-3.24.3_2	 (Ports から カテゴリ:devel)
 ・libxml2-2.10.3_1	 (Ports から カテゴリ:textproc)
 ・curl-7.86.0		 (Ports から カテゴリ:ftp)
 ・json-c-0.16		 (Ports から カテゴリ:devel)
 ・jsoncpp-1.9.5	 (Ports から カテゴリ:devel)
 ・check-0.15.2	 (Ports から カテゴリ:devel)
 ・libmspack-0.10.1	 (Ports から カテゴリ:archivers)
 ・tomsfastmath-0.13.1_4(Ports から カテゴリ:math)
 ・python39-3.9.16	 (Ports から カテゴリ:lang)
 ・py39-pytest-7.2.0,1	 (Ports から カテゴリ:devel)
 ・rust-1.66.0		 (Ports から カテゴリ:lang)
更に、インストールに先立ち、clamAV のセキュリティポリシーに従うため、ユーザ clamav を、vipw や useradd コマンドで追加します:

vipw コマンドの場合は、下記の1行を編集画面で追加します。
clamav::65002:65002::0:0:Clam antivirus:nonexistent:/usr/sbin/nologin
vipw でユーザ追加した場合、/etc/group に下記の1行を追加しておきます:
clamav:*:65002:
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd clamav
clamAV のコンパイルは、Ver 0.105.0 から、cmake を使うようになりました。下記のように順に実施していきます:
# cp clamav-1.0.0.tar.gz /usr/local/src	 
# cd /usr/local/src	 
# tar xvzf clamav-1.0.0.tar.gz	 
# cd clamav-1.0.0	 
# mkdir build	 
# cd build	 
# cmake .. -D ENABLE_JSON_SHARED=OFF 	(JSONライブラリはスタティックリンク)
Configuration に成功すると、下記のようなサマリーが出力されます。
何かしらの問題がある場合は、問題点を指摘するメッセージが表示され、Configuration プロセスは失敗します。
-- Configuration Options Summary --
    Package Version:        ClamAV 1.0.0
    libclamav version:      11:0:0
    libfreshclam version:   2:2:0
    Install prefix:         /usr/local
    Install database dir:   /usr/local/share/clamav
    Install config dir:     /usr/local/etc
    Host system:            FreeBSD-13.1-RELEASE-p5
    Target system:          FreeBSD-13.1-RELEASE-p5
    Compiler:
        Build type:         RelWithDebInfo
        C compiler:         /usr/bin/cc
        C++ compiler:       /usr/bin/c++
        Rust toolchain:     /usr/local/bin/cargo (1.66.0)
        CFLAGS:             -O2 -g
        CXXFLAGS:           -O2 -g
        WARNCFLAGS:          -Wall -Wextra -Wformat-security
    Build Options:
        Build apps:         ON
        Shared library:     ON
        Static library:     OFF
        Enable UnRAR:       ON
        Examples:           OFF
        Tests:              ON
        Build man pages:    ON
        Build doxygen HTML: OFF
        Maintainer Mode:    OFF
    Build Extras:
        Build milter:       ON  (toggle with -DENABLE_MILTER=ON/OFF)
-- Engine Options --
        Bytecode Runtime:
            interpreter
-- Test Dependencies --
        Unit Test Framework:
            libcheck        /usr/local/include
                            /usr/local/lib/libcheck.so
        Feature Test Framework:
            python3         /usr/local/bin/python3.9
            test command    pytest;-v
-- libclamav Dependencies --
        Compression support:
            bzip2           /usr/include
                            /usr/lib/libbz2.so
            zlib            /usr/include
                            /usr/lib/libz.so
        XML support:
            libxml2         /usr/local/include/libxml2;/usr/local/include/libxml2
                            /usr/local/lib/libxml2.so
        RegEx support:
            libpcre2        /usr/local/include
                            /usr/local/lib/libpcre2-8.so
        Crypto support:
            openssl         /usr/include
                            /usr/lib/libssl.so;/usr/lib/libcrypto.so
        JSON support:
            json-c          /usr/local/include/json-c
                            /usr/local/lib/libjson-c.a
        Threading support:
            pthread
        Locale support:
            iconv           /usr/local/include
                            /usr/local/lib/libiconv.so
-- libfreshclam Extra Dependencies --
        HTTP support:
            curl            /usr/local/include
                            /usr/local/lib/libcurl.so
-- Application Extra Dependencies --
        GUI support:
            ncurses         /usr/include
                            /usr/lib/libncurses.so
        Milter Support:
            libmilter       /usr/include
                            /usr/lib/libmilter.so

-- Configuring done
-- Generating done
-- Build files have been written to: /usr/local/src/clamav-1.0.0/build
引き続き、以下の手順でソースコードからの構築を実施します:
# cmake --build . 			(ウィルススキャナの構築)
# cmake --build . --target install 	(ウィルススキャナのインストール)
続いて、動作に必要な環境とログファイルの初期化です。
# mkdir /var/run/clamav 		(clamd のプロセス情報保存領域)
# chown clamav:mailuser /var/run/clamav 	 
 	 
# touch /var/log/clamd.log	 
# chown clamav /var/log/clamd.log	 
# touch /var/log/clamav-milter.log	 
# chown clamav /var/log/clamav-milter.log	 
# touch /var/log/freshclam.log	 
# chown clamav /var/log/freshclam.log	 
# mkdir /usr/local/share/clamav		(ウィルスデータベース格納先)
# chown clamav:mailuser /usr/local/share/clamav
clamd,freshclam,clamAV-milter それぞれ、別々に動作します。
それぞれの設定ファイルを、/usr/local/etc 配下に作成します。通常、ここまでの手順を踏んでいれば、インストールされているはずです。

先ず、/usr/local/etc/clamd.conf の設定ファイルを、下記を参考に該当行を変更します。
# Example				  ← 削除
LogFile /var/log/clamd.log		  ← 追加又は変更
LogTime yes				  ← 追加又は変更
PidFile /var/run/clamav/clamd.pid	  ← 追加又は変更
DatabaseDirectory /usr/local/share/clamav  ← 追加又は変更
LocalSocket /var/run/clamav/clamd	  ← 追加又は変更
FixStaleSocket yes			  ← 追加又は変更
User clamav				  ← 追加又は変更
AllowSupplementaryGroups yes		  ← 追加又は変更
続いて、/usr/local/etc/freshclam.conf の設定ファイルを、下記を参考に該当行を変更します。
# Example				  ← 削除
DatabaseDirectory /usr/local/share/clamav  ← 追加又は変更
UpdateLogFile /var/log/freshclam.log	  ← 追加又は変更
PidFile /var/run/clamav/freshclam.pid	  ← 追加又は変更
DatabaseOwner clamav			  ← 追加又は変更
DatabaseMirror db.jp.clamav.net		  ← 追加又は変更
DatabaseMirror clamav.nara.wide.ad.jp	  ← 追加又は変更
NotifyClamd /usr/local/etc/clamd.conf	  ← 追加又は変更
最後に、/usr/local/etc/clamav-milter.confの設定ファイルを、下記を参考に該当行を変更します。
# Example					 ← 削除
MilterSocket unix:/var/run/clamav/clmilter.sock	 ← 追加又は変更
User clamav					 ← 追加又は変更
AllowSupplementaryGroups yes			 ← 追加又は変更
PidFile /var/run/clamav/clamav-milter.pid	 ← 追加又は変更
TemporaryDirectory /var/tmp			 ← 追加又は変更
ClamdSocket unix:/var/run/clamav/clamd		 ← 追加又は変更
LogFile /var/log/clamav-milter.log		 ← 追加又は変更
3つの設定ファイル変更が終わったら、以下の手順でウィルスパターン定義を初期化・最新版に更新します:
# rehash	 
# freshclam 	(ウィルススキャンデータベースを最新の状態にする)

起動・停止スクリプトの設置(FreeBSD に特化している項目)

これらのスクリプトは一度作っておくと、バージョンアップの際に再作成の必要はありません。

○ clamd の起動スクリプト
/usr/local/etc/rc.d/ ディレクトリ配下に、clamav_clamd のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh

# PROVIDE: clamav_clamd
# REQUIRE: LOGIN
# BEFORE: mail
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable clamd:
#
# clamav_clamd_enable="YES"
# clamav_clamd_flags=""
#
# See clamd(8) for flags
#

. /etc/rc.subr

name=clamav_clamd
rcvar=clamav_clamd_enable

# read settings, set default values
load_rc_config "$name"

: ${clamav_clamd_enable:=NO}
: ${clamav_clamd_socket="/var/run/clamav/clamd"}
: ${clamav_clamd_pidfile="/var/run/clamav/clamd.pid"}
: ${clamav_clamd_user="clamav"}

command=/usr/local/sbin/clamd
required_dirs=/var/db/clamav
required_files=/usr/local/etc/clamd.conf

start_precmd=clamav_clamd_precmd
extra_commands=reload
reload_cmd="/usr/local/bin/clamdscan --reload"

clamav_clamd_precmd()
{
        local rundir=${clamav_clamd_pidfile%/*}
        if [ ! -d $rundir ] ; then
                install -d -m 0755 -o ${clamav_clamd_user} -g ${clamav_clamd_user} $rundir
        fi
        if [ ! -f /var/db/clamav/main.cvd -a ! -f /var/db/clamav/main.cld ];then
                echo "Missing /var/db/clamav/*.cvd or *.cld files.  You must run freshclam first"
                exit 1
        fi
}

run_rc_command "$1"
○ freshclam の起動スクリプト
これは、一定時間毎に新しいコンピュータウィルス定義パターンを更新するためのものです。
/usr/local/etc/rc.d/ ディレクトリ配下に、clamav_freshclam のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh

# PROVIDE: clamav_freshclam
# REQUIRE: LOGIN clamav_clamd
# BEFORE: mail
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable the freshclam daemon:
#
# clamav_freshclam_enable="YES"
# clamav_freshclam_flags=""
#
# See freshclam(1) for flags
#

. /etc/rc.subr

name=clamav_freshclam
rcvar=clamav_freshclam_enable

# read settings, set default values
load_rc_config ${name}

: ${clamav_freshclam_enable:=NO}
: ${clamav_freshclam_pidfile=/var/run/clamav/freshclam.pid}
: ${clamav_freshclam_user=clamav}

command=/usr/local/bin/freshclam
pidfile=${clamav_freshclam_pidfile}
command_args="--daemon -p ${pidfile}"
required_dirs=/var/db/clamav
required_files=/usr/local/etc/freshclam.conf

start_precmd=clamav_freshclam_precmd

clamav_freshclam_precmd()
{
        local rundir=${clamav_freshclam_pidfile%/*}
        if [ ! -d $rundir ] ; then
                install -d -m 0755 -o ${clamav_freshclam_user} -g ${clamav_freshclam_user} $rundir
        fi
}

run_rc_command "$1"
○ clamav-milter の起動スクリプト
/usr/local/etc/rc.d/ ディレクトリ配下に、clamav_milter のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh

# PROVIDE: clamav_milter
# REQUIRE: LOGIN clamav_clamd
# BEFORE: mail
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable clamav-milter:
#
# clamav_milter_enable="YES"
#
# See clamav-milter(1) for flags
#

. /etc/rc.subr

name=clamav_milter
rcvar=clamav_milter_enable

load_rc_config $name

: ${clamav_milter_enable:=NO}
: ${clamav_milter_socket="/var/run/clamav/clmilter.sock"}
: ${clamav_milter_conf="/usr/local/etc/clamav-milter.conf"}
: ${clamav_milter_flags="-c ${clamav_milter_conf}"}
: ${clamav_milter_socktimeout=60}
: ${clamav_milter_socket_mode=777}
: ${clamav_milter_socket_user=clamav}
: ${clamav_milter_socket_group=mailuser}
: ${clamav_clamd_enable:=NO}
: ${clamav_clamd_socket="/var/run/clamav/clamd"}

command=/usr/local/sbin/clamav-milter
required_dirs=/var/db/clamav
required_files=${clamav_milter_conf}

start_precmd=start_precmd
start_postcmd=start_postcmd

start_precmd()
{
        if [ -S "$clamav_milter_socket" ]; then
                warn "Stale socket $clamav_milter_socket removed."
                rm "$clamav_milter_socket"
        fi
        rc_flags="${flags:-$clamav_milter_flags}"

        clamav_clamd_socket_prefix=${clamav_clamd_socket%:*}
        # We can have inet or inet6, try to remove 6
        clamav_clamd_socket_prefix=${clamav_clamd_socket_prefix%6}

        if checkyesno clamav_clamd_enable && [ "x$clamav_clamd_socket" != "x" -a "${clamav_clamd_socket_prefix}" != "inet" ]; then
                echo -n "Waiting for clamd socket.. "
                i=${clamav_milter_socktimeout}
                while [ $i -ne 0 ]
                do
                        [ -S "$clamav_clamd_socket" ] && break
                        if [ `expr $i % 10` -eq 0 ]; then
                                echo -n "${i}.. "
                        fi
                        sleep 1
                        i=$(($i-1))
                done
                echo
                if [ $i -eq 0 ]; then
                        echo "There is no clamd socket (${clamav_clamd_socket})!"
                        exit 1
                fi
        fi
}

start_postcmd()
{
        clamav_milter_socket_prefix=${clamav_milter_socket%:*}
        # We can have inet or inet6, try to remove 6
        clamav_milter_socket_prefix=${clamav_milter_socket_prefix%6}

        if [ "x$clamav_milter_socket" != "x" -a "${clamav_milter_socket_prefix}" != "inet" ]; then
                echo -n "Waiting for clamav-milter socket.. "
                i=${clamav_milter_socktimeout}
                while [ $i -ne 0 ]
                do
                        [ -S "$clamav_milter_socket" ] && break
                        if [ `expr $i % 10` -eq 0 ]; then
                                echo -n "${i}.. "
                        fi
                        sleep 1
                        i=$(($i-1))
                done
                echo
                if [ $i -eq 0 ]; then
                        echo "There is no clamav-milter socket (${clamav_milter_socket})!"
                        exit 1
                fi
                /bin/chmod ${clamav_milter_socket_mode} ${clamav_milter_socket}
                /usr/sbin/chown ${clamav_milter_socket_user}:${clamav_milter_socket_group} ${clamav_milter_socket}
        fi
}

run_rc_command "$1"
clamAV のインストールは、これでとりあえず完了です。