제 목 : procmail과 perl로 메일수신로그를 DB로. v2
작성자 : 좋은진호(truefeel, http://coffeenix.net/ )
작성일 : 2004.1.15(목)
수정일 : 2004.1.18(일) DB 스키마 수정, mail_log.pl에서 작은따옴표(')처리
http://coffeenix.net/board_view.php?bd_code=172
업데이트 : 2004.9.10(금) 메일 필터링 여부 체크 필드 추가
메일 쿼터(파일시스템 쿼터나 milterAPI를 이용하지 않고 순수 procmail+perl로만으로
구현할려는 진보적인(?) 쿼터)를 위해 만드는 과정에서 수신 정보가 필요했고, 이
수신정보를 DB로 남겨도 좋겠다는 생각을 하게되었다.
즉, 단순히 곁다리로 나온 것이지만 쓸만하다 싶어(?) 정리하여 소개한다.
1. 들어가기
1) DB로 남기면 뭐가 좋은가?
- 통계처리가 쉽게 가능하다.
월 몇통의 메일을 받는 서버인지 COUNT(*)만으로 쉽게 확인할 수 있다.
- 수신자별로 메일 수신 메일 통수 통계를 볼 수 있다. (수신자별 GROUP BY 로 가능)
- 메일 제목을 통해 필터링할 스팸 메일 설정을 쉽게 도와준다.
- SUM(MAIL_SIZE)를 이용하면 월별 메일 수신용량(헤더 제외)을 확인할 수 있다.
- 메일 필터링 여부를 DB에 저장하여 필터링 비율을 확인할 수 있다.
2) 어떤 로그를 남기는가?
- 메일 송신자 메일주소와 이름
- 메일 수신자 ID
- 메일 제목
- 본문 길이 (단위 byte)
- 송신한 일시 (정확히는 DB에 로그를 남긴 일시이나 시간상의 차이는 거의 없다.)
- 필터링 여부 (값이 0이면 필터링되지 않은 메일이다.)
3) 과정을 이해해보자.
sendmail, qmail 등에서 메일을 수신하면 MDA인 procmail로 넘겨준다.
-> /etc/procmailrc 에서 메일 제목 디코딩을 한다. (procmail에서)
-> 송신자, 수신자, 제목, 길이 등을 얻어내어 변수에 저장한다. (procmail에서)
-> 얻어낸 값을 mail_log.pl 로 넘겨준다. (procmail에서)
-> DB로 저장한다. (mail_log.pl에서)
-> 필터링 여부를 체크한다. (mail_filterchk.pl에서)
2. 요구 사항
1) DB는 MySQL을 사용한다.
오라클도 상관없다. 그게 바로 Perl DBI모듈의 장점이다.
2) Perl과 Perl DBI, DBD 모듈이 필요하다.
펄의 저장창고라 불리는 CPAN( http://www.cpan.org/modules/ )에서
DBI, DBD 모듈을 구할 수 있다.
참고로 레드햇 9에서는 rpm으로 제공된다.
http://www.cpan.org/authors/id/T/TI/TIMB/DBI-1.43.tar.gz
http://www.cpan.org/authors/id/J/JW/JWIED/DBD-mysql-2.1028.tar.gz
먼저 DBI을 다음과 같은 과정으로 설치하고 똑깥이 DBD-mysql도 설치하면 된다.
기존에 설치된 것을 사용했으므로, 위에 링크한 소스로 컴파일했을 때 문제가
발생하는지에 대해서는 확인해줄 수 없다.
3) 메일 제목의 한글 디코딩을 위해서는 hcode 프로그램이 필요하다. (옵션)
ftp://ftp.kaist.ac.kr/pub/hangul/code/hcode/
ftp://ftp.kreonet.re.kr/pub/hangul/cair-archive/code/hcode/
에서 구할 수 있으며, make 만으로 컴파일할 수 있다.
3. procmail 설정
[ /etc/procmailrc 설정 중 디코딩 부분만 ]
: 는 처리할 조건의 시작을 의미하며 recipes라 불린다.
위에서 헤더에서 각각의 조건을 찾아 맞지 않으면 다음 조건(E = else if로 이해하면 됨)을
처리하는 형태로 되어 있다.
이런 과정을 거쳐 Base64나 QP로 인코딩된 메일 헤더를 디코딩하게 된다.
이제 include된 mail_log.rc과 mail_filterchk.rc 를 살펴보자.
[ /etc/procmail/mail_log.rc ]
* 다운로드 : http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.rc
각각의 조건에 의해 수신자, 송신자, 메일제목, 본문 길이를 얻어낸다.
그 얻어진 값은 변수에 저장되어 mail_log.pl 프로그램에 인수로 넘겨주게 된다.
어떻게 매칭이 되어 FROM, TO, SUBJECT, LENGTH 변수에 값이 들어가는지 궁금하면
procmailrc 에 VERBOSE=yes 로 하면 쉽게 확인할 수 있을 것이다.
[ /etc/procmail/mail_log.rc ]
* 다운로드 : http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.rc
4. DB 스키마
MAIL_LOG DB 스키마이다.
* 다운로드 http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.sql
5. 로깅 및 필터링 여부 체크 프로그램
다음은 로그를 DB에 저장하는 펄 소스이다.
[ /etc/procmail/db_lib.pl ]
[ /etc/procmail/mail_log.pl ]
* Syntax Highlight된 소스 보기 :
http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.pl.html
http://coffeenix.net/truefeel/files/mail_log_v2/db_lib.pl.html
* 다운로드
http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.pl.txt
http://coffeenix.net/truefeel/files/mail_log_v2/db_lib.pl.txt
간단히 살펴보자.
넘겨온 인수중에서 송신자 정보는 이름과 메일주소로 나눈다. 물론 이름이 없어도 문제없이
처리한다. 그리고 DB에 저장하고 종료한다.
$DEBUG = 1 으로 지정하면 디버깅에 유용하다. 넘겨받은 인수를 /tmp/maillog.debug에 저장 한다.
db_connect() 함수에서 $szDBName, $szDBUser, $szDBPasswd을 설정해주어야 한다.
만약 Oracle DB이라면 'DBI:mysql' 대신 'DBI:Oracle'을 써주면 된다.
주의할 것은 DB 비밀번호도 있으니 파일 퍼미션을 700(rwx------)으로 해야한다.
[ /etc/procmail/mail_log.pl ]
* Syntax Highlight된 소스 보기 :
http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.pl.html
* 다운로드
http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.pl.txt
수신 메일에 대한 유일한 키값($FILTERCHK)을 넘겨받아서 필터링 되지 않은 메일임을
표시한다. (UPDATE문, MAIL_FILTERCHK = 0)
로그가 제대로 남았는지 확인해보자.

로그를 DB로 남겼을 때 어떻게 활용할 것인지 생각했는가?
그럼 지금 당장 시작해라!
6. 참고 자료
* Procmail Tips
http://pm-doc.sourceforge.net/pm-tips.html
* procmail에 관하여 (글 이상로)
http://trade.chonbuk.ac.kr/~leesl/procmail/index.html
* Short guide to DBI (The Perl Database Interface Module)
http://www.perl.com/pub/a/1999/10/DBI.html
작성자 : 좋은진호(truefeel, http://coffeenix.net/ )
작성일 : 2004.1.15(목)
수정일 : 2004.1.18(일) DB 스키마 수정, mail_log.pl에서 작은따옴표(')처리
http://coffeenix.net/board_view.php?bd_code=172
업데이트 : 2004.9.10(금) 메일 필터링 여부 체크 필드 추가
메일 쿼터(파일시스템 쿼터나 milterAPI를 이용하지 않고 순수 procmail+perl로만으로
구현할려는 진보적인(?) 쿼터)를 위해 만드는 과정에서 수신 정보가 필요했고, 이
수신정보를 DB로 남겨도 좋겠다는 생각을 하게되었다.
즉, 단순히 곁다리로 나온 것이지만 쓸만하다 싶어(?) 정리하여 소개한다.
1. 들어가기
1) DB로 남기면 뭐가 좋은가?
- 통계처리가 쉽게 가능하다.
월 몇통의 메일을 받는 서버인지 COUNT(*)만으로 쉽게 확인할 수 있다.
- 수신자별로 메일 수신 메일 통수 통계를 볼 수 있다. (수신자별 GROUP BY 로 가능)
- 메일 제목을 통해 필터링할 스팸 메일 설정을 쉽게 도와준다.
- SUM(MAIL_SIZE)를 이용하면 월별 메일 수신용량(헤더 제외)을 확인할 수 있다.
- 메일 필터링 여부를 DB에 저장하여 필터링 비율을 확인할 수 있다.
2) 어떤 로그를 남기는가?
- 메일 송신자 메일주소와 이름
- 메일 수신자 ID
- 메일 제목
- 본문 길이 (단위 byte)
- 송신한 일시 (정확히는 DB에 로그를 남긴 일시이나 시간상의 차이는 거의 없다.)
- 필터링 여부 (값이 0이면 필터링되지 않은 메일이다.)
3) 과정을 이해해보자.
sendmail, qmail 등에서 메일을 수신하면 MDA인 procmail로 넘겨준다.
-> /etc/procmailrc 에서 메일 제목 디코딩을 한다. (procmail에서)
-> 송신자, 수신자, 제목, 길이 등을 얻어내어 변수에 저장한다. (procmail에서)
-> 얻어낸 값을 mail_log.pl 로 넘겨준다. (procmail에서)
-> DB로 저장한다. (mail_log.pl에서)
-> 필터링 여부를 체크한다. (mail_filterchk.pl에서)
2. 요구 사항
1) DB는 MySQL을 사용한다.
오라클도 상관없다. 그게 바로 Perl DBI모듈의 장점이다.
2) Perl과 Perl DBI, DBD 모듈이 필요하다.
펄의 저장창고라 불리는 CPAN( http://www.cpan.org/modules/ )에서
DBI, DBD 모듈을 구할 수 있다.
참고로 레드햇 9에서는 rpm으로 제공된다.
http://www.cpan.org/authors/id/T/TI/TIMB/DBI-1.43.tar.gz
http://www.cpan.org/authors/id/J/JW/JWIED/DBD-mysql-2.1028.tar.gz
먼저 DBI을 다음과 같은 과정으로 설치하고 똑깥이 DBD-mysql도 설치하면 된다.
기존에 설치된 것을 사용했으므로, 위에 링크한 소스로 컴파일했을 때 문제가
발생하는지에 대해서는 확인해줄 수 없다.
# perl Makefile.PL # make # make test (꼭 할 필요는 없다. 정상 동작하는 것인지 확인하기 위한 용도. 예전에 설치했을 때 몇 개 오류가 발생했어도 실제 사용에는 문제없었다.) # make install |
3) 메일 제목의 한글 디코딩을 위해서는 hcode 프로그램이 필요하다. (옵션)
ftp://ftp.kaist.ac.kr/pub/hangul/code/hcode/
ftp://ftp.kreonet.re.kr/pub/hangul/cair-archive/code/hcode/
에서 구할 수 있으며, make 만으로 컴파일할 수 있다.
3. procmail 설정
[ /etc/procmailrc 설정 중 디코딩 부분만 ]
# 메일 헤더 디코딩 :0 fhw *^(Subject|From|Cc):.*=\?EUC-KR\?(B|Q)\? |formail -c | /usr/bin/hcode -dk -m :0 Efhw *^(Subject|From|Cc):.*=\?ks_c_5601-1987\?(B|Q)\? |formail -c | /usr/bin/hcode -dk -m :0 Efhw *^(Subject|From|Cc):.*=\?KSC5601\?(B|Q)\? |formail -c | /usr/bin/hcode -dk -m :0 Efhw *^(Subject|From|Cc):.*=\?ISO-8859-1\?(b|B|Q)\? |formail -c | /usr/bin/hcode -dk -m # 메일 수신로그를 DB로 저장 INCLUDERC=/etc/procmail/mail_log.rc # 이부분에 필터링 내용을 나열한다. # # 예) # # SPAM_LOG=/var/log/SPAM.log # :0 : # * ^Subject:.*(무료.*(교재|샘플|증정|홍삼)|샘플.*무료.*(배송|배포|제공)|자선전안내|기적.*영문법|명품.*(최저 # * *가|시계)) # $SPAM_LOG # 필터링 여부를 체크한다. (필터링이 안된 메일만 mail_filterchk.rc가 실행된다.) INCLUDERC=/etc/procmail/mail_filterchk.rc |
: 는 처리할 조건의 시작을 의미하며 recipes라 불린다.
위에서 헤더에서 각각의 조건을 찾아 맞지 않으면 다음 조건(E = else if로 이해하면 됨)을
처리하는 형태로 되어 있다.
이런 과정을 거쳐 Base64나 QP로 인코딩된 메일 헤더를 디코딩하게 된다.
이제 include된 mail_log.rc과 mail_filterchk.rc 를 살펴보자.
[ /etc/procmail/mail_log.rc ]
# 송신자 메일주소 :0 * ^From: \/.* { FROM = "$MATCH" } # 수신자 메일주소 :0 * ^To: \/.* { TO = "$MATCH" } # 메일제목 :0 * ^Subject: \/.* { SUBJECT = "$MATCH" } # 메일 본문 byte수 :0 * 1^1 B ?? > 1 { } LENGTH = $= RESULT=`/etc/procmail/mail_log.pl "$FROM" "TO" $LOGNAME "$SUBJECT" $LENGTH` |
* 다운로드 : http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.rc
각각의 조건에 의해 수신자, 송신자, 메일제목, 본문 길이를 얻어낸다.
그 얻어진 값은 변수에 저장되어 mail_log.pl 프로그램에 인수로 넘겨주게 된다.
어떻게 매칭이 되어 FROM, TO, SUBJECT, LENGTH 변수에 값이 들어가는지 궁금하면
procmailrc 에 VERBOSE=yes 로 하면 쉽게 확인할 수 있을 것이다.
LOGFILE=/var/log/procmail VERBOSE=yes |
[ /etc/procmail/mail_log.rc ]
# 메일 필터링 여부 체크 # # 메일 필터링이 되지 않은 경우는 DB에서 필터링 유무 체크용 필드를 0 으로 # update합니다. # 이 파일은 /etc/procmailrc 의 제일 마지막에 INCLUDE해야 합니다. RESULT2=`/etc/procmail/mail_filterchk.pl "$RESULT"` |
* 다운로드 : http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.rc
4. DB 스키마
MAIL_LOG DB 스키마이다.
/* 메일 수신 로그 */ CREATE TABLE MAIL_LOG ( MAIL_SEQ int unsigned not null auto_increment, /* 로그 SEQ. */ MAIL_FROM varchar(255), /* 송신자(From) */ MAIL_FROMNAME varchar(255), /* 송신자 이름 */ MAIL_FROMMAIL varchar(255), /* 송신자 메일주소 */ MAIL_TO varchar(255), /* 수신자(To) */ MAIL_LOGNAME varchar(255), /* 수신 ID */ MAIL_SUBJ varchar(255), /* 제목 */ MAIL_SIZE int unsigned default 0, /* 메일 크기 */ MAIL_FILTERCHK int unsigned default 0, /* 필터링 유무 (0=필터링 안됨) */ MAIL_DATE datetime, /* 메일 날짜 */ PRIMARY KEY (MAIL_SEQ), INDEX key_filterchk(MAIL_FILTERCHK) ); |
* 다운로드 http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.sql
5. 로깅 및 필터링 여부 체크 프로그램
다음은 로그를 DB에 저장하는 펄 소스이다.
[ /etc/procmail/db_lib.pl ]
#!/usr/bin/perl # # DB 함수 # # Made By Jinho Hwangbo ( 좋은진호, http://coffeenix.net/ ) use DBI; # DB 연결 sub db_connect { $szDBName = "DB지정"; $szDBUser = "DB USER ID"; $szDBPasswd= "DB 비밀번호"; $dbh = DBI->connect ( "DBI:mysql:$szDBName", $szDBUser, $szDBPasswd) || die "$DBI::errstr"; } # DB 접속을 끊음 sub db_disconnect { $dbh->disconnect(); } # SQL문 실행 sub db_do_sql { my ( $szSQL ) = @_; my ( $sth ); $sth = $dbh->prepare($szSQL); # 오류가 발생했는지 검사 -------- if ( $@ ) { &db_disconnect; print " 오류 발생 : $@\n"; } else { $sth->execute; } $sth->finish(); } $temp="1"; |
[ /etc/procmail/mail_log.pl ]
#!/usr/bin/perl # # procmail을 통해 넘겨온 메일 수신 정보를 DB로. # # Made By Jinho Hwangbo ( 좋은진호, http://coffeenix.net/ ) # # 2004.1.13(화) # 2004.9.10(금) 필터링 여부 체크용 필드 추가 # # - Perl DBI, DBD 모듈 필요 # - DB : MySQL # - 넘겨오는 값 : 순서대로 From, To, 수신ID, 메일제목, 본문크기(byte) require '/etc/procmail/db_lib.pl'; # $DEBUG = 1; # 정보를 넘겨 받음 if ( $#ARGV < 4 ) { print "실행방법이 틀렸습니다. procmail을 통해서 실행하세요.\n"; exit 1; } ($FROM, $TO, $LOGNAME, $SUBJECT, $SIZE ) = @ARGV; # DB저장을 위한 작은 따옴표 처리 $FROM =~ s/'/''/g; $TO =~ s/'/''/g; $SUBJECT =~ s/'/''/g; # From: 에서 이름과 메일주소를 분리 # 예 1) $FROM = '"truefeel" # 예 2) $FROM = 'true____@coffee___.___'; # 예 3) $FROM = ' if ( $FROM =~ /"{0,}([^"|.]*)"{0,}\s{0,}<(.*)>/g ) { $FROMNAME = $1; $FROMMAIL = $2; } else { $FROMMAIL = $FROM; } # 필터링 여부 체크를 위한 Uniq한 키(9자리) 만들기 srand(); $FILTERCHK = sprintf("%09d", int(rand(999999999)) ); # ------------------------------------------------- # DB 처리 # ------------------------------------------------- # DB 접속 &db_connect; # 로그 저장 $szSQLMailLog = qq { INSERT INTO MAIL_LOG VALUES ('', '$FROM', '$FROMNAME', '$FROMMAIL', '$TO', '$LOGNAME', '$SUBJECT', '$SIZE', '$FILTERCHK', now() ) }; &db_do_sql($szSQLMailLog); &db_disconnect; # 디버깅 if ( defined($DEBUG) ) { $szMailLog = sprintf("송신= %s\n수신= %s, %s\n제목= %s\n크기= %dBytes\n", $FROM, $TO, $LOGNAME, $SUBJECT, $SIZE); open(FILE, ">/tmp/maillog.debug"); print FILE $szMailLog; print FILE "$szSQLMailLog \n"; close(FILE); } print $FILTERCHK; # 키값을 procmail 로 넘김 exit; |
* Syntax Highlight된 소스 보기 :
http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.pl.html
http://coffeenix.net/truefeel/files/mail_log_v2/db_lib.pl.html
* 다운로드
http://coffeenix.net/truefeel/files/mail_log_v2/mail_log.pl.txt
http://coffeenix.net/truefeel/files/mail_log_v2/db_lib.pl.txt
간단히 살펴보자.
넘겨온 인수중에서 송신자 정보는 이름과 메일주소로 나눈다. 물론 이름이 없어도 문제없이
처리한다. 그리고 DB에 저장하고 종료한다.
$DEBUG = 1 으로 지정하면 디버깅에 유용하다. 넘겨받은 인수를 /tmp/maillog.debug에 저장 한다.
db_connect() 함수에서 $szDBName, $szDBUser, $szDBPasswd을 설정해주어야 한다.
만약 Oracle DB이라면 'DBI:mysql' 대신 'DBI:Oracle'을 써주면 된다.
주의할 것은 DB 비밀번호도 있으니 파일 퍼미션을 700(rwx------)으로 해야한다.
# chmod 700 /etc/procmail/db_lib.pl |
[ /etc/procmail/mail_log.pl ]
#!/usr/bin/perl # # procmail을 통해 넘어온 키로 필터링 여부를 DB에 표시 # # Made By Jinho Hwangbo ( 좋은진호, http://coffeenix.net/ ) # # 2004.9.10(금) # # - Perl DBI, DBD 모듈 필요 # - DB : MySQL # - 필터링 안된 것은 MAIL_FILTERCHK 필드를 0 으로 함 require '/etc/procmail/db_lib.pl'; # $DEBUG = 1; # 정보를 넘겨 받음 if ( $#ARGV < 0 ) { print "실행방법이 틀렸습니다. procmail을 통해서 실행하세요.\n"; exit 1; } ($FILTERCHK ) = @ARGV; # ------------------------------------------------- # DB 처리 # ------------------------------------------------- # DB 접속 &db_connect; # 로그 저장 $szSQLMailLog = qq { UPDATE MAIL_LOG SET MAIL_FILTERCHK = 0 WHERE MAIL_FILTERCHK = '$FILTERCHK' }; &db_do_sql($szSQLMailLog); &db_disconnect; # 디버깅 if ( defined($DEBUG) ) { $szMailLog = sprintf("송신= %s\n수신= %s, %s\n제목= %s\n크기= %dBytes\n", $FROM, $TO, $LOGNAME, $SUBJECT, $SIZE); open(FILE, ">/tmp/maillog.debug"); print FILE $szMailLog; print FILE "$szSQLMailLog \n"; close(FILE); } exit; |
* Syntax Highlight된 소스 보기 :
http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.pl.html
* 다운로드
http://coffeenix.net/truefeel/files/mail_log_v2/mail_filterchk.pl.txt
수신 메일에 대한 유일한 키값($FILTERCHK)을 넘겨받아서 필터링 되지 않은 메일임을
표시한다. (UPDATE문, MAIL_FILTERCHK = 0)
로그가 제대로 남았는지 확인해보자.

로그를 DB로 남겼을 때 어떻게 활용할 것인지 생각했는가?
그럼 지금 당장 시작해라!
6. 참고 자료
* Procmail Tips
http://pm-doc.sourceforge.net/pm-tips.html
* procmail에 관하여 (글 이상로)
http://trade.chonbuk.ac.kr/~leesl/procmail/index.html
* Short guide to DBI (The Perl Database Interface Module)
http://www.perl.com/pub/a/1999/10/DBI.html
내가 관리하는 리눅스 서버는 약 12대 정도다. 한대당 평균 하루 5~10만건 이상의 메일이 발송된다. 이중 스팸메일이 85%가 넘으며, 이런 스팸메일로 인해 우리 리눅스 서버는 IP가 자주 블럭되었었다. 해당 서버의 IP가 블럭되는것을 막기위해 난 Procmail과 Sendmail을 이용하여 발송되는 메일들을 필터링 하기 시작했다.
필터링 하여 로그를 남기는건 좋으나, 나중에 분서하기가 까다로웠는데, 아래 메뉴얼을 참고하여 발송되는 내역에 대한 로그를 DB화 해야 할꺼 같다....
위 메뉴얼은 받는 메일에 대한 부분이지만, 난 발송되는 메일에 대한 부분? ㅋ
응용하면 얼마든지..
필터링 하여 로그를 남기는건 좋으나, 나중에 분서하기가 까다로웠는데, 아래 메뉴얼을 참고하여 발송되는 내역에 대한 로그를 DB화 해야 할꺼 같다....
위 메뉴얼은 받는 메일에 대한 부분이지만, 난 발송되는 메일에 대한 부분? ㅋ
응용하면 얼마든지..
'Linux' 카테고리의 다른 글
제로보드를 이용한 사이트가 늦게 뜰때!!! (0) | 2007.02.09 |
---|---|
vsftp 아스키 모드 지원 셋팅 (0) | 2006.12.20 |
유닉스 및 리눅스 명령어 팁 (0) | 2006.10.09 |
Procmail 과 Sendmail을 이용한 Outgoing mail Filtering (0) | 2006.09.21 |
아파치에서 포워딩 셋팅하기(virtual host) (0) | 2006.04.28 |