2024/06/21(金)OpenLDAP 2.6.8 では、Cyrus-SASL 抜きの構築は出来ない

これも嵌ったので自分メモ:
OpenLDAP をソースコードから構築する場合、configure で指定するオプションに --without-cyrus-sasl を指定して、Cyrus-SASL のサポートを外すことが出来ます。
ところが、こうすると、 OpenLDAP 2.6.8 では、途中で下記のエラーを吐いて構築不能になるのです:
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_host_connected_to' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_config' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_get_option' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_open' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_set_option' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_cbinding' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_cbinding_parse' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_install' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_dispose' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_lock' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_new' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_unlock' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_remove' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_secprops' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_secprops_unparse' failed: symbol not defined
cc: error: linker command failed with exit code 1 (use -v to see invocation)
これは、Cyrus-SASL をインストールし、更に --without-cyrus-sasl のオプションを外さないとエラーが取れない。
弊社の使用形態では Cyrus-SASL は使わないため、このオプションは必須の形で指定していたが、嫌でも Cyrus-SASL をインストールし、サポートさせないといけない状態に…

このバージョンだけの問題なのか、本来は Cyrus-SASL と OpenLDAP はセットで使う仕様であるので、今後の強制的な方針なのかは判りません。
メンテナンス・運用管理の観点からは、出来る限り余計なものは入れたくないんだけどね・・・

2023/11/12(日)Python 一部モジュール・ライブラリが更新できない

FreeBSD12 → FreeBSD13 に更新後、アプリケーションのライブラリ等が旧OSバージョンにて構築したものだったりすると、それが原因で謎の動作不具合起こしたりする場合が稀にあるため、「安定動作」を目的として、全てのアプリケーションを再構築します。

通常、OSバージョンアップの場合、前バージョンまでの動作互換性は保証しているので、必ずしも必要ではないはずなのですが、それでも不可解な挙動が生じる場合があり、それを未然に防ぐために「全てのアプリケーション再構築」という作業を行うことにしているのです。

多くのマトモな方々には信じられないかもしれないですが、この事業を始めたばかりの頃、
『たった1回のアプリケーション障害で、しかもたった1日で一方的なサーバホスティング解消』をされたことがあり、これは純粋にアプリケーションの不具合で、OSアップデートが問題ということでもなく、運用側でどうすることも出来ない不可抗力な原因の事象でした。

当然、「一方的」だったので、説明責任のかけらも果たすことが出来ませんでした。
まぁ、そんなに信用できないなら「最初から易々と近づくな」と言いたいし、何より舐められまくってますね。
そんな態度の「上から目線なクライアント」は、相手にしないことは言うまでもないです。
きっと、他所でも毛嫌いされていることだろうと思われます。
20年以上やっていますが、この類の不具合は、この件含めて2回。
2回目は説明責任果たせたので、理解してもらえました。過去20年に限れば安定稼働しています。

前置きが長くなり過ぎたのですが、ここから本題。
FreeBSD12 → FreeBSD13 にOSアップデートして、アプリケーション再構築をすると、一部の Python アプリケーションが再構築出来ずに、以下のようなエラーを吐いてしまう。
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/usr/local/lib/python3.9/site-packages/setuptools/__init__.py", line 18, in 
    from setuptools.dist import Distribution
  File "/usr/local/lib/python3.9/site-packages/setuptools/dist.py", line 34, in 
    from ._importlib import metadata
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 39, in 
    disable_importlib_metadata_finder(metadata)
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 28, in disable_importlib_metadata_finder
    to_remove = [
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 31, in 
    if isinstance(ob, importlib_metadata.MetadataPathFinder)
AttributeError: module 'importlib_metadata' has no attribute 'MetadataPathFinder'

ERROR Backend subprocess exited when trying to invoke get_requires_for_build_wheel
*** Error code 1
この現象は、時々起きるらしく、外国のメンテナンス作業者の解決策として、どうやら
# cd /usr/local/lib
# rm -rf python3.*
としてから、
# portupgrade -rf python.3.9.18
(インストールされている Python バージョンに合わせる)
とすると、解消する模様。やや強引かつ豪快な解決手段だが、これが確実なのだとか。
これをやったら、確かに問題は解消しました。

2023/04/15(土)整流回路の平滑コンデンサは容量が大きければいいというものではないらしい

自作の安定化電源装置にて、再びパワートランジスタが破損したので、その原因を調査している途上で設計を色々見直しているのだが・・・
10年近く前に、PC-9801 のBASIC で組んで公開されていた整流回路シミュレーションがあったので、手元のMZ-2500 BASIC-M25 に移植して使っています。最低限機能させるための移植なので、色々変な部分はあるんですが。。orz

引用元の書籍を紹介しようとして、手持ちの書籍を漁ったのですが、何故か見つからない。
ということで、「原作者の方、申し訳ないです・・・」ということで、、、
いずれにしても、MZ-2500だったから移植出来たのです。他の機器だったら、かなり苦しい。

ここから本題なのですが、当初は、こんなふうにしていました ↓
20230415_6800uF.png


そして、おもむろにこのシミュレーションを動作させてみると・・・
20230415_6800uF.jpg


負荷電流1Aという、意図的に悪い動作条件を与えたんですが、こんな感じになりました。
黄色の線は電流を示しますが、正確ではないし、描画がおかしいので、とりあえず無視。
注目すべきは赤っぽい実線。
このあとに5V出力の3端子レギュレータが繋がるのですが、安定動作には7.5V以上の電圧が必要なので、14ms あたりまでは何かしらの不具合が出る可能性がある。
静電容量が大きすぎて、電圧が上がり切りません。

ということで、平滑コンデンサ C01 を下記のようにしてみました ↓
20230415_2200uF.png


静電容量だけ変えて、他は同じ条件です。
20230415_2200uF.jpg


ここまでは、安定化電源装置を動作させるための補助電源部の話で、
本当の本題は、PT2-D3-C05 で構成する、この平滑回路です ↓
20230415_22000uF.png


22mF(22,000μF)というオーディオアンプの電源に使うような大容量電解コンデンサを当初使っていました。
安定化電源装置の最高出力電圧は24V、最高出力電流は3Aの設計です。
24Vの安定化出力を出すためには、平滑回路の電圧は27Vは欲しい。
これを安定化電源装置の設計最大電流である3Aでシミュレーションさせると・・・
20230415_22000uF.jpg


40ms あたりまで、所要の電圧にはならないことが判りました。
試行錯誤で、落ち着いたのがこれ ↓
20230415_4700uF.png


シミュレーション結果は以下。
20230415_4700uF.jpg

ギリギリ大丈夫か? みたいな感じです。今までは経験と勘だけで適当に決めていたところだったが。。orz

Rs というのは、変圧トランスやコンデンサを結線する配線抵抗等ですが、これを正確に決めるのは難しく、代表的な値として0.5Ωと見做すことが多い模様。
恐らく、殆どのケースでは0.5Ωよりも小さく、このシミュレーションで得られる電圧よりは若干高めになると思います。

シミュレーション上の紫の実線の山と赤っぽい実線の山の間隔が広いと、それだけ電力損失が増え、整流ダイオードあたりが発熱しやすくなるのではないかと思われます。
ですが、このことがパワートランジスタの破損に直結するとは思えないです。

2023/02/26(日)perl で子プロセスで su を実行させると、親プロセスも道づれで消える

ごく単純と思われる、サーバプログラムで、
     《起 動》
       ↓
  ┌─→《接続要求待ち》 ← LAN・WANからの接続要求
  │    ↓
  │  《接続応答》
  │    ↓
  │  《子プロセスをfork()》→────┐
  │    ↓(親プロセス)      │(子プロセス)
  │    │             ↓
  │    │           《実処理》
  │    │             │
  |  《子プロセス終了待ち》←──《処理終了》
  │    ↓
  └←《次の接続を待つ》
のような概略構造で、 root 権限で稼働させる代物だが、この構造で、子プロセス内で、perl にて

$status = system (/usr/bin/su username -c "exec comannd ... ") ;

または

$result = `/usr/bin/su username -c "exec comannd ... "`

みたいなことをやらせると、当該処理は実行するものの、実行後、親プロセスまでも終了してしまう。
どちらも更に子プロセスを起こし、 username 権限で実行するというところまでは各所に記載があるため判るが、処理終了時の動作については記載が見当たらず、これ以上の調査に時間を費やせないという現実。

結局、su をやめ、root 権限で動作させ、実行後、このプログラムで生成された必要なファイルを chown するという策で回避。
バグなのかもしれないが、何かやらかしてなるべきしてこうなっているかも知れずで、よく判らないところですね。

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="/tmp/_${randomid}_1"
tmpfname2="/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 のインストールは、これでとりあえず完了です。