Tag: postfix

zen.spamhaus.org

이전까지 스팸 호스트 막으려고 spamlist.or.kr와 sbl.spamhaus.org를 두가지 사용하고 있었다. 그런데 점점 스팸이 늘어나고, 그 패턴을 보니 내용으로 검사하기엔 힘들다. 헤더에 특별한 내용도 없고, 제목도 항상 바뀌고, 딸랑 링크 한줄에 그 링크 주소도 계속 바뀌고, 링크 클릭하면 한곳으로 redirect되고... 역시나 대부분 러시아 산이다.

무식하게 access, body_check에 넣으려다 다시 spamhaus의 rbl을 또 공부해봤는데... sbl뿐문 아니고 다른 것도 많다. 또 그때 했었던 서버스 말고 다른 서비스도 많이 늘었다. 그래서 바꿨더니 아직 하루도 되지 않았지만 상당한 성과가 있네..

하는 김에 더 많이 넣어버렸다.

smtpd_client_restrictions = foo bar...
        reject_rbl_client spamlist.or.kr
        reject_rbl_client zen.spamhaus.org
        reject_rbl_client cbl.abuseat.org
        reject_rbl_client dnsbl.sorbs.net

5시간 정도 적용한 결과다..

$ grep spamhaus /var/log/maillog | wc -l
      28
$ grep spamlist /var/log/maillog | wc -l
      78
 grep abuseat /var/log/maillog | wc -l
       2
$ grep sorbs /var/log/maillog | wc -l
       3

(하루 지나니 보니 192, 99, 2, 7로 변했음) 이게 많은지 모르면.. 이 전날 통계를 보면...

$ bzgrep -E -o  'blocked using ([a-zA-Z\.]+)' /var/log/maillog.1.bz2 | sort | uniq -c
 65 blocked using spamlist.or.kr

대략 괜찮은 듯..


postfix mail archiving

메일을 보낼때 검토받아서 보내는 부분은 보류되었습니다. 역시나 하나가 끝나면 하나가 다시 오는법... 검토는 하지않지만 주고받은 메일을 archiving하는 것으로 선회했습니다. moderation은 영 정말로 껄그럽지만, archiving은 그보다는 좀 낫네요.

가장 간단한 방법은 모든 always_bcc으로 모든 메일 메시지를 특정 계정으로 보내면 됩니다. archiving용 계정 하나 만들고 모두 보내면 되니깐 편하겠지요.

그런데 모든 메일입니다. archiving에 필요없는 메시지도 가는 것이지요. 뭐 메일 root@로 날아오는 관리일이나 각종 보안 검사메일, cron 명령 메일등등 이런것을 굿이 archiving할 필요가 없는데 말입니다. 그래서 가장 간단하지만 always_bcc를 사용하지 않기로하고, 그런 다음에 눈에 들어온게 content_filter입니다.

content_filter를 만들어서 거기서 메일을 저장하면 되겠지요. 간단한 내용이지만 이거 메커니즘 이해하느라 상당히 고생했습니다. postfix 샘플대로 하니 안됩니다. 계속 loop를 돌아서 에러를... ㅡㅜ;

설정

archiver 필터를 archiver라고 하면

main.cf:

content_filter = archiver

이제 모든 메시지는 archiver 서비스를 통해서 전달됩니다.

master.cf:

archiver   unix -       n       n       -       10      pipe
   flags=Rq user=whitekid null_sender=
   argv=/usr/local/bin/python /home/whitekid/work/postfix-archiver/archiver.py 127.0.0.1 20026 ${sender} ${recipient}

127.0.0.1:20026 inet n - - - - smtpd
        -o content_filter=
        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
        -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
  • archiver 서비스에서 필터를 실행합니다.
  • 그리고 파라미터들은 필터를 처리하고 난 다음의 action들을 지정하는데 archiver 필터를 처리한 후 127.0.0.1:20026 서비스를 호출하기 위해 파라미터를 전달해줍니다. 이 파라미터는 archiver.py에서 받아서 다음 서비스에 전달합니다. 아래 서비스가 smtpd이므로 smtp로 메일을 보내는 것 처럼 합니다.

content_filter --(pipe)--> archiver --(smtp)--> 127.0.0.1:20026 의 순서로 메시지가 전달됩니다.

archiver.py

이제 archiver.py의 대략적인 소스를 보면...

import sys
import smtplib
import email

EX_OK   = 0
EX_TEMPFAIL = 75
EX_UNAVAILABLE = 69

def main():
    host, port, sender, recipient  = sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4:]
    msg = email.message_from_string(sys.stdin.read())
    msg['X-Archive-Status'] = 'Archived'

    server = smtplib.SMTP(host, port)
    for r in recipient:
        server.sendmail(sender, r, msg.as_string())
    server.quit()

if __name__ == '__main__':
    try:
        main()
        sys.exit(EX_OK)
    except Exception, e:
        print e
        sys.exit(EX_TEMPFAIL)

내용은 아주 간단합니다. stdin에서 받은 내용에서 약간의 메시지 조작을 하고, 그 메시지를 다시 파라미터로 받은 smtp로 연결해서 넘김니다. 자 이렇게고 maillog를 보면

Aug 24 14:23:39 freebsd postfix/qmgr[22880]: 537F51705A: from=<root@freebsd.woosum.net>, size=1013, nrcpt=1 (queue active)
Aug 24 14:23:39 freebsd postfix/pipe[22890]: 3D3E217030: to=<whitekid@freebsd.woosum.net>, relay=archiver, delay=0.1, delays=0.01/0.01/0/0.09, dsn=2.0.0, status=sent (delivered via archiver service)
Aug 24 14:23:39 freebsd postfix/qmgr[22880]: 3D3E217030: removed
Aug 24 14:23:39 freebsd postfix/local[22894]: 537F51705A: to=<whitekid@freebsd.woosum.net>, relay=local, delay=0.01, delays=0.01/0/0/0, dsn=2.0.0, status=sent (delivered to mailbox)
Aug 24 14:23:39 freebsd postfix/qmgr[22880]: 537F51705A: removed

음 archiver 서비스를 이용해서 mailbox가지 간게 보이나요? 보이면 OK!

amavisd-new와 공존

amavisd도 content_filter를 사용합니다. 그런데 이게 2개가 적용되지 않습니다. content_filter는 1개인것이고, 이게 chain으로 역여서 돌아가는 것이지요. 이렇게 chain에 넣어야하는데, 그건 간단하게...

amavis unix - - - - 2 smtp
   -o smtp_data_done_timeout=1200
   -o smtp_send_xforward_command=yes

127.0.0.1:10025 inet n - - - - smtpd
-o content_filter=archiver

처럼 content_filter에 archiver를 넣어주면 됩니다.


postfix outgoing mail moderation

이메일 서버를 postfix를 사용하고 있습니다. 이런 중에 외부로 나가는 메일을 허락받고 보내는 솔루션을 검토할 필요가 생겼습니다. MS Outtlook에서는 email modeeration이라는 기능으로 이무 구현이 되어있더군요. 그런데 postfix용으로 이런게 있는지 뒤져보는데, 역시나 없습니다. 물론 하고자하는 일이 영 껄그런기능이라 그런지 그런 질문에 부정적인 답볃도 많구요. CCTV를 설치하고 감시하는게 어떠냐는 등등.. ^^;

어찌되었던간 postfix로 간단하게 구현했습니다.

smtpd_data_restriction에서 check_policy_service를 이용해서 처리하고, 검토되어야하는 메일일 경우에는 메시지를 HOLD하고 moderator에게 확인 메시지를 보내고 그 결과에 따라서 처리합니다.

main.cf:

smtpd_data_restrictions = check_policy_service unix:private/moderation

smtp_data_restriction을 이용한건 다른 곳에서는 해당 메시지의 queue id가 넘어오지 않아서 입니다.

master.cf:

moderation         unix  -      n       n       -       0       spawn
 user=nobody argv=/usr/local/bin/python /home/whitekid/work/postfix-moderation/postfix-moderation.py

이렇게 주면 smtp에서 데이터가 넘어올때 master.cf에 설정된 moderation 서버스에 의해서 메시지가 처리됩니다.

posfix-moderation.py

postfix-moderation.py는 smtpd policy 서비스를 하는 것으로 자세한 프로토콜은 Postfix 웹사이트에 있습니다. sender, recipient를 읽어서 외부로 나가는지 확인하고 정상적이면 action=dunno를 아니면 action=hold를 리턴하면 됩니다. 소스는 아주 간단합니다. 약 주석을 포함해서도 100줄 정도 되는군요

여기서 hold된 상태들은 PostgreSQL에 관리된 DB에 들어갑니다(간단한 것이라 SQLite등 보다 가벼운 것으로 할 수 있지만, PostgreSQL이 설치되 사용중이라 그걸로 했습니다.).

cron.py:

이렇게 DB에 메시지들이 설정되면 cron.py에서 메일 보내가나 큐를 조작하는 일들을 합니다. 1분 간격으로 실행되면서 DB를 검토해서 새로운 검토가 필요한 메시지면 moderator에 메시지를 보내고, moderator가 allow 또는 deny한 것들을 처리힙니다.

큐에서 postcat -bh [queue_file]을 이용해서 큐의 메시지를 이메일 메시지로 얻어오고 이 정보로 확인하죠.

email.message_from_file(os.popen('/usr/local/sbin/postcat -bh /var/spool/postfix/hold/%s' % queue_id))

또 문제되었던게 원본 메시지를 포함하여 moderator에게 메일을 보내는 건데..

    def attach_message(msg, attach):
        base = MIMEBase('message', 'rfc822')
        base.attach(attach)
        msg.attach(base)

    msg = MIMEMultipart()
    msg['From'] = 'moderation@' + MY_DOMAIN
    msg['To'] = moderator
    msg['Subject'] = u'[메일 전송 확인] %s' % subject
    msg.attach(MIMEText(message.encode('utf-8'), _subtype='html', _charset='utf-8'))

    attach_message(msg, orig_message)
    server = smtplib.SMTP()
    server.connect('localhost')
    server.sendmail('moderation@' + MY_DOMAIN, moderator, msg.as_string())
    server.close()

이런식으로 보냅니다.

메일 큐 조작은 postsuper를 이용하면 됩니다. -H 옵션은 홀드된 메시지를 다시 보내는 것이고, -d 옵션은 큐를 지웁니다. 따라서 allow된 것은 release하고 postqueue -i [queue_id]로 바로 보내도록 설정합니다.

cgi/moderate.py

cron에서 moderator에게 메일을 보내면 여기에는 allow/deny 링크가 포함됩니다. moderator는 이 링크를 이용해서 메일을 허락 또는 거부하죠.

물론 여기에서는 DB를 조작하는 것 빼고는 아무것도 없습니다. 당연히 모든 작업은 cron.py에서 하니깐요.

Alias /moderate "/home/whitekid/work/postfix-moderation/cgi/moderate.cgi"

<Directory "/home/whitekid/work/postfix-moderation/cgi">
 AddHandler cgi-script .cgi
 Options NONE +ExecCGI
 Order allow,deny
 Allow from all
</Directory>

정신없이 대충 적어서 무슨 말인지 도저히 저도 모르겠군요. ^^; 소스요? 그건 좀 소스정리좀 하구요...


  • Copyright © 1996-2010 Your wish is my command. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress