sendmail 8.12.9, m4, qpopper 4.0.3, poprelayd でバーチャルメール
〜 送受信共にバーチャル 〜
(Solaris にも対応)
2001-09-22 作成 福島
2001-09-27 更新 福島
2001-10-01 更新 福島
2001-10-21 更新 福島
2002-03-14 更新 福島
2003-03-11 更新 福島
2003-04-09 更新 福島
2003-04-19 更新 福島
2003-07-09 更新 福島
2003-09-02 更新 福島
2003-09-14 更新 福島
2003-09-26 更新 福島
2006-05-17 更新 福島
いや〜。今回はホンット苦労しました。
調査開始から 2 週間ぐらいかかりました。

8.12.9 使用上 (仕様上 ?) の注意

root に suid しなくなった (起動時はポート 25 を bind するため、root です) ために、以下の条件が違います。

・root を使わずにユーザ smmsp とグループ smmsp を使う。
 こうすることで、セキュリティレベルが高くなった。

・送受信共に、sendmail が常駐している必要がある。
 LDA (Local Delivery Agent: ローカルメーラ) に送受信を任せるので、受信が不要でも常駐しなければなりません。
 常駐せずに送信したい場合は 8.11 系 (8.11.7 が最終:2003-03-30) を使いましょう。
 送信だけなら 8.11 系でも安心して使えます。

・その他

 access.db の書式に注意

  access.db の書式が FEATURE(`access_db', `hash -T<TMPF> -o /etc/mail/access') に変更になっています。
  従来の FEATURE(`access_db', `hash -o /etc/mail/access') では動作できません。

 フォーマットに注意

  cf のフォーマットは V10/Berkeley です。
  8.12 系で初めて V10 が採用されたわけではありませんが、8.12 系に付属の cf は全て V10 になっています。
  以前のバージョンの cf で sendmail を起動 (sendmail -bt -C sendmail.cf) をすると、
   Warning: .cf file is out of date: sendmail 8.12.x supports version 10, .cf file is version n
   x は sendmail のバージョン n は cf のバージョン
  という警告が出ます。


・ユーザ smmsp の作成 (メール送信の際、8.12.x からはこのユーザを使う) $ su # groupadd smmsp # useradd -g smmsp smmsp Solaris の場合はこちらもやる # mkdir /export/home/smmsp # chown smmsp:smmsp /export/home/smmsp # usermod -d /export/home/smmsp smmsp
・cc の準備 (Linux は不要) DB_File (Perl モジュール) のインストールで cc が無いといってうるさいのでリンクを張ってしまう (Solaris のみ) Linux は既に張ってあるので必要無し。 $ su # cd /usr/bin/ # ln -s ../local/bin/gcc cc
・NEWDB (Berkelay DB) のインストール (Solaris の場合は入っていない) http://www.sleepycat.com/update/3.3.11/db-3.3.11.tar.gz をダウンロード $ tar xzf db-3.3.11.tar.gz $ cd db-3.3.11 $ cd build_unix $ ../dist/configure $ make $ su # make install /usr/local/BerkeleyDB.3.3/ にインストールされる リビルドは $ make clean $ make あるいは $ make realclean $ ../dist/configure $ make と書いてある。 Solaris の場合、Linux の様に /etc/ld.so.conf が無いのでリンクを張る $ su # cd /usr/lib/ # ln -s ../local/BerkeleyDB.3.3/lib/libdb-3.3.so libdb-3.3.so 最低限、これだけでも張る # ln -s ../local/BerkeleyDB.3.3/lib/libdb-3.so libdb-3.so # ln -s ../local/BerkeleyDB.3.3/lib/libdb.so libdb.so # ln -s ../local/BerkeleyDB.3.3/lib/libdb-3.3.a libdb-3.3.a # ln -s ../local/BerkeleyDB.3.3/lib/libdb-3.3.la libdb-3.3.la
・sendmail のインストール $ ncftpget sendmail.8.12.9.tar.sig $ tar xzf sendmail.8.12.9.tar.gz $ cd sendmail-8.12.9 devtools/OS/SunOS.5.8 (Solaris8 の場合: Linux は不要)
define(`confMAPDEF', `-DNEWDB -DNDBM -DNIS -DNISPLUS -DMAP_REGEX')  -DNEWDB スイッチを追加
define(`confINCDIRS', `-I/usr/local/BerkeleyDB.3.3/include')   1行追加
define(`confLIBDIRS', `-L/usr/local/BerkeleyDB.3.3/lib')   1行追加
$ ./Build $ su Linux の場合はディレクトリを作成 # mkdir /usr/man/man1 # mkdir /usr/man/man5 # mkdir /usr/man/man8 # ./Build install /var/spool/clientmqueue が smmsp になっていなかったら変更する # chown smmsp:smmsp /var/spool/clientmqueue # chmod g+w /var/spool/clientmqueue # mkdir /var/spool/mqueue # chown root:mail /var/spool/mqueue # chmod 0755 /var/spool/mqueue
・sendmail.cf の作成 $ cd sendmail-8.12.9/cf/cf/ 必ずこのディレクトリに移動すること $ cp generic-linux.mc mydomain.mc … Linux の場合 $ cp generic-solaris.mc mydomain.mc … Solaris の場合 mydomain.mc (変更点を太字にしてあります)
include(`../m4/cf.m4')dnl    ※1 コマンドラインで指定するのが面倒なのでここで指定する
divert(-1)
divert(0)dnl
VERSIONID(`$Id: generic-linux.mc,v 8.1 1999/09/24 22:48:05 gshapiro Exp $')
OSTYPE(linux)dnl
DOMAIN(generic)dnl

RELAY_DOMAIN_FILE(`/etc/mail/relay-domains')     スタティックなリレー許可ホストファイル (デフォルトなので省略可)
FEATURE(`always_add_domain')     ローカルユーザがドメイン名を省略してもドメイン名を付加する
FEATURE(`virtusertable', `hash -o /etc/mail/virtusertable')  受信振り分け指定
FEATURE(`access_db', `hash -T<TMPF> /etc/mail/access')   接続許可/拒否ホスト指定 ※2 (dbm でも可)
FEATURE(`dnsbl', `relays.ordb.org', `"550 Email rejected due to sending server misconfiguration - see "')dnl    ORDB を利用しないなら不要

include(`poprelay.mc')dnl    ※4 … poprelayd を使用しないなら不要
FEATURE(`greet_pause',`5000')dnl    8.13.1 からは access.db に GreetPause: が使用可能です

MAILER(local)dnl
MAILER(smtp)dnl

# DNS の逆引きがおかしいサイトを拒否する設定
# RFORGED はマルチドメインのメールを拒否してしまうので、コメントアウトのほうが吉
LOCAL_RULESETS
SLocal_check_relay
R$*	$: $&{client_resolve}
RTEMP	$#error $@ 4.7.1 $: "450 Access denied. Cannot resolve PTR record for " $&{client_addr} 管理する DNS が存在しないホスト (SERVFAIL) を拒否
#RFORGED	$#error $@ 4.7.1 $: "450 Access denied. IP name possibly forged " $&{client_name} 逆引きと正引きの名前が合致しないホストを拒否
RFAIL	$#error $@ 4.7.1 $: "450 Access denied. IP name lookup failed " $&{client_name} 逆引きできないホスト (NXDOMAIN) を拒否
RELAY_DOMAIN_FILE(), FEATURE(), include() は必ず MAILER() より前に記述すること。 ※2 -T<TMPF> というのは、抽象表現ではありません。このままを記述します。 ここでは hash を使用していますが、dbm にすることも出来ます。(第 2 パラメータ全体を省略すると hash になる) poprelayd を使用しないなら dbm でも可。 poprelay.mc の用意 (※4 を指定しない場合は不要) poprelay.mc を修正する
# We probably want the access_db feature enabled.
#FEATURE(`access_db')dnl   上記で既に指定してあるので削除する

# List of IP addresses we allow relaying from.
Klocalip hash -a /etc/mail/localip dbm でも可
Kpopip hash -a /etc/mail/popip     dbm は不可 (poprelayd が hash を対象にするため)


LOCAL_RULESETS


SLocal_check_rcpt
# Put the address into cannonical form (even if it doesn't resolve to an MX).
R$*                     $: $>Parse0 $>3 $1
R$* < $* > $*           $: $1 < $2 . > $3                       Pretend it's canonical.
R$* < $* . . > $*       $1 < $2 . > $3                          Remove extra dots.

# Allow relaying if the connected host is a local IP address.
R$*                     $: < $&{client_addr} >                  Get client IP address.
R<>                     $#OK                                    Local is ok.
R< $* . $- > $*         $(localip $1.$2 $: < $1 > . $2 $)       Check last three octets.
R$* < MATCH >           $#OK
R< $- > $*              $: $(localip $1 $: < > $1 $2 $)         Check first octet.
R$* < MATCH >           $#OK

# Allow relaying if the connected host has recently POP3 authenticated.
R$*                     $: < $&{client_addr} >                  Get client IP address.
R< $* >                 $(popip $1 $)                           Check full address.
R$* < MATCH >           $#OK

# IP address didn't match.
$ m4 mydomain.mc > sendmail.cf $ m4 ../m4/cf.m4 mydomain.mc > sendmail.cf ※1 を記述しなかった場合はこちら $ su # cp ./sendmail.cf /etc/mail/. # chmod g-w /etc/mail/sendmail.cf
# touch /etc/mail/aliases エイリアスファイルを作成
/etc/mail/local-host-names (plain) バーチャルを名乗るドメイン (ホスト名) を列記する 無いとダメ (実行時に読みこむので変更したときは sendmail を再起動する)
example1.com
example2.com

/etc/mail/virtusertable (db)
who@example1.com who@overthere.com  --- who@example1.com として届いたメールを who@overthere.com に送る
                                             (.forward と同じ機能)
@example1.com example                 --- ***@example.com に届いたメールをローカルユーザ example に蓄積する
@example2.com 550 error-message        --- エラーステータスを返す
# makemap hash /etc/mail/virtusertable < /etc/mail/virtusertable (dbm でも可)
/etc/mail/relay-domains 空っぽでも可 (実行時に読みこむので変更したときは sendmail を再起動する)
other.com         --- ***.other.com へのリレーを許可する
192.168.10.10     --- 192.168.10.10 へのリレーを許可する

/etc/mail/access (db) 送受信の度に参照される ※2 を指定したときに必要 (空っぽでも可)
Connect:relay.com	RELAY   --- ***.relay.com からのリレーを許可する
Connect:other.com	OK      --- ***.other.com からの受け取りを許可する
Connect:192.168	REJECT  --- 192.168.*.* からの接続を拒否する
To:true.com	OK      --- @true.com への受け取りを許可する
From:false@junk.com	REJECT  --- false@junk.com からの受け取りを拒否する (詐称は見抜けない)

reject.com		REJECT  --- ***.reject.com からの接続を拒否する (デフォルトが Connect: になる)
# makemap hash /etc/mail/access < /etc/mail/access dbm でも可
/etc/mail/localip (db) 送受信の度に参照される (空っぽでも可)
other.com         --- ***.other.com からのリレーを許可する
192.168           --- 192.168.*.* からのリレーを許可する
# makemap hash /etc/mail/localip < /etc/mail/localip dbm でも可
/etc/mail/popip (db) poprelayd がセット/リセットするので、空っぽで用意する poprelayd を使わない場合は不要
   (空)   
# makemap hash /etc/mail/popip < /dev/null hash のみ (これが BerkeleyDB が必要な理由)
・起動スクリプト (RedHat の場合) # vi /etc/sysconfig/sendmail
DAEMON=yes
QUEUE=1h
# vi /etc/rc.d/init.d/sendmail
#!/bin/sh

# chkconfig: 2345 80 30
# description: Sendmail
# processname: sendmail

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Source sendmail configureation.
if [ -f /etc/sysconfig/sendmail ] ; then
	. /etc/sysconfig/sendmail
else
	DAEMON=no
	QUEUE=1h
fi

# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0

[ -f /usr/sbin/sendmail ] || exit 0

RETVAL=0
prog="sendmail"

start() {
	# Start daemons.

	echo -n $"Starting $prog: "
	/usr/bin/newaliases > /dev/null 2>&1
	if test -x /usr/bin/make -a -f /etc/mail/Makefile ; then
	  make -C /etc/mail -s
	else
	  for i in virtusertable access domaintable mailertable ; do
	    if [ -f /etc/mail/$i ] ; then
	       makemap hash /etc/mail/$i < /etc/mail/$i
	    fi
	  done
	fi
	daemon /usr/sbin/sendmail $([ "$DAEMON" = yes ] && echo -bd) \
	                          $([ -n "$QUEUE" ] && echo -q$QUEUE)
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/sendmail
	return $RETVAL
}

stop() {
	# Stop daemons.
	echo -n $"Shutting down $prog: "
	killproc sendmail
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sendmail
	return $RETVAL
}

# See how we were called.
case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	restart|reload)
		stop
		start
		RETVAL=$?
		;;
	condrestart)
		if [ -f /var/lock/subsys/sendmail ]; then
			stop
			start
			RETVAL=$?
		fi
		;;
	status)
		status sendmail
		RETVAL=$?
		;;
	*)
		echo $"Usage: $0 {start|stop|restart|condrestart|status}"
		exit 1
esac

exit $RETVAL
# chmod ug+x /etc/rc.d/init.d/sendmail # chkconfig --add sendmail ・起動スクリプト (Solaris の場合) # vi /etc/init.d/sendmail
#!/sbin/sh

ERRMSG1='WARNING: /var/mail is NFS-mounted without setting actimeo=0,'
ERRMSG2='this can cause mailbox locking and access problems.'

case "$1" in
'start')
        if [ -f /usr/lib/sendmail -a -f /etc/mail/sendmail.cf ]; then
                if [ ! -d /var/spool/mqueue ]; then
                        /usr/bin/mkdir -m 0750 /var/spool/mqueue
                        /usr/bin/chown root:bin /var/spool/mqueue
                fi
                MODE="-bd"
                if [ -f /etc/default/sendmail ]; then
                        . /etc/default/sendmail
                fi
                if [ -z "$QUEUEINTERVAL" ]; then
                        QUEUEINTERVAL="15m"
                fi
                case $QUEUEINTERVAL in
                        *s | *m | *h | *d | *w) ;;
                        *)  QUEUEINTERVAL="15m" ;;
                esac
                if [ $QUEUEINTERVAL -le 0 ]; then
                        QUEUEINTERVAL="15m"
                fi
                /usr/lib/sendmail $MODE -q$QUEUEINTERVAL $OPTIONS &
        fi

        if /usr/bin/nawk 'BEGIN{s = 1}
            $2 == "/var/mail" && $3 == "nfs" && $4 !~ /actimeo=0/ &&
            $4 !~ /noac/{s = 0} END{exit s}' /etc/mnttab; then

                /usr/bin/logger -p mail.crit "$ERRMSG1"
                /usr/bin/logger -p mail.crit "$ERRMSG2"
        fi
        ;;

'stop')
        /usr/bin/pkill -x -u 0 sendmail
        ;;

*)
        echo "Usage: $0 { start | stop }"
        exit 1
        ;;
esac
exit 0
# chmod ug+x /etc/init.d/sendmail # cd /etc/rc0.d # ln -s ../init.d/sendmail K36sendmail # cd /etc/rc1.d # ln -s ../init.d/sendmail K36sendmail # cd /etc/rcS.d # ln -s ../init.d/sendmail K36sendmail # cd /etc/rc2.d # ln -s ../init.d/sendmail S88sendmail
・sendmail の再起動 # /etc/rc.d/init.d/sendmail stop Linux の場合 # /etc/rc.d/init.d/sendmail start # /etc/init.d/sendmail stop Solaris の場合 # /etc/init.d/sendmail start
・qpopper のインストール $ tar xzf qpopper4.0.3.tar.gz $ cd qpopper4.0.3 $ ./configure --enable-log-login ユーザのログイン状況を出力する $ make $ su # cp popper/popper /usr/sbin/. # cd /usr/sbin/ # ln -s popper in.pop3d tcp-wrapper の場合 /etc/inetd.conf
pop-3 stream tcp nowait root /usr/sbin/tcpd in.pop3d -t /var/log/popper.log
※3 Solaris の場合は pop-3 を pop3 と記述する (/etc/services に合わせる) # /etc/rc.d/init.d/inet restart Linux の場合 # ps -eaf | grep inetd Solaris の場合 root 100 1 0 Sep 12 ? 0:00 /usr/sbin/inetd -s # kill -HUP 100 xinetd の場合 /etc/xinetd.d/pop3
service pop3
{
	socket_type     = stream
	wait            = no
	user            = root
	server          = /usr/sbin/in.pop3d
	server_args     = -t /var/log/popper.log
	log_on_failure += USERID
	disable         = no
}
# /etc/rc.d/init.d/xinetd restart
・DB_File.pm のインストール (Linux は入っているので不要) poprelayd を使用しないなら不要。 $ tar xzf DB_File-1.78.tar.gz $ perl Makefile.PL $ make $ make test $ su # make install
・poprelayd のインストール 上記 (qpopper) の ./configure --enable-log-login によって、/var/log/popper.log には
Sep 26 00:19:44.817 2001 [12892] (v4.0.3) POP login by user "who" at (client.example.com) 192.168.10.10
ユーザ who がホスト 192.168.10.10 から接続して認証されたという意味
というログが出力されるようになった。 正しくは、-t /var/log/popper.log の指定による。 しかし、poprelayd はそれにまだ対応していないのでパッチ (手作業) を加える。
#!/usr/local/bin/perl    … perl のパスに合わせる
#!/usr/local/bin/perl5#$logfile = "/var/log/maillog";   #POP3 daemon log.
$logfile = "/var/log/popper.log";   #POP3 daemon log.

…

# You may need to uncomment this if your fcntl.ph doesn't export it.
#sub O_EXLOCK { 0x20 };
sub O_EXLOCK { 0x20 };                Solaris の場合はこれを有効にする

…

sub scanaddr ($) {
    my $s = $_[0];
    my @paddrs;         # Packed IP addresses.
    my @addrs;          # ASCII addresses.
    my $junk;

#    if ($s =~ m/i(pop2|pop3|map)d\[[0-9]+\]: Login user=/)  {
#       $s =~ s/.*host=(\S+).*/$1/;
     if ($s =~ m/POP login by user /) {
        $s =~ s/.* at \((\S+)\).*/$1/;
        ($junk, $junk, $junk, $junk, @paddrs) = gethostbyname($s);
        while (@paddrs)  {
            push(@addrs, join('.', unpack('C4', shift(@paddrs))));
        }
        return @addrs;
    }
    return ();
}

…
ここまでで、 $ perl poprelayd -p -p は表示を指示するスイッチ を実行してエラーが表示されなければとりあえず OK 。 # /usr/bin/install poprelayd /usr/sbin/ … Linux # /usr/ucb/install poprelayd /usr/sbin/ … Solaris (/usr/sbin/install は使わない) # touch /var/log/popper.log # chown root:root /var/log/popper.log # chmod 660 /var/log/popper.log # /usr/sbin/poprelayd -d -d はデーモン動作するためのスイッチ
・poprelayd の確認 他サーバから telnet して確認 $ telnet popserv1.example.com 110 Trying 192.168.10.10... Connected to popserv1.example.com. Escape character is '^]'. +OK Qpopper (version 4.0.3) at popserv1 starting. user UserID +OK Password required for UserID. pass Password +OK UserID has 1 visible message (0 hidden) in 874 octets. quit +OK Pop server at popserv1 signing off. Connection closed by foreign host. $ /usr/sbin/poprelayd -p 192.168.10.11 10 クライアントの IP アドレスと経過秒数が表示される (poprelayd の監視インターバルが 5 秒なのですぐに反映されないことがあります)
・poprelayd を init に登録 /etc/rc.d/init.d/poprelayd … Linux の場合 /etc/init.d/poprelayd … Solaris の場合
#!/bin/sh
# chkconfig: 2345 55 25
# description: POP before SMTP relay daemon
# processname: poprelayd

[ -x /usr/sbin/poprelayd ] || exit ;
[ -d /var/run ] || exit ;

case "$1" in
  start)
    /usr/sbin/poprelayd -d
    ;;
  stop)
    kill `cat /var/run/poprelayd.pid`
    ;;
  *)
    echo "Usage: $0 {start|stop}"
    ;;
esac
# /sbin/chkconfig --add poprelayd Linux の場合 # cd /etc/rc2.d/ Solaris の場合 # ln -s ../init.d/poprelayd S88poprelayd
/usr/sbin/poprelayd -a 210.164.52.3 /etc/mail/popip.db に直接 IP を入れる方法
手動で SMTP を確認
$ telnet mail.example.com 25
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Sendmail 8.12.9/8.12.9; Tue, 11 Mar 2003 11:50:18 +0900
helo mail.mydomain.com
250 mail.example.com Hello mail.mydomain.com [192.168.10.10], pleased to meet you
mail from:<myname@mail.mydomain.com>
250 2.1.0 <myname@mail.mydomain.com>... Sender ok
rcpt to:<yourname@mail.example.com>
250 2.1.5 <yourname@mail.example.com>... Recipient ok (will queue)
data
354 Enter mail, end with "." on line by itself
dear yourname.
this is a test mail.
.
250 2.0.0 h2B2l6qx031243 Message accepted for delivery
quit
221 2.0.0 mail.example.com closing connection
Connection closed by foreign host.

リレーされることと、されないことを確認すること。 こちらのサイトが有名です http://www.nanet.co.jp/rlytest/relaytest.html チェック用スクリプトを書いてみました
relaychecker.pl
#!/usr/local/bin/perl

use strict ;
use Socket ;

if ($ARGV[0] eq '')
	{
	print "Usage: $0 <ServerName>\n" ;
	exit ;
	}

my $targ_host = $ARGV[0] ;
my $my_host   = 'mail.example.com' ;
my $mail_from = 'checker@example.com' ;
my $mail_to   = 'somewhere@example.com' ;

my @command = (
	"helo $my_host",
	"mail from:<$mail_from>",
	"rcpt to:<$mail_to>",
	) ;

my $port = getservbyname('smtp','tcp') ;

local *SOCK_SMTP ;
socket(SOCK_SMTP, PF_INET, SOCK_STREAM, 0) ;

my $target = sockaddr_in($port, inet_aton($targ_host)) ;
if (! connect SOCK_SMTP, $target)
	{
	print "$targ_host can't connect.\n" ;
	exit ;
	}

select SOCK_SMTP ; $| = 1 ; select STDOUT ;

my $res = <SOCK_SMTP> ;
print "$targ_host > $res" ;
if ($res !~ /^2/)
	{
	print "server $targ_host response undefined.\n" ;
	}
else
	{
	my $relay = 'Allow' ;
	my $cmd ;
	for ( my $i = 0 ; $i < @command ; $i ++ )
		{
		$cmd = $command[$i] ;
		print "$targ_host < $cmd\n" ;
		print SOCK_SMTP "$cmd\n" ;
		my $res = <SOCK_SMTP> ;
		print "$targ_host > $res" ;
		if ($res !~ /^2/)
			{
			$relay = 'Deny' ;
			last ;
			}
		}
	$cmd = "quit" ;
	print "$targ_host < $cmd\n" ;
	print SOCK_SMTP "$cmd\n" ;
	my $res = <SOCK_SMTP> ;
	print "$targ_host > $res" ;

	print "$targ_host relay $relay\n" ;
	}

close SOCK_SMTP ;

付録 主な定数 (詳しくは sendmail-8.12.9/cf/README) define(`LOCAL_MAILER_MAX', `1000000')dnl ローカル配信の最大バイト数 (保存バイト数) define(`LOCAL_MAILER_MAXMSGS', `100')dnl    〃   メール数 define(`SMTP_MAILER_MAX', `1000000')dnl 外部配信の最大バイト数 (転送サイズ) define(`SMTP_MAILER_MAXMSGS', `100')dnl   〃  メール数 define(`confMAX_MESSAGE_SIZE', `1000000')dnl "O MaxMessageSize xxxxx" に相当 (以前のバージョンにはこちらを使う)