Monthly Archives: February 2011

Linux #11 : How to implement spam filtering apps on Postfix

필자가 적용한 Spam filtering위한 방법 들을 나열해 보면 아래와 같다.

  SPF RULE Filtering  : Policyd SPF on Postfix
  SPAM Policy Filtering : SpamAssassin
  DKIM Verification Filtering : DKIMProxy - In
  Domain Key Verification Filtering : DKIMProxy - In
  Virus Detect Filtering : ClamAV
  Black List (RBL) Filtering : RBL Reject from Postfix

순서대로 하나씩 확인을 해보자. 가장 먼저 Black List (RBL) Filtering을 확인 해 보면, 이걸 가장 먼저 시작하는 이유는 postfix의 main.cf에 추가만 하면 되어 간단하기 때문이다.

# vi /etc/postfix/main.cf
...
smtpd_recipient_restrictions =
...
reject_rbl_client cbl.abuseat.org
reject_rbl_client zen.spamhaus.org
reject_rbl_client multihop.dsbl.org
reject_rbl_client unconfirmed.dsbl.org
reject_rbl_client dnsbl.sorbs.net
reject_rbl_client combined.njabl.org
...

위 reject_rbl_client를 smtp_recipient_restrictions에 추가 한 뒤 postfix를 restart하면 적용된다. 그럼, Maillog에서 아래와 같은 Log를 확인 할 수 있다.

Jul 13 15:55:43 xxxxxx postfix/smtpd[21862]: NOQUEUE: reject: RCPT from unknown[xxx.xxx.xxx.xxx]: 554 5.7.1 Service unavailable; Client host [xxx.xxx.xxx.xxx] blocked using cbl.abuseat.org; Blocked - see http://cbl.abuseat.org/lookup.cgi?ip=xxx.xxx.xxx.xxx; from= to= proto=ESMTP helo=

Clam Anti-Virus

Anti-Virus App으로 널리 사용되는 Clam Anti-Virus를 설치와 설정 부분이다. Source Download는 아래의 경로에서 받을 수 있다.

Download Clam AV : 
http://sourceforge.net/projects/clamav/files/clamav/0.96.1/clamav-0.96.1.tar.gz/download
Document : http://www.clamav.net/doc/latest/clamav-mirror-howto.pdf

# groupadd -g 8004 vscan

# useradd -g vscan -s /bin/false -c "Clam AntiVirus" -u 8897 clamav

# ./configure --prefix=/usr/local/clamav --with-group=vscan
# make;make install

# chown -R clamav.vscan /usr/local/clamav

Clam Anti-Virus를 위한 Group 및 User를 추가하고, Source로 부터 설치를 행한다.

# vi /usr/local/clamav/etc/clamd.conf
...
Example to #Example
LogFile /var/log/clamd/clamd.log
LogFileMaxSize 10M
LogTime yes
PidFile /var/run/clamd/clamd.pid
DatabaseDirectory /usr/local/clamav/db
LocalSocket /var/run/clamd/clamd.socket
#TCPSocket 3310
SelfCheck 0
ExcludePath ^/proc/
ExcludePath ^/sys/

# mkdir -p /var/log/clamd; mkdir -p /var/run/clamd;mkdir -p /usr/local/clamav/db
# chown -R clamav.vscan /var/log/clamd /var/run/clamd /usr/local/clamav/db

# vi /usr/local/clamav/etc/freshclam.conf
...
Example to #Example
DatabaseDirectory /usr/local/clamav/db
UpdateLogFile /var/log/clamd/freshclam.log
LogFileMaxSize 10M
LogTime yes
...

필자는 위와 같이 설정하였다. 그리고, Policy File을 Download 받기 위해 아래와 같이 실행하고 기동을 하면 된다.
주기적으로 Policy file을 갱신 할 수 있도록 cron에 등록을 한다.

# /usr/local/clamav/bin/freshclam

# ln -s /usr/local/clamav/bin/freshclam /etc/cron.daily/freshclam
# /usr/local/clamav/sbin/clamd

# vi /etc/inittab
...
cl:3:respawn:/usr/local/clamav/sbin/clamd
...

SPAM Assassin with Procmail

이 Procmail은 SPAM Assassin에서 Filtering한 메일 처리를 위해 설치하였다.

Download : http://www.alliedquotes.com/mirrors/apache/spamassassin/source/Mail-SpamAssassin-3.3.1.tar.gz
CPAN Site : http://www.perl.com/CPAN/

# yum -y install spamassassin procmail

SPAM Assassin의 동작을 위해 필요한 Perl Module은 아래와 같다. CPAN을 아래와 같이 설정 후 설치를 행하면 간단하게 설치할 수 있다.

CPAN Reconfiguration : cpan> o conf init
Configure HTTP CPAN Site => http://www.perl.com/CPAN/

cpan> o conf urllist push http://www.perl.com/CPAN/
cpan> o conf commit

Required and Optional Perl Module:
REQUIRED module : NetAddr::IP
REQUIRED module : Archive::Tar
REQUIRED module : IO::Zlib
optional module : Mail::SPF ( http://www.openspf.org/Implementations) : Use RPM => perl-Mail-SPF-2.006-1.el5.rf.noarch.rpm
: Dependency RPMS
perl-Error-0.17016-1.el5.rf.noarch
perl-NetAddr-IP-4.027-1.el5.rf
perl-version-0.76-1.el5.rf
optional module : IP::Country
optional module : Razor2 (http://packages.sw.be/razor-agents)
: Use RPM => perl-Razor-Agent-2.84-1.el5.rf
optional module : Net::Ident
optional module : IO::Socket::INET6
optional module : IO::Socket::SSL : http://search.cpan.org/~sullr/IO-Socket-SSL-1.33/SSL.pm
: Source Compile
optional module older than recommended: Mail::DKIM (perl-Mail-DKIM-0.36-1.el5.rf.noarch)
: Dependency RPMS
perl-Pod-Escapes-1.04-1.2.el5.rf.noarch
perl-Pod-Simple-3.13-1.el5.rf.noarch
perl-Test-Pod-1.42-1.el5.rf.noarch
yum -y install perl-TimeDate.noarch
perl-MailTools-2.06-1.el5.rf.noarch
perl-Crypt-OpenSSL-RSA-0.25-1.el5.rf
perl-Digest-SHA-5.48-1.el5.rf
optional module : Encode::Detect

설치 완료 후 SPAM Assassin 및 Procmail용 rc file을 설정한다.

# vi /etc/mail/spamassassin/local.cf
...
required_hits 5
report_safe 0
rewrite_header Subject **SPAM**
skip_rbl_checks 1 => Currently RBL Check works on PostFix

trusted_networks 10.0.0.0/8
allow_user_rules 0

use_razor2 1
use_pyzor 1

score HEADER_8BITS 0
score HTML_COMMENT_8BITS 0
score SUBJ_FULL_OF_8BITS 0
score CASHCASHCASH 0
score UPPERCASE_25_50 0
score UPPERCASE_50_75 0
score UPPERCASE_75_100 0

## PROCMAIL
# vi /etc/procmailrc
SHELL=/bin/bash
PATH=/usr/bin:/bin
#DEFAULT=/home/popmail/[email protected]$DOMAIN/new
DEFAULT=/home/popmail/$USER/new
LOGFILE=/var/log/spam/procmail.log
#LOCKFILE=/home/popmail/[email protected]$DOMAIN/.procmail.lock
LOCKFILE=/home/popmail/$USER/.procmail.lock
#VERBOSE=ON

:0fw
|/usr/bin/spamc
#|/usr/bin/spamc -u $USER

아래와 같이 chkconfig에 추가하고, 기동을 하면 된다.

# chkconfig spamassassin on
# /etc/init.d/spamassassin start

기동 시 아래와 같은 Error가 발생을 하여 제대로 기동이 되지 않는 경우가 있는데, 이런 경우는 관련 Perl Module이 부족한 경우 이므로, 추가적으로 설치를 하면 된다.

spamd[7616]: Can't locate Crypt/OpenSSL/Bignum.pm in
==> http://packages.sw.be/perl-Crypt-OpenSSL-Bignum/perl-Crypt-OpenSSL-Bignum-0.04-1.el5.rf.x86_64.rpm

cd /etc/mail/spamassassin
wget http://spamassassin.apache.org/released/GPG-SIGNING-KEY
gpg --import GPG-SIGNING-KEY

Amavisd-new

Amavisd-new는 High performance interface로 MTA와 Content checker인 Clamv, Spam Assassin과 연동하여 사용된다.

Link : http://www.ijs.si/software/amavisd
Download RPM : http://packages.sw.be/amavisd-new/amavisd-new-2.6.4-2.el5.rf.x86_64.rpm

필요 Perl Modules

Required Perl Module:
  IO::Stringy
  Unix::Syslog
  MIME::Words
   : MIME::Head
   : MIME::Body
   : MIME::Entity
   : MIME::Parser
   : MIME::Decoder
   : MIME::Decoder::Base64
   : MIME::Decoder::Binary
   : MIME::Decoder::QuotedPrint
   : MIME::Decoder::NBit
   : MIME::Decoder::UU
   : MIME::Decoder::Gzip64
  Convert::BinHex(http://search.cpan.org/~eryq/Convert-BinHex-1.119/lib/Convert/BinHex.pm)
   : Compile
  BerkeleyDB

설치 후 Group 및 User를 생성 해 주고, 아래와 같이 작업한다.

# useradd -g vscan -s /bin/false -c "Amavisd New" -u 8895 amavis

# mkdir /usr/local/amavis
# mkdir /usr/local/amavis/tmp /usr/local/amavis/var /usr/local/amavis/db /usr/local/amavis/home

# chown -R amavis:vscan /usr/local/amavis
# chmod -R 750 /usr/local/amavis

# cp amavisd /usr/local/sbin/
# chown root /usr/local/sbin/amavisd
# chmod 755 /usr/local/sbin/amavisd

# cp amavisd.conf /etc/amavisd.conf
# chown root:vscan /etc/amavisd.conf
# chmod 640 /etc/amavisd.conf
# chown -R amavis.vscan /usr/share/spamassassin

설정은 아래와 같이 하였다.

# vi /etc/amavisd.conf
...
$undecipherable_subject_tag = '';
@bypass_spam_checks_maps = (1); # controls running of anti-spam code
-> Amavisd-new와 연계하지 않고, Spam Assassin을 이용하는 경우, 또는 SPAM 대책을 행하지 않는 경우
$daemon_user = 'amavis'; # (no default; customary: vscan or amavis), -u
$daemon_group = 'vscan'; # (no default; customary: vscan or amavis), -g
$mydomain = 'ijji.com';
$MYHOME = '/usr/local/amavis';
$syslog_facility = 'daemon';

$myhostname = 'XXXX.ijji.com'; # must be a fully-qualified domain name!

#$QUARANTINEDIR = '/var/virusmails'; # -Q
$helpers_home = "$MYHOME/var";
$lock_file = "$MYHOME/var/amavisd.lock"; # -L
$pid_file = "$MYHOME/var/amavisd.pid"; # -P

##
['ClamAV-clamd',
\&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamd/clamd.socket"],
qr/\bOK$/, qr/\bFOUND$/,
qr/^.?: (?!Infected Archive)(.) FOUND$/ ],

@mynetworks = qw( 127.0.0.0/8 [::1] [FE80::]/10 [FEC0::]/10
10.0.0.0/8 );

$notify_method = 'smtp:[127.0.0.1]:10026';
$forward_method = 'smtp:[127.0.0.1]:10026'; # set to undef with milter!

$final_virus_destiny = D_DISCARD;
$final_banned_destiny = D_BOUNCE;
$final_spam_destiny = D_PASS;
$final_bad_header_destiny = D_PASS;

##### Meaning
D_BOUNCE : Mail is not delivered, but noticed to sender.
D_REJECT : same as D_BOUNCE, but noticed to sender in case of the mail is virus or mass spam.
D_DISCARD : Mail is not delivered and doesn't notice to sender.
D_PASS : Mail is delivered.

$sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level
$sa_tag2_level_deflt = 6.2; # add 'spam detected' headers at that level
$sa_kill_level_deflt = 6.9; # triggers spam evasive actions (e.g. blocks mail)
$sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
# $sa_quarantine_cutoff_level = 25; # spam level beyond which quarantine is off

$sa_mail_body_size_limit = 400*1024; # don't waste time on SA if mail is larger
$sa_spam_subject_tag = '**SPAM** ';
...

# cp amavisd_init.sh /etc/init.d/amavisd
# chmod 755 /etc/init.d/amavisd
# chkconfig amavisd on

# vi /etc/init.d/amavisd
...
prog="/usr/local/sbin/amavisd"
...

# /etc/init.d/amavisd start

Amavisd 기동 시 아래와 같은 Error가 발생할 수도 있는데, 이 경우 역시 Perl Module의 부족한 것이므로 추가 설치를 해 주면 된다.

.. ERROR>>
Starting amavisd: fetch_modules: error loading optional module MIME/Decoder/BinHex.pm: Can't locate Convert/BinHex.pm
==> INSTALL Perl Module Convert::BinHex
Problem in Amavis::DB or Amavis::DB::SNMP code: Can't locate BerkeleyDB.pm
==> INSTALL Perl Module BerkeleyDB
Starting amavisd: Problem in Amavis::DKIM code: Can't locate Crypt/OpenSSL/RSA.pm
==> INSTALL RPM :

perl-Crypt-OpenSSL-RSA-0.25-1.el5.rf
perl-Digest-SHA-5.47-1.el5.rf
perl-Mail-DKIM-0.36-1.el5.rf.noarch
perl-Carp-Clan-5.3-1.2.1.noarch : YUM
perl-Bit-Vector-6.4-2.2.2.1 : YUM
perl-Date-Calc-5.4-1.2.2.1 : YUM
perl-Net-DNS : YUM
perl(Mail::Address) : http://packages.sw.be/perl-MailTools/perl-MailTools-2.06-1.el5.rf.noarch.rpm
perl-TimeDate.noarch
perl(Test::Pod) : http://packages.sw.be/perl-Test-Pod/perl-Test-Pod-1.42-1.el5.rf.noarch.rpm
perl(Pod::Simple) : http://packages.sw.be/perl-Pod-Simple/perl-Pod-Simple-3.13-1.el5.rf.noarch.rpm
perl(Pod::Escapes) : http://packages.sw.be/perl-Pod-Escapes/perl-Pod-Escapes-1.04-1.2.el5.rf.noarch.rpm
Starting amavisd: Problem in antispam SA code: Can't locate Mail/SpamAssassin.pm
==> # /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi]$ ln -s /usr/local/SpamAssassin/lib/perl5/site_perl/5.8.8/Mail
Convert::TNEF
Convert::UUlib
Archive::Zip
Unable to load module: Can't locate Net/Server/Proto/UNIX.pm
==> http://packages.sw.be/perl-Net-Server/perl-Net-Server-0.96-1.el5.rf.noarch.rpm

Amavisd에 DKIM Checker를 추가 할 경우는 아래와 같은 설정 내용을 추가한다.

# vi /etc/amavisd.conf
...
$enable_dkim_verification = 1; # enable DKIM signatures verification
$enable_dkim_signing = 1; # load DKIM signing code, keys defined by dkim_key
dkim_key('esmtp.ijji.com', 'default', '/etc/domainkeys/default');
dkim_key('karosgame.com', 'default', '/etc/domainkeys/default');
...

위에서 설치한 Spam Assassin과 더불어 Amavisd-new가 Postfix 상에서 동작할 수 있도록 master.cf를 수정 해 준다.

# vi /etc/postfix/master.cf

#
# Content Filter amavisd-new
#

smtp-amavis unix - - n - 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
-o disable_dns_lookups=yes
127.0.0.1:10029 inet n - n - - smtpd
-o content_filter=dksign-in:[127.0.0.1]:10025
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=
-o mynetworks=127.0.0.0/8,10.36.0.0/16,10.37.0.0/16
-o receive_override_options=no_unknown_recipient_checks
-o strict_rfc821_envelopes=yes
-o smtpd_error_sleep_time=0
-o smtpd_soft_error_limit=1001
-o smtpd_hard_error_limit=1000

#
# Procmail
#
procmail unix - n n - - pipe
# flags=R user=vuser argv=/usr/bin/procmail -t -m USER=${user} DOMAIN=${nexthop} /etc/procmailrc
flags=R user=vuser argv=/usr/bin/procmail -t -m USER=${recipient} /etc/procmailrc

# vi /etc/postfix/main.cf
...
# The mailbox_command parameter specifies the optional external
# command to use instead of mailbox delivery. The command is run as
# the recipient with proper HOME, SHELL and LOGNAME environment settings.
# Exception: delivery for root is done as $default_user.
#
# Other environment variables of interest: USER (recipient username),
# EXTENSION (address extension), DOMAIN (domain part of address),
# and LOCAL (the address localpart).
#
# Unlike other Postfix configuration parameters, the mailbox_command
# parameter is not subjected to $parameter substitutions. This is to
# make it easier to specify shell syntax (see example below).
#
# Avoid shell meta characters because they will force Postfix to run
# an expensive shell process. Procmail alone is expensive enough.
#
# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN
# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.
#

#mailbox_command = /some/where/procmail
#mailbox_command = /some/where/procmail -a "$EXTENSION"
mailbox_command = /usr/bin/procmail

#virtual_transport = virtual
virtual_transport = procmail

### FILTER
content_filter=smtp-amavis:[127.0.0.1]:10024
content_filter=dksign-in:[127.0.0.1]:10025

### PROCMAIL
procmail_destination_recipient_limit = 1
transport_destination_recipient_limit = 1
...

정상적으로 동작을 할 경우 Amavisd-new 기동 시 아래와 같은 Log를 maillog에서 확인 할 수 있다.

Jul 13 15:02:53 ca1xc131 amavis[19660]: starting. /usr/local/sbin/amavisd at esmtp1.ijji.com amavisd-new-2.6.4 (20090625), Unicode aware, LC_ALL="en_US", LANG="en_US.UTF-8"
Jul 13 15:02:53 xxxxxxx amavis[19660]: Perl version 5.008008
Jul 13 15:02:53 xxxxxxx amavis[19661]: Net::Server: Group Not Defined. Defaulting to EGID '8005 8005'
Jul 13 15:02:53 xxxxxxx amavis[19661]: Net::Server: User Not Defined. Defaulting to EUID '8895'
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Amavis::Conf 2.207
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Archive::Zip 1.30
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module BerkeleyDB 0.42
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Compress::Zlib 2.017
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Convert::TNEF 0.17
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Convert::UUlib 1.33
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Crypt::OpenSSL::RSA 0.25
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Digest::MD5 2.36
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Digest::SHA 5.48
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module IO::Socket::INET6 2.65
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module MIME::Entity 5.428
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module MIME::Parser 5.428
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module MIME::Tools 5.428
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Mail::DKIM::Signer 0.36
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Mail::DKIM::Verifier 0.36
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Mail::Header 2.06
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Mail::Internet 2.06
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Net::DNS 0.59
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Net::Server 0.97
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Socket6 0.23
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Time::HiRes 1.9719
Jul 13 15:02:53 xxxxxxx amavis[19661]: Module Unix::Syslog 1.1
Jul 13 15:02:53 xxxxxxx amavis[19661]: Amavis::DB code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Amavis::Cache code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: SQL base code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: SQL::Log code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: SQL::Quarantine NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Lookup::SQL code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Lookup::LDAP code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: AM.PDP-in proto code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: SMTP-in proto code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Courier proto code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: SMTP-out proto code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Pipe-out proto code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: BSMTP-out proto code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Local-out proto code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: OS_Fingerprint code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: ANTI-VIRUS code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: ANTI-SPAM code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: ANTI-SPAM-EXT code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: ANTI-SPAM-C code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: ANTI-SPAM-SA code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Unpackers code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: DKIM code loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Tools code NOT loaded
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found $file at /usr/bin/file
Jul 13 15:02:53 xxxxxxx amavis[19661]: No $altermime, not using it
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .mail
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .asc
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .uue
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .hqx
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .ync
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .F tried: unfreeze, freeze -d, melt, fcat
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .Z at /usr/bin/gzip -d
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .gz at /usr/bin/gzip -d
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .bz2 at /usr/bin/bzip2 -d
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .lzo tried: lzop -d
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .rpm at /usr/bin/rpm2cpio
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .cpio at /usr/bin/pax
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .tar at /usr/bin/pax
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found decoder for .deb at /usr/bin/ar
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .zip
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .7z tried: 7zr, 7za, 7z
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .rar tried: rar, unrar
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .arj tried: arj, unarj
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .arc tried: nomarch, arc
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .zoo tried: zoo, unzoo
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .lha tried: lha
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .cab tried: cabextract
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .tnef tried: tnef
Jul 13 15:02:53 xxxxxxx amavis[19661]: Internal decoder for .tnef
Jul 13 15:02:53 xxxxxxx amavis[19661]: No decoder for .exe tried: rar, unrar; lha; arj, unarj
Jul 13 15:02:53 xxxxxxx amavis[19661]: Using primary internal av scanner code for ClamAV-clamd
Jul 13 15:02:53 xxxxxxx amavis[19661]: Found secondary av scanner ClamAV-clamscan at /usr/local/clamav/bin/clamscan
Jul 13 15:02:53 xxxxxxx amavis[19661]: Creating db in /usr/local/amavis/db/; BerkeleyDB 0.42, libdb 4.3

또한, 동작 시의 Log는 아래와 같다.

Jul 13 14:30:49 xxx amavis[17786]: (17786-04) Blocked BANNED (application/octet-stream,.exe,.exe-ms,twain.dll), [209.85.212.47] [209.85.212.47] -> , Message-ID: , mail_id: 7GceC62MrFpK, Hits: -, size: 131943, [email protected],[email protected], 433 ms
Jul 13 14:30:49 xxx postfix/smtp[17907]: B396A67887E: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=1.1, delays=0.67/0/0.01/0.44, dsn=2.5.0, status=sent (250 2.5.0 Ok, id=17786-04, BOUNCE)
Jul 13 16:07:04 xxxxxx amavis[19664]: (19664-15) Passed CLEAN, [xxx.xxx.xxx.xxx] [xxx.xxx.xxx.xxx] -> , Message-ID: , mail_id: TXb1emZTzJHa, Hits: -, size: 2687, queued_as: 991C9678882, [email protected],[email protected], 205 ms
Jul 13 16:07:04 xxxxx postfix/smtp[22425]: 44271678881: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=46, delays=46/0/0.01/0.21, dsn=2.0.0, status=sent (250 2.0.0 Ok, id=19664-15, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 991C9678882)
Jul 13 16:13:22 xxxxx amavis[19664]: (19664-17) Blocked INFECTED (Eicar-Test-Signature), MYNETS LOCAL [10.37.114.27] [10.37.114.27] -> , Message-ID: , mail_id: SXo7SRF93r5L, Hits: -, size: 36861, 265 ms
Jul 13 16:13:22 xxxxx postfix/smtp[22787]: 4A622678881: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=0.28, delays=0.01/0/0.01/0.27, dsn=2.7.0, status=sent (250 2.7.0 Ok, discarded, id=19664-17 - INFECTED: Eicar-Test-Signature)

SPF Rule Filtering

이 경로에서 해당 file을 받을 수 있다. https://launchpad.net/postfix-policyd-spf-perl/

필요한 Perl Module들은 아래와 같다.

Mail::SPF (http://packages.sw.be/perl-Mail-SPF/perl-Mail-SPF-2.006-1.el5.rf.noarch.rpm)
  perl(Error) (http://packages.sw.be/perl-Error/perl-Error-0.17016-1.el5.rf.noarch.rpm)
  perl(NetAddr::IP) (http://packages.sw.be/perl-NetAddr-IP/perl-NetAddr-IP-4.027-1.el5.rf.x86_64.rpm)
  perl(version) (http://packages.sw.be/perl-version/perl-version-0.76-1.el5.rf.x86_64.rpm)
  *** DOESN'T NEED => Mail::SPF::Query (http://packages.sw.be/perl-Mail-SPF-Query/perl-Mail-SPF-Query-1.999.1-1.2.el5.rf.noarch.rpm)
 perl(Net::CIDR::Lite) (http://packages.sw.be/perl-Net-CIDR-Lite/perl-Net-CIDR-Lite-0.20-1.2.el5.rf.noarch.rpm)
 perl(Sys::Hostname::Long) (http://packages.sw.be/perl-Sys-Hostname-Long/perl-Sys-Hostname-Long-1.4-1.2.el5.rf.noarch.rpm)

제공되는 Manual에 맞춰 설정을 하면,

1. Copy postfix-policyd-spf-perl to /etc/postfix/postfix-policyd-spf-perl

2. Add the following to /etc/postfix/master.cf:

#
# SPF Rule Filter
#
policy unix - n n - 0 spawn
user=nobody argv=/etc/postfix/postfix-policyd-spf-perl

3. Configure the Postfix policy service in /etc/postfix/main.cf:

smtpd_recipient_restrictions =
...
reject_unauth_destination
check_policy_service unix:private/policy
...

4. Add “policy_time_limit = 3600” to main.cf

### SPF
policy_time_limit = 3600

5. Restart Postfix.

아래와 같이 동작 Log를 확인 할 수 있다.

Jul 13 15:51:57 xxxxxx postfix/policy-spf[21886]: : SPF none (No applicable sender policy available): HELO/EHLO (Null Sender): omr-m12.mx.aol.com
Jul 13 15:51:57 xxxxxx postfix/policy-spf[21886]: handler sender_policy_framework: is decisive.
Jul 13 15:51:57 xxxxxx postfix/policy-spf[21886]: : Policy action=PREPEND Received-SPF: none (omr-m12.mx.aol.com: No applicable sender policy available) receiver=xxxxxx.xxx.com; identity=helo; helo=omr-m12.mx.aol.com; client-ip=64.12.207.154

DKIM Rule Filtering이 적용되었을 경우의 동작 Log는

Jul 14 12:25:56 xxxxxx dkimproxy.in[10325]: DKIM verify - pass; message-id=, signer=, from=
Jul 14 12:25:56 xxxxxx postfix/cleanup[12221]: 84BA1678850: message-id=
Jul 14 12:25:56 xxxxxx postfix/qmgr[12215]: 84BA1678850: from=, size=2946, nrcpt=1 (queue active)
Jul 14 12:25:56 xxxxxx postfix/smtpd[12295]: disconnect from localhost.localdomain[127.0.0.1]
Jul 14 12:25:56 xxxxxx postfix/virtual[12311]: 84BA1678850: to=, relay=virtual, delay=0.12, delays=0.12/0/0/0, dsn=2.0.0, status=sent (delivered to maildir)
Jul 14 12:25:56 xxxxxx postfix/qmgr[12215]: 84BA1678850: removed
Jul 14 12:25:56 xxxxxx amavis[11025]: (11025-05) Passed CLEAN, [74.125.83.47] [74.125.83.47] -> , Message-ID: , mail_id: icPExIFYk88J, Hits: -, size: 2150, queued_as: 84BA1678850, [email protected],[email protected], 381 ms

마지막으로 Mail의 처리 순서를 Incoming, Outgoing으로 나눠서 보면 아래와 같이 동작을 하게 되는 것이다.

Incoming Mail

Incoming Mail > Prot 25 : [SMTPD] > Blocking RBL(Black List) > SPF Verify : SPF RULE > 127.0.0.1:10024 [SMTPD] > Amavisd-new (Verify) With ClamAV, SpamAssassin > 127.0.0.1:10029 [SMTPD]
> 127.0.0.1:10025 [SMTPD] > DKIMProxy_IN Verify : DKIM, DomainKey > 127.0.0.1:10026 [SMTPD] > MAIL BOX

Outgoing Mail

MyNetwork (Can transfer TCP Relay or Send) > Port 25 : [SMTPD] > 127.0.0.1:10027 [SMTPD] > DKIMProxy_OUT Originating : No SIGN > 127.0.0.1:10028 [SMTPD] > Outgoing Mail
: Submission (Can transfer TCP Relay or Send) > Port 587 : [SMTPD] > 127.0.0.1:10027 [SMTPD] > DKIMProxy_OUT Originating : DKIM SIGN > 127.0.0.1:10028 [SMTPD] > Outgoing Mail

Linux #10 : LDAP Server is down and then we can’t access all servers is using ldap authentication.

Now using NSS_LDAP for LDAP Client. /etc/ldap.conf is being controlled NSS_LDAP.
NSS_LDAP has reconnect policy, default policy is hard_open(alias hard).

It means ..

# Reconnect policy:
# hard_open: reconnect to DSA with exponential backoff if
# opening connection failed
# hard_init: reconnect to DSA with exponential backoff if
# initializing connection failed
# hard: alias for hard_open
# soft: return immediately on server failure
#bind_policy hard

hard_open mode is reconnecting the ldap server is down or problem, again and again.
because the result that this situation looks same hang up the server, we can’t access it and takes a long time to access it.
account by ldap and root account, too.

soft mode is return immediately on server failure.
If LDAP server is down, return message immediately. “Permission denied, please try again.”
and we can access for root account immediately when LDAP is down.

Linux #9 : could not search LDAP server – Server is unavailable

nss_ldap: could not search LDAP server - Server is unavailable

You can see this message when the LDAP server is down or bad performance.
if the LDAP server is down, you can solve this issue only the LDAP restart.
but, if it caused by bad performance, you should check about connection counts of LDAP.

Local Account(In Base Authentication) is describing their account from LDAP server when nss_swich of the server is configured for LDAP system and the account is trying to use any processes or tasks, jobs.

Sometimes it caused by increasing sessions and decreasing performance of LDAP system.

You can ignore accounts to access to LDAP when the accounts is working their processes or tasks, jobs.

You can reduce a lot of session from local account adding as below the option
URL : http://linux.die.net/man/5/nss_ldap

nss_initgroups_ignoreusers This option directs thenss_ldapimplementation of initgroups(3)
to return NSS_STATUS_NOTFOUND if called with a listed users as its argument.

$ more /etc/ldap.conf
... Add below a line
nss_initgroups_ignoreusers root,nagios,nrpe,www,rancid,oracle,mysql,ntp,postfix,daemon,named

Linux #8 : How to Implement Dovecot, DKIM, DomainKey, MySQL in Postfix #2

5. Making and checking each configuration files for Postfix and dovecot

지난 시간에 이어서 각 file들의 대략적인 설정 내용을 확인 해 보도록 하자. 해당 설정 파일은 필자가 구축한 시스템에서 적용된 내용으로 모든 시스템에 적합하다고는 할 수 없다.

그럼, dovecot.conf를 살펴보자.

$ /etc/dovecot.conf
## Dovecot configuration
..
# Protocols we want to be serving: imap imaps pop3 pop3s
# If you only want to use dovecot-auth, you can set this to "none".
#protocols = imap imaps pop3 pop3s
protocols = imaps pop3s - 원하는 Protocol을 설정한다.

## Mailbox locations and namespaces
#mail_location = maildir:/home/popmail/%d/%u
mail_location = maildir:/home/popmail/%u <- Maildir 형식을 사용하고 /home/popmail을 Mailbox Directory로 지정했을 경우

# Don't use mmap() at all. This is required if you store indexes to shared
# filesystems (NFS or clustered filesystem).
#mmap_disable = no
mmap_disable=yes <- NFS나 clustered filesystem 사용 여부에 따라..

# Valid UID range for users, defaults to 500 and above. This is mostly
# to make sure that users can't log in as daemons or other system users.
# Note that denying root logins is hardcoded to dovecot binary and can't
# be done even if first_valid_uid is set to 0.
#first_valid_uid = 500
last_valid_uid = 80000 <- Mailbox에 대한 Permission을 80000으로 설정

# Valid GID range for users, defaults to non-root/wheel. Users having
# non-valid GID as primary group ID aren't allowed to log in. If user
# belongs to supplementary groups with non-valid GIDs, those groups are
# not set.
#first_valid_gid = 1
last_valid_gid = 80000

auth default {
# Space separated list of wanted authentication mechanisms:
# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi
# NOTE: See also disable_plaintext_auth setting.
mechanisms = plain login digest-md5 cram-md5 apop -> 인증 관련 mechanism을 설정

#passdb pam {
#{
<- comment passdb pam.
passdb sql {
# Path for SQL configuration file, see doc/dovecot-sql-example.conf
args = /etc/dovecot-mysql.conf
}
..
# It's possible to export the authentication interface to other programs:
socket listen {
..
client {
# The client socket is generally safe to export to everyone. Typical use
# is to export it to your SMTP server so it can do SMTP AUTH lookups
# using it.
path = /var/spool/postfix/private/auth
mode = 0660
user = postfix
group = root

}
}
}

다음은 postfix 설치시 mysql 연동을 위한 설정을 하였으므로, dovecot을 통해 mysql안의 user table을 참조 할 수 있도록 한다.

$ /etc/dovecot-mysql.conf
driver = mysql
#default_pass_scheme = PLAIN
default_pass_scheme = CRAM-MD5
connect = dbname=postfix user=postfix host=XXXXXXX password=XXXXXXX
password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1'
user_query = SELECT concat('/home/popmail/', maildir) as home, 80000 as uid, 80000 as gid FROM mailbox WHERE username = '%u' AND active = '1'

필자는 위에서 “CLAM-MD5″를 사용하였는데, Webmail, postfixadmin, dovecot의 암호화 방식을 통일하기 위해서 입니다. PLAIN을 사용하면 dovecot의 log에서 바로 보이는게 문제가 되고, 다른 MD5를 쓰면 webmail에서 지원을 안하기도 하고 해서..

다음은 postfix의 main.cf의 설정이다. 이 또한 dovecot 연동을 위한 설정을 한다.

$ /etc/postfix/main.cf
..
# INTERNET HOST AND DOMAIN NAMES
#
..
#myhostname = host.domain.tld
myhostname = [my.domain.com] <- FQDN을 지정
..
# SENDING MAIL
#
..
#myorigin = $mydomain
myorigin = $myhostname <- Mail 송신시 Sending [email protected] Domain 명을 기입

# RECEIVING MAIL
..
#inet_interfaces = localhost
inet_interfaces = all <- 외부로부터 Mail 수신을 허가

# TRUST AND RELAY CONTROL
..
mynetworks = [Add Trust IPs] , localhost, 127.0.0.1
..
# DELIVERY TO MAILBOX
#
..
#home_mailbox = Mailbox
home_mailbox = Maildir/ <- Mailbox 형식을 Maildir로 설정
..
# SHOW SOFTWARE VERSION OR NOT
#
#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)
smtpd_banner = $myhostname ESMTP unknown <- Version 정보의 제거
..
message_size_limit = 10485760 <- 수신 Mail Size를 제한 10485760은 10MB
...
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination

...
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.pem
smtpd_tls_key_file = /etc/pki/tls/certs/mail.pem
smtpd_tls_session_cache_database = btree:/etc/postfix/smtpd_scache
tls_daemon_random_source = dev:/dev/urandom
...

local_transport = virtual
virtual_transport = virtual
virtual_mailbox_base = /home/popmail
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_alias_domains = $virtual_alias_maps
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 80000
virtual_uid_maps = static:80000

virtual_gid_maps = static:80000
mailbox_size_limit = 51200000
message_size_limit = 10240000
virtual_mailbox_limit = 51200000
#virtual_mailbox_limit_maps = hash:/etc/postfix/vquota
virtual_mailbox_limit_override = yes
virtual_overquota_bounce = yes
virtual_mailbox_limit_inbox = yes

virtual_alias_maps = mysql:/etc/postfix,virtual_mailbox_domains = mysql:/etc/postfix,virtual_mailbox_maps = mysql:/etc/postfix 위 설정과 관련 된 DB 접근을 위한 설정 파일도 준비한다. Files은 mysql_virtual_alias_maps.cf mysql_virtual_domains_maps.cf mysql_virtual_mailbox_maps.cf와 같다.

$ more mysql_virtual_alias_maps.cf
user = [db username]
password = [password]
hosts = [hostname]
dbname = postfix
table = alias
select_field = goto
where_field = address

$ more mysql_virtual_domains_maps.cf
user = [db username]
password = [password]
hosts = [hostname]
dbname = postfix
table = domain
select_field = domain
where_field = domain
additional_conditions = and active = '1'

$ more mysql_virtual_mailbox_maps.cf
user = [db username]
password = [password]
hosts = [hostname]
dbname = postfix
table = mailbox
select_field = maildir
where_field = username

일단 여기까지라면 Postfix와 Dovecot, MySQL까지의 단계가 완료 된다.

6. Install DKIM, Domain Key Module on Postfix

• Crypt::OpenSSL::RSA
• Digest::SHA
• Digest::SHA1
• Error
• Mail::Address
• MIME::Base64
• Net::DNS
• Net::Server
• Test::More

DKIM, Domain Key Module을 설치하기 위해서 위와 같은 Perl module들이 필요하게 되고, 물론 Perl도 설치되어 있어야 한다. CPAN으로 설치가 가능한 환경이라면 아래와 같이 하면 된다.

cpan> install Crypt::OpenSSL::RSA Digest::SHA Digest::SHA1 Error Mail::Address MIME::Base64 Net::DNS Net::Server Test::More

그렇지 않다면 상당히 번거로운 작업이 되는데, 아래와 같이 각 Source들을 직접 download 받은 뒤 순서대로 작업을 하면 된다. 각 Module들이 각각 dependency가 존재 해 먼저 설치되어야만 하는 것들이 있다.

[PERL :Error] Error-0.17016.tar.gz
[PERL :Crypt::OpenSSL::RSA]
WITH Crypt::OpenSSL::Random
Crypt-OpenSSL-Random-0.04.tar.gz
Crypt-OpenSSL-RSA-0.26.tar.gz
[PERL :Net::DNS]
WITH Digest-HMAC
WITH Digest::SHA1
WITH Net::IP
WITH Digest::SHA
Digest-SHA1-2.12.tar.gz
Digest-HMAC-1.02.tar.gz
Net-IP-1.25.tar.gz
Digest-SHA-5.48.tar.gz
Net-DNS-0.66.tar.gz
[PERL :Mail::Address]
WITH Date::Format
WITH Date::Parse
WITH Test::Pod
TimeDate-1.20.tar.gz
Test-Pod-1.41.tar.gz
MailTools-2.06.tar.gz
[PERL :Mail-DomainKeys-0.80]
Mail-DomainKeys-0.80.tar.gz
[PERL :AppConfig-1.56]
AppConfig-1.56.tar.gz

위 추가적인 Perl Module을 설치해야만 Mail-DKIM과 DKIMPROXY가 설치가능하게 된다. Perl의 경우 압축 해제 후 perl Makefile.PL;make;make install 명령어로 설치한다.

[PERL :MAIL-DKIM]
Mail-DKIM-0.36.tar.gz
[DKIMPROXY : http://dkimproxy.sourceforge.net/download.html%5D
WITH PERL :Net::Server
Net-Server-0.97.tar.gz
tar zxvf dkimproxy-1.2.tar.gz
cd dkimproxy-1.2
./configure --prefix=/usr/local/dkimproxy
make;make install

이후 Rebooting시 자동으로 시작 되도록 등록하면 설치 완료.

$ cp source-directory/dkimproxy /etc/init.d/dkimproxy
$ vi dkimproxy
..
### BEGIN CONFIGURABLE BITS
DKIMPROXYDIR=/usr/local/dkimproxy
DKIMPROXYUSER=postfix
DKIMPROXYGROUP=postfix
### END CONFIGURABLE BITS
..
$ chkconfig dkimproxy on

7. Configure DKIM, Domain Key Module on Postfix

위 과정대로 설치를 하였다면 dkimproxy는 /usr/local/dkimproxy에 설치되어 있을 것이다. 그럼, inbound, outbound와 관련 된 설정파일을 설정하는데, 이 파일의 Inbound, outbound Port 즉 아래 설정 내용은 10025, 10026, 10027, 10028은 Postfix의 master.cf의 설정과 연동이 된다.

## Inbound
$ more /usr/local/dkimproxy/etc/dkimproxy_in.conf
# specify what address/port DKIMproxy should listen on
listen 127.0.0.1:10025

# specify what address/port DKIMproxy forwards mail to
relay 127.0.0.1:10026

## Outbound
$ more /usr/local/dkimproxy/etc/dkimproxy_out.conf
# specify what address/port DKIMproxy should listen on
listen 127.0.0.1:10027

# specify what address/port DKIMproxy forwards mail to
relay 127.0.0.1:10028

# specify what domains DKIMproxy can sign for (comma-separated, no spaces)
domain [each domain information]

# specify what signatures to add
signature dkim(c=relaxed)
signature domainkeys(c=nofws)

# specify location of the private key
keyfile /etc/domainkeys/default

# specify the selector (i.e. the name of the key record put in DNS)
selector default

그리고, Domain Key를 생성하고, 아래와 같이 Permission과 Ownership 설정을 한다.

mkdir -p /etc/domainkeys
cd /etc/domainkeys
openssl genrsa -out rsa.private 768
openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM
chown -R root.postfix /etc/domainkeys
chmod 755 /etc/domainkeys

$ rsa.private
-----BEGIN RSA PRIVATE KEY-----
MIIBygIBAAJhALgNMgOmSOtOSD3wcDBKb+y2tM1ZOQGTgWIYzzY/TivZvGAaQWMW
K1XuLscr89pqw9tIXrJYrlN0lLhtiddXc9FZwL0AE3LtQQQFUjMgc4JtAHZ13PR7
DNYWFGcF+oQh3QIDAQABAmB5VGGLHFxnmjRveIhMnVWJQ6yOzmmRC284fship3rd
12yiIczHCD9LJ3f43pWjf8s9G6NmpbYO5PZlhGMI9Sc0CM+ZOSfzQgaa2T/Jp7ol
GxgA+PIR+I8uvA7+3iNtIfUCMQDZfEaqFxl2X/Vx7HDIlkioN05Rq4JWIR0e60Ot
vZ6Swy1naK55hCmKJ4GqR+rzRu8CMQDYpTCaied2MJbAbdAf1jKB2GjqHroCCDI0
rqu4Nkp2vW/vt7k6C/I9MBTTkphtA/MCMQCkkKsPzUbREa8xsw1sWntD/gVfOv32
mjvkx3SApPi2aMWUd9t9y4l7JoWxCZY42zUCMDHXBvB2i1ZIZVYNrVS1rVbr128M
9JPlGM+/42d0rfrVJ/xJEwbEmszy08e8sO5bewIwaPvjrQG6sepZ3gSWcL+8Lg5f
dX2fJEbvJjNUFOgzqvmqaPujOR6nvhArLc/ZViDx
-----END RSA PRIVATE KEY-----

$ rsa.public
-----BEGIN PUBLIC KEY-----
MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALgNMgOmSOtOSD3wcDBKb+y2tM1ZOQGT
gWIYzzY/TivZvGAaQWMWK1XuLscr89pqw9tIXrJYrlN0lLhtiddXc9FZwL0AE3Lt
QQQFUjMgc4JtAHZ13PR7DNYWFGcF+oQh3QIDAQAB

여기서 rsa.public에 있는 내용이 DNS TXT record의 p value로서 사용되게 된다. 아래 예를 참조.

k=rsa; t=y; p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALgNMgOmSOtOSD3wcDBKb+y2tM1ZOQGT
gWIYzzY/TivZvGAaQWMWK1XuLscr89pqw9tIXrJYrlN0lLhtiddXc9FZwL0AE3Lt
QQQFUjMgc4JtAHZ13PR7DNYWFGcF+oQh3QIDAQAB;

여기서 주의 할 점은 해당 Key는 한줄짜리 임으로 copy & paste 시 line 넘김이 발생하지 않도록 하는 것이다. 이것 때문에 문제가 생긴 적이 있었다.

그럼, Postfix에 dkimproxy를 적용 해 보자. Inbound, outbound에서 설정한 내용대로 Receive & Send가 적절하게 이루어 질 수 있도록 Postfix의 master.cf를 설정해야 한다.

## inbound and outbound filter
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100)
# ==========================================================================
smtp inet n - n - - smtpd
-o smtpd_proxy_filter=127.0.0.1:10024
-o smtpd_client_connection_count_limit=10
dksign-in unix - - n - 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
127.0.0.1:10026 inet n - n - - smtpd
-o content_filter=
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=
-o mynetworks=127.0.0.0/8
-o receive_override_options=no_unknown_recipient_checks
submission inet n - n - - smtpd
-o smtpd_etrn_restrictions=reject
-o smtpd_sasl_auth_enable=yes
-o content_filter=dksign:[127.0.0.1]:10027
-o receive_override_options=no_address_mappings
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
# -o smtpd_enforce_tls=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
smtps inet n - n - - smtpd
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
dksign unix - - n - 10 smtp
-o smtp_send_xforward_command=yes
-o smtp_discard_ehlo_keywords=8bitmime
127.0.0.1:10028 inet n - n - 10 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

여기서 일반 Mail을 보낼 때는 Port 25를 사용하면 되고 Domain Key가 Signed 된 Mail을 보낼 때는 submission Port 587을 사용하면 된다.

$ more /etc/services | grep 587
submission 587/tcp msa # mail message submission
submission 587/udp msa # mail message submission

8. Configure Domain Key on DNS

DNS는 아래와 같이 TXT 형식으로 저장을 하고,

[Zone File]
_domainkey TXT "t=n; o=-;"
[select name]._domainkey TXT "k=rsa; t=n; p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALgNMgOmSOtOSD3wcDBKb+y2tM1ZOQGT
gWIYzzY/TivZvGAaQWMWK1XuLscr89pqw9tIXrJYrlN0lLhtiddXc9FZwL0AE3Lt
QQQFUjMgc4JtAHZ13PR7DNYWFGcF+oQh3QIDAQAB;"

dig로 확인을 하면 아래와 같다.

$ dig _domainkey.[domain.com] txt
..
;; ANSWER SECTION:
_domainkey.[domain.com]. 300 IN TXT "t=y\; o=-\;"

$ dig [select name]._domainkey.[domain.com] txt
..
;; ANSWER SECTION:
_domainkey.[domain.com]. 300 IN TXT "k=rsa; t=y; p=MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALgNMgOmSOtOSD3wcDBKb+y2tM1ZOQGT
gWIYzzY/TivZvGAaQWMWK1XuLscr89pqw9tIXrJYrlN0lLhtiddXc9FZwL0AE3Lt
QQQFUjMgc4JtAHZ13PR7DNYWFGcF+oQh3QIDAQAB;"

또한, 아래 Site에서 정상 등록 여부 확인이 가능하다.
http://domainkeys.sourceforge.net/policycheck.html
http://domainkeys.sourceforge.net/selectorcheck.html

9. Start Postfix with Dovecot, DKIM, DomainKey, MySQL

각 Daemon의 기동은 간단하고, netstat를 통해 각 Port를 확인 할 수 있다.

$ /etc/init.d/dovecot start
$ /etc/init.d/dkimproxy start
$ /etc/init.d/postfix start
$ /etc/init.d/httpd start

$ netstat -an
..
tcp 0 0 127.0.0.1:10025 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:10026 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:587 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:10027 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:10028 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN
tcp 0 0 :::993 :::* LISTEN
tcp 0 0 :::995 :::* LISTEN
tcp 0 0 :::80 :::* LISTEN

각 Port의 대한 정보는 아래와 같다.

DKIM, DomainKey Processing => Local : 10025, 10026, 10027, 10028
DKIM, DomainKey Signed SMTP => 587
Normal SMTP => 25
POP3S => 995
IMAPS => 993
HTTP(WebMail) => 80 (Web Mail 서버가 설치되고, 기동 되었다면)

다음 시간에 위 Mail Server상에 Spam Filtering 설치 해 보도록 하겠습니다.

Linux #7 : Dos / DDos 공격에 대한 대처법. #1

수 많은 Site들이 Dos / DDos 공격으로 인해 피해를 보고 있다. 필자도 일본에선 별로 느껴보진 못했지만, 미국에 와서 Dos / Ddos 공격을 여러번 경험하고 실제 지금도 항상 Hacker들을 경계하고 있습니다. 그럼, Dos / DDos 공격이란 무엇인지 먼저 알아보죠.

Dos 공격이란 Daniel of Service attacks의 약자로 Network의 대역을 소비하게 만들거나, 대량의 request를 Server에 보내 connection을 증가시켜 Server를 사용 불능 상태로 만들거나, 더불어 Password를 알아내기 위해 여러 방법을 통해 접근을 시도하는 Brute Force 공격등도 행해지는데,  공격 주체가 1대의 host라면 특정가능하여 해당 IP를 제한하면 되지만 (여기까지가 Dos), 이 공격 주체가 특정할 수 없는 다수일 경우 DDos (Distributed Denial of Service) 공격이라하고, 이를 방지하기 위해선 다른 방법이 필요합니다.

그럼, Dos / DDos와 관련 된 여러 공격에 대한 대처 방법에 대해서 알아보도록 하죠.

SYN Flooding 공격

기본적으로 TCP Packet은 3 Way Handshaking에 의해 초기 Session이 이루어지게 되는데, 즉 SYN -> SYN / ACK -> ACK로 완료가 이루어져야 하지만 정상적인 Session으로 종료가 가능하지만, SYN Packet을 요청하고 SYN / ACK Packet을 Server에서 보내려고 할 때 보내려는 주소가 무의미한 주소여서 Server가 해당 Session을 대기상태 (SYN_RECV)에 놓게 되어 Server 대기 Queue의 증가로 인한 Server Resource가 증가로 정상동작을 할 수 없게 만드는 공격을 말합니다.

확인은 일단 Server가 비정상적으로 느려지고, 접속히 원활하지 않게 되며 netstat 명령어를 통해 SYN_RECV의 수를 체크 했을 시 SYN_RECV의 대기 Queue가 많이 증가하였다고 의심해 볼 필요가 있습니다.

# netstat -an | grep SYN_RECV
...
tcp        0      0 ::ffff:xxx.xxx.xxx.xxx:80    ::ffff:xxx.xxx.xxx.xxx:28719  SYN_RECV
tcp        0      0 ::ffff:xxx.xxx.xxx.xxx:80     ::ffff:xxx.xxx.xxx.xxx:61544   SYN_RECV
...

대처 방법은 아래와 같이 3가지가 있습니다.
첫번째는 Backlog size를 증가시켜 주는 방법으로 Default값에서 1024 ~ 그 이상의 값을 설정합니다.

# SYN Flooding attack block (need to increase backlog size)
net.ipv4.tcp_max_syn_backlog=16000

이 설정을 하게 됨으로 얼마정도 버틸 수 있게 되지만 지속적으로 공격을 당했을 경우 queue가 꽉차게 됨으로 두번째인 SYN Cookies도 활성화를 시켜야 합니다. SYN Cookies가 활성화가 되면 ACK 중에 포함된 확인 응답 번호를 계산하여, 올바른 Session인지를 확인하게 됩니다. Session이 올바른 경우 처리를 행하고, 그렇지 않은 경우의 Connection에 대해서는 System Resource를 낭비하는 걸 막을 수 있습니다.

# Activate SYN Cookies
net.ipv4.tcp_syncookies=1

1인 경우가 활성화된 상태이고, 0인 경우가 비활성 상태입니다. 이 두개의 값은

# echo '16000' > /proc/sys/net/ipv4/tcp_max_syn_backlog
# echo '1' > /proc/sys/net/ipv4/tcp_syncookies

와 같이 설정가능하고 또한,

# sysctl -w net.ipv4.tcp_max_syn_backlog=16000
# sysctl -w net.ipv4.tcp_syncookies=1

또, /etc/sysctl.conf에 추가를 한 뒤

# sysctl -p /etc/sysctl.conf

로도 가능합니다. /etc/sysctl.conf에 넣었을 경우 System Reboot시 자동으로 등록이 되게 됩니다. SYN Cookies를 활성화 하면 확인 응답번호의 계산에 따른 Packet처리에서 Overhead가 발생하게 되고, Packet 량에 따라 CPU 사용량이 증가하여 Bottleneck이 발생 할 가능성이 있습니다.
그리고, 마지막으로 Iptable을 이용하여 비정상적은 IP를 block하면 됩니다.

운영상의 편의를 위해 아래와 같이 Script를 작성해 보았습니다.

### Block_SYN_Flooding.sh
#!/bin/sh
# ================================================================
# -script name
# Block_SYN_Flooding.sh
# -script desc
# Block DDos Attack through the SYN Flooding
# -how to use
# ./Block_SYN_Flooding.sh $1 $2
# $1 Type
# $2 List File
# -NOTE
#
# -directory
# /home/scripts/security
# -create date
# 2010/06/22 NHN USA)Jeff KIM
# -Update
#
# ----------------------------------------------------------------
# -internal variable
# BASE Common lib directory
# LIB_ENV Location of env file
# FUNCTION Location of function file
# SCRIPT_NAME Script name
# APP_VERSion Application & History Number
# ================================================================
BASE=/home/scripts
SCRIPT_NAME="Block_SYN_Flooding.sh"
LIB_ENV=$BASE/setenv.conf
FUNCTION=$BASE/function
APP_VERSION=$BASE/app_version
. $LIB_ENV
. $FUNCTION
. $APP_VERSION
##################################################################
# start script
Startscript
Whoamiroot
MENU_INIT="[ start | stop | start_pp | stop_pp ]"
function Read_Iplist_File {
if [ "${IPS}" == "" ];then
Printmsg "ERROR" "Please, Input IP List File Path!"
Endscript
else
if test -r ${IPS};then
iRC=$?
if [ ${iRC} -eq 0 ];then
chkFile=1
else
chkFile=0
Printmsg "ERROR" "${2} File is not existed."
fi
fi
fi
}
if [ "${1}" == "" ];then
Printmsg "ERROR" "Please, Input Type (Usage: ${MENU_INIT} ) correctly!"
Endscript
else
TYPE=${1}
IPS=${2}
case ${TYPE} in
start)
PRC_NAME="Start IPtables to block ${2}.........."
Startprocess
Read_Iplist_File
if [ "${chkFile}" == "1" ];then
for ipaddress in `cat ${2}`;do
Printmsg "NOTICE" "/sbin/iptables -A INPUT -s ${ipaddress} -j DROP"
/sbin/iptables -A INPUT -s ${ipaddress} -j DROP
done
/sbin/iptables -L
fi
/etc/init.d/iptables save
/etc/init.d/iptables restart
Endprocess
;;
stop)
PRC_NAME="Stop IPTables to remove ${2}.........."
Startprocess
Read_Iplist_File
if [ "${chkFile}" == "1" ];then
for ipaddress in `cat ${2}`;do
Printmsg "NOTICE" "/sbin/iptables -D INPUT -s ${ipaddress} -j DROP"
/sbin/iptables -D INPUT -s ${ipaddress} -j DROP
done
/sbin/iptables -L
fi
/etc/init.d/iptables save
/etc/init.d/iptables stop
Endprocess
;;
start_pp)
PRC_NAME="Start IPtables to block SYN,FIN,RST from ALL IPs.........."
Startprocess
Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP"
/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP"
/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP"
/sbin/iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP"
/sbin/iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
/sbin/iptables -L
/etc/init.d/iptables save
/etc/init.d/iptables restart
Endprocess
;;
stop_pp)
PRC_NAME="Stop IPTables to remove SYN,FIN,RST from ALL IPs.........."
Startprocess
Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP"
/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP"
/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP"
/sbin/iptables -D INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP"
/sbin/iptables -D INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
/sbin/iptables -L
/etc/init.d/iptables save
/etc/init.d/iptables stop
Endprocess
;;
*)
Printmsg "ERROR" "Please, Input Type (Usage: ${MENU_INIT} ) correctly!"
Endscript
;;
esac
fi
Endscript

위 Script에서 4개의 선택 메뉴가 있는데, MENU_INIT=”[ start | stop | start_pp | stop_pp ]”
start / stop은 Block_SYN_Flooding.sh start [IP_List_file]과 같이 IP List File을 지정하면 각 IP를 Block 할 수 있도록 되어 있습니다.
Stop을 하면 해제가 되구요. start_pp / stop_pp에 대해서는 IP가 너무많아 대응하기 힘든 경우 SYN Flooding과 관련 된 것들을 Iptable에서 몽땅 Drop 해버리는 겁니다. 실제 Web Service에서 문제없이 사용하고 있습니다. 그러나, 제가 다른 서비스에 대해서는 보장 못 해드립니다. ^ ^;

SYN Flooding 공격에 대한 대처법은 여기까지입니다.  Linux : Dos / DDos 공격에 대한 대처법. #2에서는 Smurf 공격 및 Spoofing 대처법 등에 대해서 다루도록 하겠습니다.

Linux #6 : Implement Postfix with Dovecot, DKIM, DomainKey, MySQL. #1

나는 이전엔 Q-Mail Platform을 상당히 선호하고 있었는데, Postfix로 전향을 하게 된 계기는 DSN(Delivery Status Notification) 기능의 구현을 위해서 였다. Q-Mail을 통한 구현을 여기저기 찾아 보았는데, 결국 허탕을 치고 모든 E-Mail Platform을 Postfix로 바꾸게 되었다. 일본에 있을 때도 그렇고 쭉 5년 넘게 Q-Mail을 다루었던 경험으로 인해 처음엔 Postfix가 어찌 그리 귀찮고 낯설었는지.. 시간이 지난 지금 Postfix가 이젠 Q-Mail보다 좋은 느낌이다.

1. Installation of Postfix

간단하게 Source RPM으로 설치해도 문제가 없으므로, Lastest Version은 아래에서 구하면 된다.
http://ftp.wl0.org/official/2.7/SRPMS/

rpm -ivh [PostFix SRPM]

* 참고로 필자가 사용한 Version은 postfix-2.3.3-2.1.el5_2.src.rpm, 또한 2.3.3 version을 위한 Patch가 있으므로 postfix-2.3.3-vda.patch.gz와 postfix-sleep.patch도 download 받는다.

# cp postfix-2.3.3-vda.patch.gz /usr/src/redhat/SOURCES
# cd /usr/src/redhat/SOURCES
# gunzip postfix-2.3.3-vda.patch.gz
# cp postfix-sleep.patch /usr/src/redhat/SOURCES
# cd /usr/src/redhat/SPECS/
# cp postfix.spec postfix.spec.old

후 MySQL과 Dovecot을 사용하기 위해 Parameter를 변경 해 줄 필요가 있다.

# cd /usr/local/redhat/SPECS/
# vi postfix.spec
..
%define with_mysql  0  ->  1
%define with_dovecot 0 -> 1
..

또한, Patch를 위해 아래와 같은 정보를 추가 해 준다.
(예제에서의 84, 85, 141, 142는 Line Number, Patch Number인 Patch11, 12는 실제와 다를 수 있다.)

84 Patch11: postfix-2.3.3-vda.patch
85 Patch12: postfix-sleep.patch

141 %patch11 -p1 -b .vda
142 %patch12 -p1 -b .sleep

추가적으로 RPMBUILD 및 Authentication 관련 Libary가 필요 하게 되는데 System 상에 설치되어 있지 않을 수도있으므로 아래 command를 실행 해 준다.

# yum -y install pkgconfig rpm-build db4-devel zlib-devel openldap-devel cyrus-sasl-devel pcre-devel mysql-devel openssl-devel

RPMBUILD를 통해 rpm 파일을 생성 후, 설치를 한다.

# rpmbuild -ba postfix.spec
# cd /usr/src/redhat/RPMS
# rpm -Uhv /usr/src/redhat/RPMS/x86_64/[PostFix RPM] (if x86_64)
# rpm -Uhv /usr/src/redhat/RPMS/i386/[PostFix RPM] (if i386)

YUM을 통해 Postfix가 Updating 되지 않도록 아래와 같이 설정을 한다.

# vi /etc/yum.conf
..
exclude=postfix*
..

Booting시 자동 기동을 위해

# chkconfig postfix on

해주면 Postfix 설치는 일단 끝이난다.

2. Installation of Dovecot

Dovecot은 POP3 및 IMAP Server로 Postfix와 같이 구성할 수 있다. Yum을 사용할 수 있다면 설치는 간단하다.

# yum install dovecot
# chkconfig dovecot on

TLS통한 인증 접근을 구성(POP3S, IMAPS)하고 싶다면 인증 Key를 아래와 같이 생성한다.

# cd /etc/pki/tls/certs
# make [Key_Name].pem
# openssl x509 -in [Key_Name].pem -outform DER -out [Key_Name].der

Additional : 필요한 경우 아래 Library도 설치

# yum -y install cyrus-sasl-md5 cyrus_sasl_sql cyrus-sasl-plain
# yum install gnutls

3. Installation of MySQL DB

MySQL은 Account 관련 정보 / Virtual Domain 관련 정보 / Domain 관련 정보를 관리하기 위함이다.

# version : 5.0.77 (필자가 사용한 Version임)
# ./configure --prefix=/usr/local/mysql --enable-thread-safe-client --with-innodb
# make;make install
# cd /usr/local/mysql
# cp /usr/local/mysql/share/mysql/my-[select].cnf /usr/local/mysql/my.cnf

* 환경에 맞춰 my.cnf file을 선택 설정을 한다.

4. Intallation of Apache with PHP

Apache Web Server는 Web-Mail Service를 제공하거나, Postfix Admin을 사용하고 싶은 경우 설치하면 된다.

# yum install httpd
# chkconfig --level 235 httpd off
** Apache도 환경에 맞게 설정을 한다.

5. Making and checking each configuration files for Postfix and dovecot

Postfix configuration files들은 /etc/postix Directory안에 있다.

Edit /etc/postfix/master.cf
Edit /etc/postfix/main.cf

Dovecot configuration files :
Edit /etc/dovecot.conf
Edit /etc/dovecot-mysql.conf   (MySQL DB의 Account 정보를 획득을 위한 파일)

Postfix with MySQL configuration files :
Edit /etc/postfix/mysql_virtual_alias_maps.cf
Edit /etc/postfix/mysql_virtual_domains_maps.cf
Edit /etc/postfix/mysql_virtual_mailbox_maps.cf

위 3 files은 Postfix의 virtual alias, domains, mailbox 정보를 관리하기 위한 파일이다.

설정과 관련 된 자세한 내용은 Implement Postfix with Dovecot, DKIM, DomainKey, MySQL. #2에서..