Skip to content

조회 수 8309 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
얼마전 마이둠으로 인해서 제가 관리하는 조그만 메일서버가
엉망이된 적이 있어서 여기저기 알아보다가 알게된 방법입니다.

qmail을 사용하라는 얘기를 많이 들었는데요.
처음부터 새로 설치한다는데에 겁이 나서 sendmail을
사용해서 하는 법을 여기저기 찾아보다가 알게된 방법이죠.

RedHat9를 기준으로 설명합니다.


1. milter 사용을 위해 sendmail-devel패키지와 sendmail소스를 설치
  (perl의 milter 모듈을 설치할 때 소스를 필요로 합니다)

[sendmail-devel rpm]
  RedHat9 cd3 RPMS
  sendmail-devel-8.12.8-4.i386.rpm

[sendmail srpm]
  
http://rpmfind.net/에서 sendmail로 검색 후 RedHat9용 같은 버전을 찾는다.
  sendmail-8.12.8-4.src.rpm

[설치]
  # rpm -Uvh sendmail-devel-8.12.8-4.i386.rpm
  # rpm -Uvh sendmail-8.12.8-4.src.rpm

[sendmail 소스 컴파일]
  # cd /usr/src/redhat/SPECS/
  # rpmbuild -bc sendmail.spec


2. perl용 milter 모듈을 설치
  (base64 인코딩된 메일도 검사하기 위하여 base64 모듈도 설치합니다)

[모듈 다운로드]
  
http://www.cpan.org/authors/id/G/GA/GAAS/MIME-Base64-3.00.tar.gz
  http://www.cpan.org/authors/id/C/CY/CYING/Sendmail-Milter-0.18.tar.gz

[Base64 모듈 설치]
  # tar xzvf MIME-Base64-3.00.tar.gz
  # cd MIME-Base64-3.00
  # perl Makefile.PL
  # make
  # make install

[Milter 모듈 설치] 반드시 sendmail소스 컴파일이 먼저 되어 있어야 합니다.
  # tar xzvf Sendmail-Milter-0.18.tar.gz
  # cd Sendmail-Milter-0.18
  # perl Makefile.PL /usr/src/redhat/BUILD/sendmail-8.12.8/ /usr/src/redhat/BUILD/sendmail-8.12.8/obj.Linux.2.4.20-8smp.i686/
  # make
  # make install


3. 이제 자신만의 밀터를 perl로 작성하겠습니다.
  (보기 좋게 표시하기 위해 tab부분에 ['ㄱ'한자1]문자가 2개씩 들어가 있습니다.
  소스를 복사하실때는 메모장 등에서 '  '문자를 다시 tab으로 바꾸셔야 됩니다.)

[perl 스크립트 생성]
  # cd /etc/mail/
  # vi MyMilter.pl
--------------------------8<------------------------------code : MyMilter.pl----
#!/usr/bin/perl

use Sendmail::Milter;
use Socket;
use MIME::Base64;

sub connect_callback {
  my ($ctx, $hostname, $sockaddr_in) = @_;

  print "[connect] hostname: '$hostname'\n";

  return SMFIS_CONTINUE;
}

sub helo_callback {
  my ($ctx, $helohost) = @_;

  print "  [helo] helohost: '$helohost'\n";

  return SMFIS_CONTINUE;
}

sub envfrom_callback {
  my ($ctx, $from, @args) = @_;

  print "  [envfrom] args: '$from','" . join("','", @args) . "'\n";

  # 익명 송신자 제거
  if ($from =~ /\<( |\t)*\>/) {
    print("  ! from nobody: $from\n");
    return SMFIS_REJECT;
  }

  return SMFIS_CONTINUE;
}

sub envrcpt_callback {
  my ($ctx, $rcpt, @args) = @_;

  print "  [envrcpt] args: '" . join("','", @args) . "'\n";

  # 익명 수신자 제거
  if ($rcpt =~ /\<( |\t)*\>/) {
    print("  ! rcpt nobody: $rcpt\n");
    return SMFIS_REJECT;
  }

  return SMFIS_CONTINUE;
}

sub check_subject {
  my ($subject) = @_;

  # 광고 및 홍보 제거
  if ($subject =~ /[[(< ({].*(광.*고|홍.*보).*[])>) }]/) {
    print("  ! ad: $subject\n");
    return "bad";
  }
  # 포르노 제거
  if ($subject =~ /.*(포르노|porno).*/) {
    print("  ! porno: $subject\n");
    return "bad";
  }
  return "good";
}

sub header_callback {
  my ($ctx, $headerf, $headerv) = @_;

  # Subject: 헤더인 경우.
  if ($headerf eq "Subject") {
    $chk = &check_subject($headerv);
    if ($chk eq "bad") {
      return SMFIS_REJECT;
    }

    # base64 디코딩을 수행
    @decoded = split(/[\?+\n+ +\t+]/, $headerv);
    $headerv = "";
    foreach (@decoded) {
      $headerv .= decode_base64($_);
    }

    $chk = &check_subject($headerv);
    if ($chk eq "bad") {
      return SMFIS_REJECT;
    }
  }

  return SMFIS_CONTINUE;
}

sub eoh_callback {
  my ($ctx) = @_;

  return SMFIS_CONTINUE;
}

sub check_body {
  my ($body) = @_;

  # 본문 광고 체크 : 정보통신부
  if ($body =~ /정보통신부 *권고 *사항에 *의거/) {
    print("  ! ad: 정보통신부 권고 사항에 의거한 내용\n");
    return "bad";
  }
  return "good";
}

sub body_callback {
  my ($ctx, $body_chunk, $len) = @_;

  print("  [body] length: $len\n");

  # 적당하지 않은 첨부파일이 있을시 거부 *.src, *.pif, *.bat, *.com
  if ($body_chunk =~ /name=\"?.*\.(scr|pif|bat|com)\"?/) {
    print("  ! virus?: $1\n");
    return SMFIS_REJECT;
  }

  $chk = &check_body($body_chunk);
  if ($chk eq "bad") {
    return SMFIS_REJECT;
  }

  # base64 디코딩을 수행
  @decoded = split(/[\?+\n+ +\t+]/, $body_chunk);
  $body_chunk = "";
  foreach (@decoded) {
    $body_chunk .= decode_base64($_);
  }

  $chk = &check_body($body_chunk);
  if ($chk eq "bad") {
    return SMFIS_REJECT;
  }

  return SMFIS_CONTINUE;
}

sub eom_callback {
  my ($ctx) = @_;

  $ctx->addheader("X-MyMilter", "Spam,Virus Chk 1.01beta [Perl version]");

  return SMFIS_CONTINUE;
}

sub abort_callback {
  my ($ctx) = @_;

  return SMFIS_CONTINUE;
}

sub close_callback {
  my ($ctx) = @_;

  return SMFIS_CONTINUE;
}

############################################
# 이하의 코드는 밀터 기본 코드로 수정 불가 #
############################################

my %my_callbacks = (
  'connect' => \&connect_callback,
  'helo' => \&helo_callback,
  'envfrom' => \&envfrom_callback,
  'envrcpt' => \&envrcpt_callback,
  'header' => \&header_callback,
  'eoh' => \&eoh_callback,
  'body' => \&body_callback,
  'eom' => \&eom_callback,
  'abort' => \&abort_callback,
  'close' => \&close_callback,
);

BEGIN:
{
  if (scalar(@ARGV) < 2) {
    print "Usage: perl $0 <name_of_filter> <path_to_sendmail.cf>\n";
    exit;
  }

  my $conn = Sendmail::Milter::auto_getconn($ARGV[0], $ARGV[1]);

  print "Found connection info for '$ARGV[0]': $conn\n";

  if ($conn =~ /^local:(.+)$/) {
    my $unix_socket = $1;

    if (-e $unix_socket) {
      print "Attempting to UNIX socket '$conn' ...";

      if (unlink($unix_socket) == 0) {
        print "failed.\n";
        exit;
      }
      print "successful.\n";
    }
  }

  if (!Sendmail::Milter::auto_setconn($ARGV[0], $ARGV[1])) {
    print "Failed to detect connection information.\n";
    exit;
  }

  if (!Sendmail::Milter::register($ARGV[0], \%my_callbacks, SMFI_CURR_ACTS)) {
    print "Failed to register callbacks for $ARGV[0].\n";
    exit;
  }

  print("Starting Sendmail::Milter $Sendmail::Milter::VERSION engine.\n");

  if (Sendmail::Milter::main()) {
    print "Successful exit from the Sendmail::Milter engine.\n";
  }
  else {
    print "Unsuccessful exit from the Sendmail::Milter engine.\n";
  }
}
--------------------------8<------------------------------code : MyMilter.pl----

위의 코드는 어느분이 milter에 대해 설명해주신 곳에서
그대로 가져다가 몇가지만 수정한 것입니다. (정확히 어딘진 기억이 가물..)


4. sendmail에서 방금 만든 밀터를 사용하도록 설정합니다.

[sendmail.mc 수정]
  # vi /etc/mail/sendmail.mc
    -다음을 마지막에 추가-----------
    INPUT_MAIL_FILTER(`MyMilter', `S=local:/var/run/mymilter.sock')dnl
    --------------------------------
  # m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf

[sendmail 재시작]
  # /etc/init.d/sendmail restart


5. 테스트를 위해 밀터를 실행시켜 봅니다.

[perl로 밀터 실행]
  # perl /etc/mail/MyMilter.pl MyMilter /etc/mail/sendmail.cf

[프로세스 확인] : 특별히 실행하는 perl이 없다면 하나만 보일껍니다.
  # ps -Al | grep perl

샐행된 상태에서 이런 저런 메일을 보내 봅니다.
그러면 메일 하나마다 화면에 찍힐껍니다.

Outlook Express라면 받은 메일에 '속성'->'자세히'해보면
제일 끝에 'X-MyMilter'라는 내용이 붙어 있는 것이 보일것입니다.

그러면 정상적으로 실행된 것입니다.


6. 재부팅하면 바로 백그라운드로 실행되게 합시다.

[rc.local 수정]
  # vi /etc/rc.local
    -다음을 마지막에 추가-----------
    perl /etc/mail/MyMilter.pl MyMilter /etc/mail/sendmail.cf &
    --------------------------------


다음부터는 밀터가 동작하면서 내용에 대한 것을 tty1에 찍게됩니다.
화면에 찍히는게 싫으면 print문을 전부 #으로 주석처리 하시면 되구요.
위의 소스만 살펴보시면 아주 다양하게 활용할 수 있을 것같습니다.

여러개의 밀터도 사용이 가능하구요..
sendmail.mc에 등록시킨 순서대로 작동하게 됩니다.
밀터가 여러개일땐 'mymilter.sock' <- 요부분의 이름이 다 달라야 합니다.

메일이 일단 도착하면 제일 먼저 milter에게 수신 여부를 묻는다고 하니 저장되기도 전에 제일 먼저 보고 차단하는 것이라고 볼 수 있습니다.

제가 관리하는 서버는 이용자가 20명 좀 안되기 때문에 충분한 효과를 보고 있습니다.
대규모에서까지 효율적일지는 모르겠군요.




PS : 코드 내용중 '@decoded'앞의 이상한 문자열'?#161;'이 들어가는데 안 없어지는군요.
'?#161;' <- 요건 전부 지워 주세요.

PS2 : 어차피 HTML태그가 안되는 거라면 내용이 <PRE>로 담겨서 나온다면
일부러 tab을 다른 문자로 바꾼다거나 하는 일은 없을텐데.. 

List of Articles
번호 제목 글쓴이 날짜 조회 수
42 telnet 을 사용한 메일서버 접속 ADMINPLAY 2009.10.20 8849
» Sendmail에서 milter 사용으로 spam, 바이러스 막기 ADMINPLAY 2009.10.20 8309
40 Sendmail 세부설정 ADMINPLAY 2009.10.20 9940
39 주요포탈업체 IP차단해제하기 ADMINPLAY 2009.11.01 11027
38 /var/spool/mail/에 저장된 메일 다른 메일로 보내기 ADMINPLAY 2009.11.01 9445
37 roundcube첨부파일 한글일때 깨지는 문제해결 ADMINPLAY 2009.11.01 12404
36 squirrelmail (다람쥐메일) 설치하기관리자 file ADMINPLAY 2009.11.24 11085
35 Sendmail 계정 및 도메인생성 ADMINPLAY 2009.11.24 13310
34 spamassassin 필터 설치하기 ADMINPLAY 2009.11.24 9281
33 폼메일 테스트 방법 ADMINPLAY 2009.11.26 11276
32 qmail error (unable to lock directory /var/log/qmail/s... ADMINPLAY 2009.11.26 11244
31 telnet을 이용한 SMTP 테스트 ADMINPLAY 2009.11.26 11945
30 sendmail mqueue 에 쌓인 메일삭제 주기수정 ADMINPLAY 2009.12.14 15147
29 리눅스 메일로그 분석 및 확인 ADMINPLAY 2009.12.23 13673
28 qmail 큐삭제 ADMINPLAY 2009.12.25 14478
27 Q메일서버 관리 1 ADMINPLAY 2009.12.25 11082
26 qmail queue 서브디렉토리 복구 ADMINPLAY 2009.12.25 10292
25 RBL 참조사이트 이용 및 스팸차단 설정 ADMINPLAY 2010.01.22 12217
24 해외(Hotmail 등)로 안정적인 메일발송방법 - Reverse DNS... ADMINPLAY 2010.03.10 14015
23 qmail relay, 암호 인증, STARTTLS ADMINPLAY 2010.05.12 13855
Board Pagination Prev 1 2 3 4 5 6 Next
/ 6

Copyright ADMINPLAY corp. All rights reserved.

abcXYZ, 세종대왕,1234

abcXYZ, 세종대왕,1234