2022/12/25(日)メールサーバの中規模改修と基礎知識(3)~ Dovecot+Pigeonhole,cert-bot

次は、電子メール受信の中核を成す dovecot まわりの構築です。
dovecot は、どうやら「ダブコット」或いは「ダヴコット」と称するようです。
日本人だと、どうしても「どべこっと」と言いたくなるのではないかと思う。。

概要

dovecot は、2022/12/22 に最新版 Ver 2.3.20 がリリースされました。
今回、dovecot で実現する構成は、概ね下記のような、下手くそな手書きで描いたようなものになります。
20221225_dovecot_2022.png

【注:この構成図は電子メール受信処理に特化したイメージ図であり、実際の dovecot 内部構成を示したものではありません。
   むしろ全く無関係です。】


構築するメールサーバの Port 110,143,993,995 で電子メールを受信するアクションを待ち、そのアクションに従ってサーバ上で受信している電子メールメッセージをダウンロードするのが、dovecot の主な任務です。

POP3 も IMAP4 も受信者を認識する個別情報(アカウント名とパスワード) にてユーザ認証をし、受信者固有のメールボックスを特定する処理を行います。そのような情報は、弊社では OpenLDAP のデータベースにて管理しており、そのために、OpenLDAP へアカウント問い合わせを行う Port 389 が使われます。OpenLDAP のSSL/TLS 対応ポートに Port 636 がありますが、OpenLDAP を同じサーバ上で稼働させるため、localhost:389 への接続となり、SSL/TLS は不要です。

また、Postfix から、配送されてくる電子メールメッセージが、LMTP インタフェースを介して流れてきます。
LMTP インタフェースは、unix ソケットで実現します。こうすることで、サーバのリソース消費を大きく増やさずに且つ高速に受信処理が出来るようになります。LMTP でも受信者のメールボックスを特定させるために、OpenLDAP へのアカウント問い合わせを実施します。

dovecotのインストール

ここからは、基本的に root アカウントでの作業となります。
「ソースコードのコンパイルは、root以外の一般ユーザで行うべき」というポリシーを堅持する開発者が居られますが、大抵の場合、上手くいきません。

dovecot をインストールする場合、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)
 ・OpenLDAP 2.6.3 以上 (これはソースコードからの構築を強く推奨)
このあたりは、バージョンアップで刻々変わっていくこともあるのですが、管理する以上は、そのような変化を知ることが重要です。
Ports や Package では、このあたりを余計なモジュールまで自動インストールしてしまうことが多々あり、「いざトラブル!」という際に却って対応困難になる原因を作ります。
それが嫌なので、要所なサーバサイドソフトウェアについては、Ports や Package に頼らずに、敢えてソースコードからの構築を行い、技術対応力低下の防止に努力しています。

更に、インストールに先立ち、dovecot のセキュリティポリシーに従うため、ユーザ dovecot・ユーザ dovenull を、vipw や useradd コマンドで追加します:
dovecot::2002:3000::0:0:dovecot MDA:nonexistent:/usr/sbin/nologin
dovenull::2007:3000::0:0:dovecot MRA:nonexistent:/usr/sbin/nologin
vipw でユーザ追加した場合、/etc/group に下記の1行を追加しておきます(あとでこれが重要になってくる):
mailuser:*:3000:
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd dovecot
# passwd dovenull
はい。いよいよコンパイル作業です。下記のように順に実施していきます:
# cp dovecot-2.3.19.1.tar.gz /usr/local/src
# cd /usr/local/src
# tar xvzf dovecot-2.3.19.1.tar.gz
# cd dovecot-2.3.19.1
 	 
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include'	(configure が上手くライブラリを探せないため)
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib'     (configure が上手くライブラリを探せないため)
# setenv LD_LIBRARY_PATH '/usr/local/lib /usr/lib'   (コンパイルが上手くいかないため)

※以下、説明のために改行していますが、 ./configure の部分は、改行せずに、半角スペース区切りで、一気に入力。
# ./configure --sysconfdir=/usr/local/etc (設定ファイル群を格納するディレクトリ)
       --localstatedir=/var     (unix ソケットなどを格納するルートディレクトリ)
       --with-ldap=yes       (LDAP 認証をサポートする)
       --with-ssldir=/usr      (openssl のインストール位置を知らせる)
       --with-zlib         (zlib[gz 形式圧縮] をサポートする)
       --with-libwrap        (TCP Wrapper をサポートする)
       --without-bsdauth      (BSD 認証はサポートしない)
       --without-pam         (pam 認証はサポートしない)
       --without-nss        (nss はサポートしない)
上記は、dovecot 2.3.19.1 の場合で、バージョンが変わると、この部分も変わります。適宜解釈を。
./configure が成功すると、下記のような情報が出力されます:
Install prefix . : /usr/local
File offsets ... : 64bit
I/O polling .... : kqueue
I/O notifys .... : kqueue
SSL ............ : yes (OpenSSL)
GSSAPI ......... : no
passdbs ........ : static passwd passwd-file checkpassword ldap
CFLAGS ......... : -std=gnu99 -g -O2 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Wall -W -Wmissing-prototypes -Wmissing-declarations -Wpointer-arith -Wchar-subscripts -Wformat=2 -Wbad-function-cast -Wno-duplicate-decl-specifier -Wstrict-aliasing=2
         : -shadow -pam -bsdauth -sia -vpopmail -sql
userdbs ........ : static prefetch passwd passwd-file checkpassword ldap
         : -vpopmail -sql
SQL drivers .... :
         : -pgsql -mysql -sqlite -cassandra
Full text search : squat
         : -lucene -solr
ここでは、OpenLDAP でユーザ管理をすることを前提にしているため、passdbs 行と userdbs 行に 'ldap' の文字列が存在することを確認します。
# make
# make install
# cd /usr/local/libexec/dovecot
# chmod 4750 dovecot-lda	(setuid でメール配信出来るようにする)
# chgrp mailuser dovecot-lda	(setuid でメール配信出来るようにする)
結構、大きなプログラム群なので、make は、そこそこの時間がかかります。
dovecot-lda は、Courier maildrop に相当する実行モジュールで、setuid を可能にすることで、後述する pignonhole で、メールアカウント毎の動作が可能になります。

次に、
# cd /usr/local/etc/dovecot
として、設定内容をこのディレクトリに記述していきます。dovecot 2.x では、設定ファイルが細かく分かれており、既存Webサイトにおける同種の説明も、教科書的にそれに沿ったものになっていますが、却って管理しにくくなるという我儘から、最低限の設定ファイルを上記ディレクトリに書き込んでいきます。

まずは、 dovecot.conf から〔注:不要な設定があるかもしれません(当方の環境では警告もエラーも出ません)〕:
auth_cache_negative_ttl = 30 mins
auth_cache_size = 10 M
auth_cache_ttl = 30 mins
auth_mechanisms = cram-md5 digest-md5 plain login scram-sha-1 apop
auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@
disable_plaintext_auth = no
first_valid_uid = 3000
last_valid_gid = 4000
last_valid_uid = 4999
mail_location = maildir:/var/mail/%h

!include ssl_sni.conf

passdb {
  args = /usr/local/etc/dovecot/dovecot-ldap.conf
  driver = ldap
}
protocols = imap pop3 lmtp

plugin {
  sieve = ~/.dovecot.sieve
  sieve_plugins = sieve_extprograms
  sieve_extensions = +vnd.dovecot.filter
  sieve_filter_bin_dir = /usr/local/etc/dovecot/sieve-filter
}
  unix_listener /var/spool/postfix/private/auth {
    group = postdrop
    mode = 0660
    user = postfix
  }
  user = root
  vsz_limit = 128 M
}
service imap-login {
  chroot =
  executable = /usr/local/libexec/dovecot/imap-login
  inet_listener imap {
    address = *,[::]
    port = 143
  }
  inet_listener imaps {
    address = *,[::]
    port = 993
  }
  user = dovecot
  vsz_limit = 32M
}
service imap {
  executable = /usr/local/libexec/dovecot/imap
}
service imaps {
  executable = /usr/local/libexec/dovecot/imap
}

service pop3-login {
  chroot =
  executable = /usr/local/libexec/dovecot/pop3-login
  inet_listener pop3 {
    address = *,[::]
    port = 110
  }
  inet_listener pop3s {
    address = *,[::]
    port = 995
  }
  user = dovecot
}
service pop3 {
  executable = /usr/local/libexec/dovecot/pop3
}
service pop3s {
  executable = /usr/local/libexec/dovecot/pop3
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0666
    user = postfix
    group = mailuser
  }
}

service stats {
  unix_listener stats-reader {
    user = vmail
    group = mailuser
    mode = 0660
  }
  unix_listener stats-writer {
    user = vmail
    group = mailuser
    mode = 0660
  }
}

userdb {
  args = /usr/local/etc/dovecot/dovecot-ldap.conf
  driver = ldap
}

protocol imap {
  imap_logout_format = bytes=%i/%o
  mail_plugins = $mail_plugins imap_filter_sieve
}
protocol imaps {
  imap_logout_format = bytes=%i/%o
  mail_plugins = $mail_plugins imap_filter_sieve
}

protocol pop3 {
  pop3_uidl_format = %08Xu%08Xv
}
protocol pop3s {
  pop3_uidl_format = %08Xu%08Xv
}

protocol lmtp {
  postmaster_address = postmaster@mx.example.com
  mail_plugins = $mail_plugins sieve
}
protocol lda {
  auth_socket_path = /var/run/dovecot/auth-master
  hostname = mx.example.com
  mail_plugins = sieve
  postmaster_address = postmaster@mx.example.com
  sendmail_path = /usr/sbin/sendmail
}
次に、 dovecot-ldap.conf〔注:構築する OpenLDAP におけるスキーマ設定に合わせた設定に適宜変えてください。〕
hosts = localhost
sasl_bind = no
tls = no
ldap_version = 3
base = dc=%d,dc=control,dc=isp
deref = never
scope = subtree

user_attrs = homeDirectory=home=/var/mail/%$,uidNumber=uid,gidNumber=gid,mail=mail
user_filter = (&(cn=%u)(status=valid))

pass_attrs = uid=cn,userPassword=password,\
homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid
pass_filter = (&(cn=%u)(status=valid))

default_pass_scheme = PLAIN
最後に、ssl_sni.conf〔注:実ホスト名と、収容する仮想ドメインのホスト名を設定・記述します。〕
最初の2行は、実ホスト名に対応するサーバ証明書の設定、local_name のブロックは、実ホスト名と収容する仮想ドメインのホスト名を記述します。
ここで、「収容する仮想ドメインのホスト名」は、メールサーバ利用者が設定する「受信メールサーバ名」にそのまま対応します。
ssl_cert = </usr/local/etc/letsencrypt/live/mx.example.com/fullchain.pem
ssl_key = </usr/local/etc/letsencrypt/live/mx.example.com/privkey.pem

local_name mx.example.com {
  ssl_cert = </usr/local/etc/letsencrypt/live/mx.example.com/fullchain.pem
  ssl_key = </usr/local/etc/letsencrypt/live/mx.example.com/privkey.pem
}

local_name mx.example2.net {
  ssl_cert = </usr/local/etc/letsencrypt/live/mx.example2.net/fullchain.pem
  ssl_key = </usr/local/etc/letsencrypt/live/mx.example2.net/privkey.pem
}
以上で、dovecot の設定は終わりですが、環境が整っていないため、まだ稼動させることが出来ません。
なので、次の項目へ進みます。

Pigeonhole のインストール

2022/12/22 に、最新版 0.5.20 がリリースされました。
Pigeonhole(ピゲオンホール?)は、本来は、受信メールの振り分けを行うものですが、ここでは spam判定・コンピュータウィルス判定されたメールの隔離を行う機能を実現するために使用します。
Pigeonhole では、振り分け条件を記述するスクリプト言語に「sieve(シーヴ)」というものを採用しています。投稿日現在、RFC 9042 で規定されており、技術規格的な標準化が試みられています。

標準仕様の sieve には、Courier maildrop にあるような外部プログラムを呼び出して、メッセージフィルタのような機能がありません。しかしながら、Pigeonhole には、Ver 0.2 で、拡張機能という形で該当機能が実装され、Ver 0.3で 「Extprograms Plugin」という名称に変わって機能強化され、Ver 0.4 以降は標準機能として提供されていることを知り、これで、maildrop に代わる手段が sieve スクリプトで実現できるようになります。

Pigeonhole をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
また、先述の dovecot における必要モジュールと重複しているものが多いですが、確認の意味で敢えて掲載しており、再インストールの必要はありません。
 ・perl5-5.36.0 	 (Ports から カテゴリ:lang)
 ・autoconf-2.71	 (Ports から カテゴリ:devel)
 ・libltdl-2.4.7	 (Ports から カテゴリ:devel)
 ・libtool-2.4.7	 (Ports から カテゴリ:devel)
 ・pcre-8.45		 (Ports から カテゴリ:devel)
 ・pcre2-10.40		 (Ports から カテゴリ:devel)
 ・OpenLDAP 2.6.3 以上	 (これはソースコードからの構築を強く推奨)
 ・dovecot 2.3.19.1 以上(前項でソースコードからの構築)
Pigeonhole は、dovecot 本体への付加機能的な形でインストールされていきます。
下記の手順で、コンパイル・構築していきます:
# cp dovecot-2.3-pigeonhole-0.5.19.tar.gz /usr/local/src	 
# cd /usr/local/src	 
# tar xvzf dovecot-2.3-pigeonhole-0.5.19.tar.gz	 
# cd dovecot-2.3-pigeonhole-0.5.19	 
 	 
# setenv CPPFLAGS '-I/usr/local/include -I/usr/include'	(configure が上手くライブラリを探せないため)
# setenv LDFLAGS '-L/usr/local/lib -L/usr/lib'		(configure が上手くライブラリを探せないため)
# setenv LD_LIBRARY_PATH '/usr/local/lib /usr/lib' 	(コンパイルが上手くいかないため)
# ./configure --with-dovecot=/usr/local/lib/dovecot 	(dovecot の構築環境を参照して構築)
# make
# make install
上記は、pigeonhole 0.5.19 の場合で、バージョンが変わると、この部分も変わります。適宜解釈を。

このモジュールは、これでインストール完了です。

certbot を使って、Let's Encrypt のサーバ証明書を活用する

無償利用が可能なサーバ証明書を取得・管理するのに不可欠です。 現行のdovecot と postfix は SSL/TLS のSNI に対応しているのと、いわゆる「オレオレ証明書」を、昨今のアンチウィルスソフトは受け付けなくなってきているので「やむを得ず」といったところです。有効期間が90日と短いのがネックですが、自動更新を確実に行わせることで、このデメリットの補完を試みます。

certbot は、Python で記述されているため、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
Ports や Package でインストールしても、管理上特段問題にはなりません。
既にインストールされている場合、再インストールの必要はありません。
 ・python39-3.9.16 	 (Ports から カテゴリ:lang)
また、このモジュールは、FreeBSD においては、Ports からのインストールでも管理上の問題はありません。
以下の手順で、不足している依存モジュールを自動検出しつつ、インストールを実行します:
# cd /usr/ports/security/py-certbot
# make install
# make clean
# cd
# rehash
インストールしたら、早速、必要なサーバ証明書を取得します。
稼動サーバ上で Apache や nginx などの Webサーバが稼動している場合は、一旦停止します。

また、外部からのアクセスを制限している場合は、それらを一旦解除します。(Let's Encrypt の認証局サーバが非公開のため)
参考までに、当方の開発環境では、Apache を動作させ、加えてサーバ攻撃のためにアクセス制限を、サーバ本体で水際対策で行っているため、それを含めて以下の手順となります:
# /usr/local/etc/rc.d/apache2 stop
# /sbin/pfctl -d
# /usr/local/bin/certbot certonly --standalone --agree-tos -m user@example.com -d mx.example.jp
# /usr/local/bin/certbot certonly --standalone --agree-tos -m user@example.com -d mx.example2.net
−m の後ろに指定するメールアドレスは、有効期限が近づいてきたり、何かあった際の連絡先として登録するメールアドレスを指定します。了承なく他人のメールアドレスにしたり、存在しないメールアドレスを指定したりしないようにしましょう。

サーバ証明書の取得に成功すると、/usr/local/etc/letsencrypt/live 配下にサーバ証明書等が作成されているはずです。

取得が成功したら、環境を元に戻します。
# /sbin/pfctl -e
# /usr/local/etc/rc.d/apache2 start
肝心の自動更新ですが、週に一度実行する形で cron 設定するとよいでしょう。
まずは、定期的に実行する sh スクリプトを下記のように /usr/local/etc/letsencrypt_renew.sh に作成しました:
#!/bin/sh

/usr/local/etc/rc.d/apache2 stop
/sbin/pfctl -d
/usr/local/bin/certbot renew -m user@example.com
/sbin/pfctl -e
/usr/local/sbin/postfix reload
/usr/local/sbin/dovecot reload
/usr/local/etc/rc.d/apache2 start
この sh スプリプトは、cron にて root で実行させ、実行権限を与えるのを忘れないようにしてください。
/usr/local/bin/certbot renew で、有効期限が30日未満の証明書のみを更新します。

この際、証明書を生成した環境で更新をしようとするので、証明書を生成した時と同じ環境に仕立てる必要があります。

2022/12/24(土)メールサーバの中規模改修と基礎知識(2)~ SpamAssassin

SpamAssassin は、定番となっている spam 検出ソフトウェアで、サーバサイドでもクライアントサイドでも使用できる汎用性があります。2022/12/14(アナウンスは 2022/12/17) に Ver 4.0.0 がリリースされました。

具体的には、電子メールヘッダを含めた電子メールメッセージ全体を与え、各種のテストを行い、その結果を spam 度合値として、判定値を提示するような動作になります。

概念図(電子メールメッセージは、① → ② のルートで流れる)

                         ①         ①
spamc《チェック対象の電子メールメッセージ全体》===> socket通信 ===> spamd(SpamAssassin 本体)
   《チェック結果を含む電子メールメッセージ》<=== socket通信 <===
                          ②         ②
上図のように、サーバサイドでは、spamd をデーモン形式で稼働させておき、spamc というクライアントプログラムにて、受信した電子メールメッセージ全体を、spamd に垂れ流し、メッセージの電子メールヘッダ先頭にその判定値が付加されたものが与えたメッセージ全体と共に出力される挙動になります。

spamc から見ると、標準入力(stdin) に電子メールメッセージを流し込み、標準出力(stdout)に結果が出てくるような挙動になります。

SpamAssassinは、下図のように X-Spam-Checker-Version・X-Spam-Level・X-Spam-Status という3つのメールヘッダを処理結果として付加します。
20221224_mailserver_2022enhance.png

このうち、X-Spam-Status というメールヘッダ行が重要で、例示では、「score=-0.6」が spam 度合判定値、「required=4.0」が、spam と見做すしきい値で、この値は別途個別設定出来ます。
電子メール配送の最終段階で、このメールヘッダ部分を参照して、spam メール隔離か通常配送かを決定し、処理する仕組みになります。
「autolearn=ham」というのは、『spam ではないと自動的にパターン学習した』という意味で、spam として見做されると、ここは「autolearn=spam」という表示になります。
「autolearn_force=no」は、SpamAssassin の自動学習機能の如何に関わらず、強制的に「spam である」と学習させる判定値で、この例では「required=4.0」以上の場合に強制的に SpamAssassin に spam メールパターンの学習をさせる挙動になります。

インストール

インストール作業は、必ず root アカウントで行います。
また、SpamAssassin は、Perl 上で動作するため、事前に Perl 5.26 以降が必要です。
FreeBSD,NetBSD 他のUnix系OS、Linux系OSにおいては、Perl をパッケージでインストールしても問題はないです。
お勧めは、現時点(2022/12/24 現在)で最新バージョンの Perl 5.36。

FreeBSD だと、Ports で以下の手順でインストールするのが確実。(依存パッケージが先に自動的にインストールされる)
# cd /usr/ports/lang/perl5.36
# make install
# make clean

また、インストールに2つの方法がありますが、現状では後者の方法(方法その2)しかまともに出来ないみたいです。(当方の環境にて)

○ 方法その1(動作に必要な依存モジュールは自動的に探してインストールされる):
# perl -MCPAN -e shell
cpan> install Mail::SpamAssassin
この方法だと、test 段階で、'spamd が見つからない' とメッセージが出て test自体がなかなか終わらない。
3.4系ではこの問題は出ません。なので、次に示す方法が有効:

○ 方法その2
・まず SpamAssassin の公式ダウンロードページ(https://spamassassin.apache.org/downloads.cgi)から、
 Mail-SpamAssassin-4.0.0.tar.gz または、Mail-SpamAssassin-4.0.0.tar.bz2 をダウンロードし、インストール機器上にアップロードする。
・この方法の場合、CPAN で予め、下記モジュールをインストールしておいた方がよい:
# perl -MCPAN -e shell	 
 cpan> install NetAddr::IP	 
 cpan> install HTML::Parser	 
 cpan> install Digest::SHA1	 
 cpan> install IP::Country	 
 cpan> install IP::Country::DB_File	 
 cpan> install Net::Ident	 
 cpan> install IO::Socket::SSL	 
 cpan> install LWP::UserAgent	 
 cpan> install BSD::Resource	 
 cpan> install Mail::SPF	 
 cpan> install Mail::DKIM	 
 cpan> exit
・次に、下記コマンドを順に実行する。
# cp Mail-SpamAssassin-4.0.0.bz2 /usr/locall/src
# cd /usr/local/src
# tar xvzf Mail-SpamAssassin-4.0.0.bz2
# cd Mail-SpamAssassin-4.0.0
# perl Makefile.PL
# make
# make install 
インストール完了後、方法その1・方法その2の何れであっても、必ず以下のコマンドを実行しておきます。
# rehash
# sa-update --no-gpg
また、運用時は、SpamAssassin 自体が意図しているセキュリティポリシーに合わせるため、ここで専用のユーザを予めvipw や useradd コマンドで作っておきます。
vipw の場合は、編集画面で、下記の行を追加しておきます:
spamd::783:783::0:0:SpamAssassin Daemon:/nonexistent:/usr/sbin/nologin
vipw でユーザ追加した場合は、/etc/group ファイルに下記の行を追加しておきます:
spamd:*:783:
ユーザID、グループIDは、783 にこだわる必要はありませんが、当然のことながら、ユーザID・グループIDが他と重複しないように注意です。
更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd spamd
vipw,passwd,useradd コマンドは、どれも必ずroot ユーザ上で行います。

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

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

○ spamd の起動スクリプト
/usr/local/etc/rc.d ディレクトリ配下に、sa-spamd のファイル名で下記内容を作成します。
1行目から6行目( KEYWORD までの行 ) は、一見するとコメント行そのものですが、意味を持っているため削除しないようにしてください。/usr/local/etc/rc.d ディレクトリ配下のスクリプトは、全てこの挙動になります。

また、実行権限を与えることを忘れないようにしてください:
#!/bin/sh

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

#
# Add the following line to /etc/rc.conf to enable spamd:
#
#   spamd_enable="YES"
#
# You can pass flags to spamd with spamd_flags="..."
# To change the user that spamd runs as, use
#
#    spamd_flags="-u USER [-H /path/to/home... we suggest /var/spool/spamd]"
#
# To keep your user-config in a SQL database, use
#
#        spamd_flags="-Q"
#
# and remove -c (auto-create user preference files).
#

. /etc/rc.subr

name=spamd
rcvar=${name}_enable

extra_commands="reload"
load_rc_config $name

start_precmd="precmd"
restart_precmd="precmd"
stop_cmd="spamd_stop"
pidfile=${spamd_pidfile:-"/var/run/${name}/${name}.pid"}

# Set defaults
: ${spamd_enable:="NO"}
: ${spamd_flags:="-c -u spamd -r ${pidfile} -x --virtual-config-dir=/var/mail/%d/%l/spamassassin/spamd"}

command=/usr/local/bin/${name}
command_args="-d -r ${pidfile}"
command_interpreter="/usr/local/bin/perl"
required_dirs="/usr/local/share/spamassassin"

precmd()
{
        if [ ! -d /var/run/${name} ]; then
                mkdir -p /var/run/${name}
                chown spamd:spamd /var/run/${name}
        fi
}

spamd_stop()
{
  kill -INT `cat /var/run/spamd/spamd.pid`
  echo ' Stop sa-spamd 4.0.0 '
}
run_rc_command "$1"
あとは、簡単な設定作業が残っていますが、メールサーバ構築作業全体の最後のほうで行います。
なので、とりあえずこのフェーズは完了です。

2022/12/23(金)メールサーバの中規模改修と基礎知識(1)

背景や前提条件など

このシリーズは、8回に分けて記事を起こした時点における、今どきのメールサーバ運営管理上のインストール手順や参考情報を掲載します。Portsやバイナリパッケージに頼らず、ソースコードから自力で構築する手順から紹介しています。
『時代錯誤だ!』と嘲笑されようが、これを経験することが基礎知識を維持し、育むにはとても重要なのです。

OSは FreeBSD13 ですが、他のLinux 系OSにおいても参考になる部分が多いと思います。
また、ここでは OpenLDAP のインストールや設定を終えており、LDAP 検索にて、電子メールアドレスまたはSASLユーザ名を検索条件として与えると、
 ・SASL パスワード
 ・UID
 ・GID
 ・homeDirectory
 ・status〔有効にしている(valid)か、無効にしている(invalid)か〕
の情報が得られる仕組みが出来ていることを前提にしています。

実は、約2年前に計画していた改修ですが、
別の企業にて壊れたオンラインシステムの復旧作業・改修作業を事実上一手に引き受けた状態になり、
そちらを優先して、自社システムの細かな不具合がないがしろになっていた状態でした。

当初技術的に先行していた(と自負できる)メールサーバの構成が15年以上経過して色あせてきたことと、
昨今のOutlookやアンチウィルスソフトとの相性が悪くなっているため、中規模改修を行ってこの細かな不具合を出来るだけ解消しようと、やっとのことで実施に移しています。

一口に『メールサーバ』と言っても、知っている人は知っているのですが、構成は複雑です。
サーバ運営・管理的には難易度が高い部類になるかと思います。
20221223_mailserver_2022enhance.jpg


上図に(丁寧に作っている暇がないので)手書きで示した構成図を見ても、実に8種類のサーバサイドソフトウェアを使って1つのメールサーバを実現しています。しかも、これは基本的構成に過ぎません。

実際は、このほかにも動作させるために、Perl,ruby,Python といったスクリプト言語や、rust などの新しい言語のライブラリ、各種の補助的ツールを必要とし、メーリングリストの実現なんかは更に、上図で図示していない別のサーバソフトウェアを必要とします。
また、SSL/TLS といった暗号化通信のためには、サーバ証明書の取得・管理だけではなく、OpenSSL,GnuTLS といった暗号化ライブラリが別途必須になります。

こういう構成図(設計図?)を描ける若手エンジニアを昨今では見ませんね。
「クラウド」「クラウド」と、もてはやすおかげで、自力でこのようなものを構築する機会を奪っているから。
ハッキリ言って、この状況では日本のICT技術水準は、自ら周回遅れの状況を作ってしまっています。

ということで、技術継承的な危機感を感じていますが、憂いでも何も変わらないので、せめて構成例の一例として参考にして欲しいということで、自分メモも兼ねて記録に残しています。(つか、こっちがメインかも)

抑えておきたいポート番号とSSL/TLS の対応

皆さまが使う、電子メール送受信ソフトウェア(Outlook,Thunderbird など)にて、サーバの設定におけるポート番号と暗号化通信形式の組み合わせは、一般的に下記の表のようになります。
時々「メール送受信が上手く出来ない」という問合わせ受けますが、単純ではないものの、下記の対応が滅茶苦茶になっている設定が案外散見されます。参考にどうぞ。
表1 送信側
ポート番号非暗号STARTTLSSSL/TLS説明
25(SMTP)×現在はメールサーバ間の通信に使用。
昨今はOP25 対策で、メール送信元で使えない場合が殆ど。
587(submission)×メール送信元にて、メール発信の際に使う。
OP25環境では、メールサーバ間の通信にも使用。
465(SMTPS)××メール送信元にて、暗号通信を行う際に使用する。
有名どころでは OCN なんかがこれを標準にしているが、
おかげで従来の制御機器が使えなくなるケースも実在する。
表2 受信側
ポート番号非暗号STARTTLSSSL/TLS説明
110(POP3)×メール受信者が、メールサーバから電子メールを取り寄せる時に使用。
143(IMAP4)×POP3 と役割は同じだが、メールサーバ上にメールを一定期間残す挙動が標準。
993(IMAP4S)××IMAP4 を暗号化通信専用にしたようなもの。その他は IMAP4 と同じ。
995(POP3S)××POP3 を暗号化通信専用したようなもの。その他は POP3 と同じ。
有名どころでは OCN なんかがこれを標準にしているが、
おかげで従来の制御機器が使えなくなるケースも実在する。
STARTTLS というのは、一般的に「暗号通信するぞ」という宣言を接続元で行うという手順を踏んで暗号化通信を行う仕組みを指します。
SSL/TLS は、SSL(Secure Sockets Layer) の後継規格が TLS(Transport Layer Security) というもので、SSL は既に旧規格です。ですが、役割は同じなので、同列に語られます。
SSL は当初、Netscape社が開発したものですが、IETF というインターネット技術の国際標準化組織に引き継ぐ際に名称が変わりました。

と、いうことで次からメールサーバ構築の流れを自分メモ的に記していきます。

2022/06/11(土)実験・開発用サーバにSSDを入れてみた

2022/06/11 06:59 サーバ運営・管理
書き込み回数に事実上の制限があるため、サーバ用途にはSSDとか、まともに使えないと思ってはいますが、
プログラム・ライブラリは、読み込みが圧倒的なので、OSとアプリケーションだけをSSDに入れるようにすることで、実用に供するかも? と思い、試験的に採用してみました。

20220611.JPG


「KIOXIA」(キオクシア)というのは、東芝メモリ株式会社の現在の企業名のようです。
つまり、東芝の半導体メモリ部門が社名変更したようなものですね。
東芝の半導体メモリ部門以外の半導体製品群は「東芝デバイス&ストレ―ジ株式会社」となっており、こちらは、従来通り「東芝」を名乗っているようです。

税込み 5,000円未満で入手出来る安物ですが、評判は悪くないので、どの程度の期間使用できるかの確認といった感じで試用中です。

書き込みが頻繁に起きる /var 配下や、ユーザディレクトリ、SWAP をSSDではなく、通常のHDDにパーティションを割り振って、書き込みを最小限に抑えるようにしています。

当然、zfs ファイルシステムは使っていません。ufs なファイルシステムで構成しています。

2022/06/11(土)FreeBSD 13.1R + Perl 5.36.0 の環境で adiaryの更新時エラー

暫定的な場当たり対処なので、とりあえず記事の形で・・・。

提起の通り、FreeBSD 13.1R 上で Perl 5.36.0 の環境で、adiary 3.40c を稼働させて、
おもむろに記事作成や更新を行うと、
[AutoLoader] Can't modify undef operator in scalar assignment at lib/SatsukiApp/adiary_4.pm line 925, near "undef
}"
Compilation failed in require at lib/Satsuki/AutoLoader.pm line 42.
といったエラーが出るようになりました。FreeBSD 13.0R + Perl 5.32 の環境までは発生しない。
OSもPerlも同時に更新したため、
Perl のバージョンアップで挙動が変わったのか、OSで何かあるのかが判らない状態です。

但し、更新そのものは出来ている模様。

これは、lib/SatsukiApp/adiary_4.pm の 913行目~
sub void_plugin_images {
        my $self = shift;
        my $h    = shift;
        my $form = shift;
        foreach(keys(%$form)) {
                if ($_ !~ /^(\w+)_void$/) { next; }
                if (! $form->{$_}) { next; }
                my $n = $1;
                $h->{$n} = undef
                $h->{"${n}_w"} = undef
                $h->{"${n}_h"} = undef
        }
}
と、なっているところを
sub void_plugin_images {
        my $self = shift;
        my $h    = shift;
        my $form = shift;
        foreach(keys(%$form)) {
                if ($_ !~ /^(\w+)_void$/) { next; }
                if (! $form->{$_}) { next; }
                my $n = $1;
                $h->{$n} = undef ;
                $h->{"${n}_w"} = undef ;
                $h->{"${n}_h"} = undef ;
        }
}
とすると、回避出来ました。これが正しいのかどうかは判りません。

2022/03/31(木)postgresql にて、任意の select 文を csv 出力する

自分メモその2。ざっくりと、こんな感じ。
# psql -h サーバFQDN DB名 -U ユーザ名 -c "《任意のselect 文》;" -A -F , > output.csv
サーバFQDN は、ホスト名のほか、IPアドレスでも可能です。自ホストの場合は localhost と指定します。
DB名・ユーザ名は、そのままですね。
任意のselect文は、全体をダブルクォーテーションで括ります。終端には必ずセミコロン「;」を付けます。
select 文中に括弧やダブルクォーテーションが入る場合、エスケープが必要かもしれません。

 -A は、「桁揃えなしのテーブル出力」で、これを指定しないと、CSV 出力の際に余計なスペース等が入ってしまいます。
 -F は、「桁揃えなし出力時のフィールド区切り文字」で、デフォルトの区切り文字は "|" なので、CSV 出力の場合は、必ずカンマ「,」を指定します。

あとは、リダイレクトで出力ファイル名の指定。
文字コードは、DBの文字コードが適用されます。

2022/03/31(木)FreeBSD12/FreeBSD13 にて Let's Encrypt を使う(manual 編)

2022/03/31 02:18 サーバ運営・管理
いつもの自分メモ。地味にいつも嵌るので、、
FreeBSD Ports においては、 securiry/py-certbot をインストールするのは、従来と同じです。

Webサーバ自体が Firewall 内部で、ルータのIPマスカレードや、NATで外部から直接アクセスできない(Firewall の内と外でIPアドレスが違うと Let's Encrypt のツールでは自動取得・自動更新は難しいと思う)ネットワーク構成の場合、--manual オプションで使うのが当方の環境では最も確実です。
但し、アクセス制限をかけたり、リダイレクトしている場合は、それらを全て一旦全て解除し、出来る限りフリーアクセスの状態にしないと、HTTPステータスが 401 になったり、 301 になったりで上手くいかない。

他に、もっと効率的な手法があればいいのですが、、
# certbot certonly --manual --preferred-challenges http-01 -d host.example.com
のように入力すると、
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for host.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

yPcvsXmaN4k_2rk-ZxFKmbp0EwgrmlgLflIPa5EsYtU.xGBBOehwohStkrITKI6h3ng8cYkGYXMtZXQnLK8SHUA

And make it available on your web server at this URL:

http://host.example.com/.well-known/acme-challenge/yPcvsXmaN4k_2rk-ZxFKmbp0EwgrmlgLflIPa5EsYtU

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
のように表示されます。

外部から、
http://host.example.com/.well-known/acme-challenge/yPcvsXmaN4k_2rk-ZxFKmbp0EwgrmlgLflIPa5EsYtU

でアクセス出来るよう、ファイルの中身が
yPcvsXmaN4k_2rk-ZxFKmbp0EwgrmlgLflIPa5EsYtU.xGBBOehwohStkrITKI6h3ng8cYkGYXMtZXQnLK8SHUA

であるコンテンツをサーバ上に設置するように。。 という意味のメッセージの模様:

上手くいくと、
Successfully received certificate.
Certificate is saved at: /usr/local/etc/letsencrypt/live/host.example.com/fullchain.pem
Key is saved at: /usr/local/etc/letsencrypt/live/host.example.com/privkey.pem
This certificate expires on 2022-06-28.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
のようなメッセ―ジが表示されます。
得られた公開鍵・秘密鍵を実際に運用するサーバに適切にセットアップすることで、公開鍵・秘密鍵のアップデート、新規登録が共に可能です。

尚、有効期間は 90日だが、一度同じホスト(FQDN)名で、Let's Encrypt のサーバ証明書を作ると、デフォルトでは、前回の有効期限から90日となる模様。

2021/12/15(水)Apache Log4j 使っていません。

2021/12/15 05:53 サーバ運営・管理
ちまたでは、大騒ぎになっていますが・・・
弊社では、このソフトウェアは使用していません。

問題となった機能を、デフォルトで無効・削除したバージョン 2.16.0 が昨日公開されたようです。

[2021/12/21 追記]
さらに、脆弱性対策漏れがあったとして、バージョン 2.17.0 が 12/17 に公開されたようです。

2021/11/26(金)clamAV は 0.104 から構築方法が変わっている模様・・・

2021/11/26 06:51 サーバ運営・管理
いつものように、ソースコードからの構築を行うべく、
# ./configure --enable-experimental --with-iconv --enable-milter
とか、やって構築しようとしたらエラーを吐くので、ちょっと見て見たら単純に configure スクリプトが無い。
更に調べたら、cmake を使う構築方法に変わったとのこと。

今まで、機会が無くやったことが無いので面食らいました。。。
clamAV 0.104 以降で cmake で構築する場合、先ず、
# cd clamav-0.104.1
# mkdir build
# cd build
# cmake .. -D ENABLE_EXPERIMENTAL=NO -D ENABLE_JSON_SHAREDOFF
とか実行して、まさに configure と同じ役割で構築環境を作り出すのですが、依存するモジュールやライブラリが結構あって、存在しないと、 cmake がエラーを吐いて終了します。最初は、これに面食らいます。

不足があるかもしれませんが、事前に下記の依存モジュールやライブラリが必須のようです:
 ・pcre-8.45   (Ports から カテゴリ:devel)
 ・pcre2-10.39  (Ports から カテゴリ:devel)
 ・autoconf-2.69_3(Ports から カテゴリ:devel)
 ・cmake-3.21.4_1 (Ports から カテゴリ:devel)
 ・libltdl-2.4.6 (Ports から カテゴリ:devel)
 ・libtool-2.4.6_1(Ports から カテゴリ:devel)
 ・libxml2-2.9.12 (Ports から カテゴリ:textproc)
 ・db5-5.3.28_7  (Ports から カテゴリ:databases)  ※ これは BerkeleyDB 5.3.28 です
 ・curl-7.80.0  (Ports から カテゴリ:ftp)
 ・json-c-0.15_1 (Ports から カテゴリ:devel)
 ・jsoncpp-1.9.5 (Ports から カテゴリ:devel)
 ・check-0.15.2  (Ports から カテゴリ:devel)
 ・ncurses-6.3  (Ports から カテゴリ:devel)
上手くいったら、
# cmake --build .
# cmake --build . --target install
とやると、ソースコードからインストールできるようです。
(ドットを省略しないことに注意を)

2021/08/28(土)FreeBSD13 からのソースコードによるOS更新は、gitup が最も手軽

2021/08/28 05:00 サーバ運営・管理
先日、FreeBSD のソースコード管理が subversion から git に変更になった旨を『厄介だ』と紹介したのですが・・・
その厄介さを払拭するメンテナンスツールが既に用意されていたのでした。

この手の情報が数えるほどしかなく、日本語による情報を当方が見つけたのはここだけです → KNCN weblog /usr/src を gitup で取得する
#見落としの可能性もあるのでご容赦を・・

これは、OSの標準コマンドではないので、出来る限り最新のports から、
# cd /usr/ports/net/gitup
# make install
# make clean
# rehash
とかやって、インストールします。インストール後、 /usr/local/etc/gitup.conf を以下のように適宜編集します:
(一部抜粋)
# $FreeBSD$
#
# Default configuration options for gitup.conf.
{
        "defaults" : {
                "host"           : "git.freebsd.org",
                "port"           : 443,
#               "proxy_host"     : "",
#               "proxy_port"     : 0,
#               "proxy_username" : "",
#               "proxy_password" : "",
#               "source_address" : "",
                "low_memory"     : false,
                "display_depth"  : 0,
                "verbosity"      : 1,
                "work_directory" : "/var/db/gitup",
        },

        "ports" : {
                "repository_path"  : "/ports.git",
                "branch"           : "main",
                "target_directory" : "/usr/ports",
                "ignores"          : [
                        "distfiles",
                        "packages",
                ],
        },

        "release" : {
                "repository_path"  : "/src.git",
                "branch"           : "releng/13.0",
                "target_directory" : "/usr/src",
                "ignores"          : [
                        "sys/amd64/conf",
                        "sys/arm64/conf",
                        "sys/i386/conf",
                        "sys/pc98/conf",
                        "sys/powerpc/conf",
                        "sys/riscv/conf",
                        "sys/sparc64/conf",
                ]
        },

        "stable" : {
                "repository_path"  : "/src.git",
                "branch"           : "stable/13",
                "target_directory" : "/usr/src",
                "ignores"          : [
                        "sys/amd64/conf",
                        "sys/arm64/conf",
                        "sys/i386/conf",
                        "sys/pc98/conf",
                        "sys/powerpc/conf",
                        "sys/riscv/conf",
                        "sys/sparc64/conf",
                ]
        },
見ての通り、JSON 形式な設定ファイル。
初めてだと JSON 形式に面食らうんですが、割と直感的に変更できます。
gitup.conf を編集後、
# gitup release
とやれば、今まで同様の /usr/src 配下へのソースコード取得、
今後は、ports ツリーもgit 管理になるらしいので、そうなった際は、
# gitup ports
で、従来のように /usr/ports 配下への取得が出来るようです。
ただ、ports ツリーの取得は、前処理・後処理があるので、portsnap のほうが使い勝手は良いです。
〔参考〕portsnap が更新しない場合の対処法

尚、新規取得時も更新時も同じコマンドラインで出来ます。