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/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/02/10(木)Windows 11 にしてみた

一度、インストール失敗したこともあり、半日かかりました。
アップデートの際は、必ずアンチウィルスの動作を無効にしましょう。これがアップデートのときに問題を引き起こします。(下図参照)
20220210.png

これは、当方で有償ライセンスで使用している Avast! の例です。
ハッキリ言って表現が悪いのですが「永久に」というよりは、「再度有効にするまで無効にする」という意味です。

結論から言うと、「今、慌ててやる必要はない」です。
20220210.jpg


GUI インタフェースはハッキリ言うと「改悪」そのものと言って良い。

改悪の極みはエクスプローラで、
Windows 10 のエクスプローラーだと、右クリックで選択出来たものが、
Windows 11 のエクスプローラでは、「その他の機能」に殆どが移行し、例えば「名前の変更」なんかは、
右クリック1回、「その他の機能」の左クリック1回、ここで始めて「名前の変更」の左クリック。
で、合計3回させられます。

タスクトレイに日時が表示されるのは変わらないですが、勘違いなセキュリティ対策なのか、
タスクトレイの改造が外部ソフトウェアで出来なくなった上に、秒の表示が出来ません。
更に、文字の大きさが小さい。
こうするなら、タスクトレイ改造相当のカスタマイズ機能を標準でサポートするべきです。

唯一の改善点(?)は、同じディスプレイ上で明確に仮想画面を持てるようになったことくらいでしょうか。
「デスクトップ1」「デスクトップ2」というのがそれ。追加できるみたいです。

これ見て思ったのは、「MacOS のパクリ?」というところ。
でも、これは便利です。
まぁ BSD系Unix とかでは昔から普通にあった機能ですけどね。CUI環境でも8画面程度の仮想画面を持っています。

2020/08/22(土)javascript の fetch と XMLHttpRequest

今回は、これにちょっと嵌ったので自分メモ。
fetch も XMLHttpRequest も Javascript 内でWebサーバと通信処理が出来る便利な機能で
ちょっと前は「Ajax」とか言ってもてはやされたものですが、今はほぼ死語でしょうかね。
それでも今はWebサイトでちょっとしたことをするには欠かせなくなってきているもの。

今回は、オンラインでの画像編集機能をサイト内にて実現するために、
最初は fetch による以下のコードを書きました(一部を公開用に変更部分あり):
async function gosub_canvasimg(serial,imgblob) {
  var responce = await (await fetch('/editor.cgi', {
                          method : 'POST',
                          cache  : 'no-cache',
                          headers : {'Content-Type' : 'application/x-www-form-urlencoded' } ,
                          body   : 'imgblob=' + imgblob ;
                         })).text() ;
  if (responce != 'valid') {
    alert(serial + "つめの画像ファイル保存に失敗しました。" );
  }
}
ところが、これだと、
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.
というエラーが出て、fetch 自体が実行されないのです。*1
これは、拙作のシステムにて「画像があるはずだが、編集画面で表示されない」という現象を作り出す直接原因になっていました。

同期処理でないと、編集時に不具合が更に出るので、こうしています。
『同期処理なんぞ殆ど使われません』と大声で叫ぶサイトなんかも散見されますが、
それはよほど精通している方か、同期処理の必要な案件に出会ったことが無い方なのでしょう。
少なくともハードウェアの制御がシステム全体として絡んで来ると、同期処理はどうやっても必須。

結局、fetch がエラーになる原因は判らず仕舞いで、fetch のところを同じ処理になるように、
async function gosub_canvasimg(serial,imgblob) {
  var imgxhr  = new XMLHttpRequest() ;
  var msgbody = 'imgblob=' + imgblob ;

  imgxhr.open('POST', '/editor.cgi', false) ;
  imgxhr.send(msgbody) ;
  var responce = imgxhr.response ;

  if (responce != 'valid') {
    alert(serial + "つめの画像ファイル保存に失敗しました。" );
  }
}
みたいな感じにすると、エラーは出なくなった模様。
これで不具合は出なくなったと思うが様子見です。
fetch の実装バグなのか、「同期方式」という使い方が悪いのか調べても全く判らないのです。

やっぱり XMLHttpRequest() のほうが断然判りやすい。
fetch は Promise という挙動の概念を理解しなければならず、これが当方にはあまり直感的ではなく判りずらさの極みです。これを使いこなせないのがこの顛末の一因でしょう。

何せ、類似の事例が殆ど表に出てきていない案件なので、エンドユーザに動作検証してもらうという暴挙を敢行するしかないのです。

*1 : 当方は英語が苦手なので、日本語表示がある程度出来る Firefox でデバッグをしています. chrome のデバッガは、英語だらけで正直発狂しそうになります...

2020/03/17(火)中華なオシロスコープを使いこなす(2)

さすがに最近のオシロスコープは、画像保存が出来るようになっている模様。
先日の記事と同じような画になってしまったのですが。。
20200317.png


ブラウン管オシロスコープな時代は、専用の道具を使いつつ、写真撮影していたものです。
USB端子が付いており、そこにUSBメモリを挿すと、オシロスコープ側で保存した画像をコピー&ペーストで取得できます。

ただ、ここからが中華らしい曲者で、、、orz
USBメモリにコピーしたはいいが、PC側で読み込みしようとすると、
「このドライブで問題が見つかりました。今すぐドライブをスキャンして修復してください。」
みたいなメッセージが出る。しかし、スキャンすると・・・「問題は見つかりませんでした。」
なんだこれ、、、更に、右端側のメニューが消えない。邪魔だ、、、

右端側のメニューがない状態で記録出来ないものか。。
ご存知のかた誰か教えてください・・。orz

2020/03/11(水)中華なオシロスコープを使いこなす(1)

最近のオシロスコープは、色々な機能が付随しています。
20200311.JPG


これは、プロトコルアナライザ。
今も昔も広く使われている RS-232C 信号線で、どういうデータが流れているかを解析した様子。
他にSPI だの I2C だのといったものが使える模様。見慣れないものもあります。

データを取得するときのトリガの掛け方(早い話、データを取得するタイミングをどう設定するか)が今ひとつ慣れていないが、こんなことができるという確認。
間違った解析をしていないのを確認。

あと、リアルタイムで電圧や周波数(周期)なんかも表示できます。
更に、ヒストグラムとFFT機能があるのですが、この2つは使う機会が出てくるのかどうか何とも・・です。
ただ、試作中の技術的評価には使えるかな、といったところ。

2020/03/09(月)中華製なオシロスコープを入手した

永年欲しくても買えずにいたオシロスコープを、やっとの思いで購入。
絶対額が安くはないんですが、業務を全うするのに必要なのです。(この機種でもどちらかといえば安いほう)
これ無いと、電子回路の設計・開発に大きな支障あるんですが、今までこれ無しでやってきたのです。
オシロスコープ使わずに電子回路の設計・開発が出来たのは奇跡以外の何物でもないのです。(本当に、、、)
当然のことながら、受注業務の技術的限界を感じていました。

でもこれ、中華メーカなんだよね。。ただ、評判はそれほど悪くはないようなので・・・
20200309_1.jpg

20200309_2.jpg


設置場所確保するだけで徹夜になってしまいました。orz
Amazon で購入したんですが、中華なところから成田空港経由の空輸で製造元にて二重梱包されていました。
梱包材破損のクレームが過去にあったからですかね。。

これからセットアップです。

2020/03/08(日)XML ファイルを解析する perl モジュール

現在の作業が落ち着いたところで、
2年近くずーっと延期していたWeb気象通報サイトの抜本的改築を行おうと考えています。

元データはXML形式のファイルで、技術的にこれを逐次解析処理する必要があります。
そこで作業準備にあたり、どのXMLライブラリを主力にするかの検討からなのです。

現状、Perl5 では下記のXMLライブラリがあり、依存する汎用ライブラリが異なります:

XML::Simple libexpat が必要
XML::Parser libexpat が必要
XML::DOM   libexpat が必要
XML::LibXML libxml2 が必要
XML::Feed  libxml2 と libexpat の両方必要
#他にもあるが、最終更新日時が古い・事例が殆ど無いなどの理由で最初から却下。

libexpat も libxml2 も XMLを処理するC言語ベースのライブラリで、
Perlで実現するXML処理は、単にこれらのライブラリとの仲介をしているだけに過ぎないです。

ですが、その処理手法が異なっており、libexpat を使うモジュールは、libexpat そのものの構造からして

・巨大なXMLをパース(解析)できない
・複雑なデータ構造のXMLをパースできない

という問題があるようで、これは今回のプロダクションには採用できない(しないほうが無難)模様。
また、XML::Feed は、複雑なデータ構造のXMLパースは機能しないという報告もあり、
昔から標準的で実績豊富とされている XML::LibXML というところに落ち着く形ですね。

ただ、ちょっと使いにくそうです。