PostfixでのSMTP認証IDによるメール送信制限(check_sasl_access)検証

Postfixの2.11では、smtpd_sender_restrictionsやsmtpd_recipient_restrictionsへcheck_sasl_accessという制限を利用できるようになりました。 このcheck_sasl_accessを利用して、SMTP認証のID(SASL user name)によるメール送信制限を実装できるかを検証しました。

目的

smtpauth-managerは、SMTP認証のIDをキーとしてメール送信を制限する機能を持っています。 また、上記の通りPostfix 2.11では、check_sasl_accessという制限を利用できるようになりましたので、smtpauth-managerを利用せずPostfixのみでSMTP認証のIDによる 送信制限を実装できるかについて検証を行いました。

経緯

そもそも、smtpauth-managerを開発するきっかけは、以下のようなスパム送信(サブミッションスパム)に対応するためでした。

  1. スパマーは、SMTP認証のIDとパスワードを入手する。
  2. スパマーは、SMTPサーバのサブミッションポート(587/TCP)へ接続する。
  3. 接続後、上記で入手したIDとパスワードで、SMTP認証を行う。
  4. SMTP認証後、メールを送信する。
  5. メール送信後、TCP接続を切断せずに再度メールを送信する
この手順をSMTPで記述すると以下のようになります。
220 mta ESMTP Sendmail ; Thu, 2 Oct 2014 12:55:18 +0900
ehlo test
250-mta Hello client[10.0.0.1], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-AUTH LOGIN PLAIN
250-DELIVERBY
250 HELP
auth plain dG9z************==
235 2.0.0 OK Authenticated
mail from: spammer@example.com
250 2.1.0 spammer@example.com... Sender ok
rcpt to:  target1@example.jp
250 2.1.5 target1@example.jp... Recipient ok
data
354 Enter mail, end with "." on a line by itself
From: spammer@example.com
To: target1@example.jp
Subject: spam

spam
.
250 2.0.0 s923tItg021445 Message accepted for delivery
mail from: spammer@example.com
250 2.1.0 spammer@example.com... Sender ok
rcpt to:  target2@example.jp
250 2.1.5 target2@example.jp... Recipient ok
data
354 Enter mail, end with "." on a line by itself
From: spammer@example.com
To: target2@example.jp
Subject: spam

spam
.
250 2.0.0 s923tItg021445 Message accepted for delivery
mail from: spammer@example.com
250 2.1.0 spammer@example.com... Sender ok
rcpt to:  target3@example.jp
250 2.1.5 target3@example.jp... Recipient ok
data
354 Enter mail, end with "." on a line by itself
From: spammer@example.com
To: target3@example.jp
Subject: spam

spam
.
250 2.0.0 s923tItg021445 Message accepted for delivery
...
一度SMTP認証を通過したコネクションは、切断しない限り永久にメールを送信し続けることができます。 このようなスパム送信を停止するには、SMTP認証のIDを指定して、すでにSMTP認証で認証されたコネクション に対して、メール送信を拒否する機能が必要になります。

検証

概要

概要は以下の通りになります。
  1. Postfix 2.11.1にて、SMTP認証にてメール送信をできるように設定します。
  2. smtpd_sender_restrinctionへcheck_sasl_accessを追加します。
  3. check_sasl_accessで使用したtableにてメール送信を拒否できるかを確認します

環境

構築手順

パッケージのインストール

まず、CentOSをインストールします。インストール後、必要なパッケージをインストールします。
# yum install \
    gcc \ 
    nc \
    db4-devel \
    cyrus-sasl-devel \
    cyrus-sasl-plain \
    cyrus-sasl-ldap \
    openldap-devel \
    openldap-servers \
    openldap-clients

postfixのインストール

postfixをコンパイルしインストールします。その際、SASL, LDAPを利用できるようにします。
$ wget http://mirror.postfix.jp/postfix-release/official/postfix-2.11.1.tar.gz
$ tar xzf postfix-2.11.1.tar.gz
$ cd postfix-2.11.1
$ make tidy
$ make makefiles \
    CCARGS="-DUSE_SASL_AUTH -DUSE_CYRUS_SASL -DHAS_LDAP -I/usr/include/sasl" \
    AUXLIBS="-lsasl2 -lldap -llber"
$ make
$ su
# make install

saslauthdの設定

saslauthdを設定し、起動します。
# vi /etc/sysconfig/saslauthd
MECH=shadow

# service saslauthd start
	
メール送信用のアカウントを作成する。
# useradd test
# passwd test

postfixの設定

SMTP認証を有効にします。その際、/etc/postfix/saslにてSMTP認証のIDでメール送信を拒否できるように設定します。
# vi /etc/postfix/sasl

test DUNNO

# postmap hash:/etc/postfix/sasl
# vi /etc/postfix/master.cf

submission inet n       -       n       -       -       smtpd
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$submission_sasl_access
-o milter_macro_daemon_name=ORIGINATING

# vi /etc/postfix/main.cf

submission_sasl_access = check_sasl_access hash:/etc/postfix/sasl

# postfix start

動作確認

メール送信テストを行います。
$ nc 127.0.0.1 587
220 localhost.localdomain ESMTP Postfix
ehlo test
250-localhost.localdomain
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
auth plain dGVzdAB0ZXN0AHNlY3JldA==
235 2.7.0 Authentication successful
mail from: test@example.com
250 2.1.0 Ok
rcpt to: test@localhost.localdomain
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
from: test@example.com
to: test@localhost.localdomain
subject: test
            
test
.
250 2.0.0 Ok: queued as 530A18601BB
ここで、SMTP接続を継続した状態で、/etc/postfix/saslを変更し、SMTP認証のIDがtestの場合にメールを拒否するように設定します。
# vi /etc/postfix/sasl
test  REJECT

# postmap hash:/etc/postfix/sasl
このまま、メールを送信できるかをチェックします。
mail from: test@example.com
250 2.1.0 Ok
rcpt to: test@localhost.localdomain
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
: test@example.com
to: test@localhost.localdomain
subject: test

test
.
250 2.0.0 Ok: queued as 779348601BB
quit
221 2.0.0 Bye
上記の通り、SMTP接続が継続している間にhash:check_sasl_accessを変更しても、メール送信を拒否することはできませんでした。 ログには、以下の行が出力されていましたので、Hash tableの変更はsmtpdの再起動がするまで反映されないようです。 SMTP接続が継続している間は、Hash tableを更新してもメールを拒否することはできないことになります。
Oct  5 04:03:40 localhost postfix/smtpd[9794]: table hash:/etc/postfix/sasl has changed -- restarting

構築手順(LDAP Table)

Hash tableを使用た場合は上記の通りメールを拒否することができませんでしたので、LDAP Tableを使用してみます。

LDAP schema

追加属性
属性名Syntax
smtpauthEnableIntegerSMTP認証フラグ
smtpResponseCodeDirectoryStringレスポンスコード
追加ObjectClass
objectClass: mailAccount
uid: test
smtpauthEnable: 1
smtpResponseCode: DUNNO
userPassword: {CRYPT}$6$KKH1maas$WZtZ7gV/IM4Q2dWb5M2yOTnbzBGVW1J3SHCLjym9dl5OOwZLsfeQhq2XNHZsan2aibF2auxkKEf7AlWV8Dme4/

LDAPサーバ設定

LDAPサーバを起動します。
# service slapd start
RootDNを設定します。
# vi root.ldif

dn: olcDatabase={2}bdb,cn=config
changeType: modify
add: olcRootPW
olcRootPW: secret

# ldapmodify -Y EXTERNAL -Q -H ldapi:/// -f root.ldif
schemaを追加します、
# vi schema.ldif

dn: cn=mailaccount,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: mailaccount
olcAttributeTypes: ( 1.3.6.1.4.1.40000.1.1 NAME 'smtpauthEnable' DESC 'smtp
 auth enabled flag' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  )
olcAttributeTypes: ( 1.3.6.1.4.1.40000.1.2 NAME 'smtpResponseCode' DESC 'check_
 sasl_access response code' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcObjectClasses: ( 1.3.6.1.4.1.40000.100.1 NAME 'mailAccount' DESC 'mailAc
 count' STRUCTURAL MUST ( uid $ smtpauthEnable $ smtpResponseCode $ userPassword )
  )

# ldapadd -Y EXTERNAL -Q -H ldapi:/// -f schema.ldif
SMTP認証のID/Passwordを追加します。
# vi mailaccounts.ldif

dn: dc=my-domain,dc=com
objectClass: dcObject
objectClass: organization
dc: my-domain
o: test

dn: uid=test,dc=my-domain,dc=com
objectClass: mailAccount
uid: test
smtpauthEnable: 1
smtpResponseCode: DUNNO
userPassword: {CRYPT}$6$KKH1maas$WZtZ7gV/IM4Q2dWb5M2yOTnbzBGVW1J3SHCLjym9dl5OOwZLsfeQhq2XNHZsan2aibF2auxkKEf7AlWV8Dme4/

# ldapadd -D "cn=Manager,dc=my-domain,dc=com" -W -f mailaccounts.ldif

saslauthdの設定

ID/PasswordをLDAPサーバから検索するようにsaslauthdを設定します。
# vi /etc/saslauthd.conf

ldap_servers: ldap://127.0.0.1/
ldap_bind_dn: cn=Manager,dc=my-domain,dc=com
ldap_password: secret
ldap_search_base: dc=my-domain,dc=com
ldap_filter: (&(uid=%u)(smtpauthEnable=1))

# vi /etc/sysconfig/saslauthd

MECH=ldap

# service saslauthd restart

postfixの設定

check_sasl_accessの参照するtableをLDAPサーバへ変更します。
# vi /etc/postfix/sasl.conf
server_host = 127.0.0.1
search_base = dc=my-domain,dc=com
query_filter = (uid=%s)
bind = yes
bind_dn = cn=Manager,dc=my-domain,dc=com
bind_pw = secret
result_attribute = smtpResponseCode

# vi /etc/postfix/main.cf

submission_sasl_access = check_sasl_access ldap:/etc/postfix/sasl.conf

# postfix reload

動作確認(LDAP Table)

メール送信テストを行います。
220 localhost.localdomain ESMTP Postfix
ehlo test
250-localhost.localdomain
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
auth plain dGVzdAB0ZXN0AHNlY3JldA==
235 2.7.0 Authentication successful
mail from: test@example.com
250 2.1.0 Ok
rcpt to: test@localhost.localdomain
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
From: test@example.com
To: test@localhost.localdomain
Subject: test

test 
.
250 2.0.0 Ok: queued as 3A3E486020F
ここで、SMTP接続を継続した状態で、LDAPサーバのエントリを変更し、SMTP認証のIDがtestの場合にメールを拒否するように設定します。
# vi mod.ldif

dn: uid=test,dc=my-domain,dc=com
changeType:modify
replace: smtpResponseCode
smtpResponseCode: REJECT

# ldapmodify -D cn=Manager,dc=my-domain,dc=com -w secret -f mod.ldif
modifying entry "uid=test,dc=my-domain,dc=com"
このまま、メールを送信できるかをチェックします。
mail from: test@example.com
250 2.1.0 Ok
rcpt to: test@localhost.localdomain
554 5.7.1 : SASL login name rejected: Access denied
quit
221 2.0.0 Bye
LDAP tableを使用することで、SMTP接続継続中のメール送信を拒否することが出来ました。

まとめ

check_sasl_accessにより、PostfixにてSMTP認証のIDにてメールを送信拒否できることが出来ました。 ただし、SMTP接続を継続しながら何通もメールを送信している場合は、Hash tableでは対応できず、 LDAP tableを使用する必要があります。