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
번호 제목 글쓴이 날짜 조회 수
» Sendmail에서 milter 사용으로 spam, 바이러스 막기 ADMINPLAY 2009.10.20 8309
61 telnet 을 사용한 메일서버 접속 ADMINPLAY 2009.10.20 8849
60 SendMail 설정 하기 file ADMINPLAY 2009.10.20 10466
59 리턴메일 분석 ADMINPLAY 2009.10.20 9477
58 Jul 5 15:53:46 www sendmail[25566]: NOQUEUE: [211.192.... ADMINPLAY 2009.09.20 10907
57 sendmail : rejecting connections on port 25 : load ave... ADMINPLAY 2009.09.20 8640
56 g5M1EkS08293: timeout waiting for input from mx4.hanma... ADMINPLAY 2009.09.20 9097
55 Jun 26 11:15:15 www sendmail[27135]: g5Q2FES27135: col... ADMINPLAY 2009.09.20 8708
54 Jun 26 11:04:12 www sendmail[26626]: g5Q246I26624: g5Q... ADMINPLAY 2009.09.20 9024
53 /var/spool/mail root계정 및 일반계정 용량삭제 ADMINPLAY 2009.08.23 12579
52 리눅스 특정 계정 받는메일,보내는메일 사용 막기 ADMINPLAY 2009.08.23 9325
51 vpopmail 과 mysql 연동 관련 장애 처리 ADMINPLAY 2009.08.08 8715
50 Sendmail 메일큐 활용하기.[멀티 큐 활용] ADMINPLAY 2009.07.18 9540
49 [스팸관련] 외국 RBL 확인하는 방법 ADMINPLAY 2009.07.18 8779
48 [SMTP(메일발송)] 에러코드 ADMINPLAY 2009.07.18 12180
47 메일서버(Sendmail) 구축 ADMINPLAY 2009.07.18 9737
46 하나의 서버에서 dns 와 rbldns 운영하기. ADMINPLAY 2009.07.14 9055
45 imap, ipop3 on xinetd ADMINPLAY 2009.06.12 8780
44 메일서버 구축하기(sendmail) ADMINPLAY 2009.06.12 9866
43 qmail + vpopmail 관리팁 ADMINPLAY 2009.06.04 8073
Board Pagination Prev 1 2 3 4 5 6 Next
/ 6

Copyright ADMINPLAY corp. All rights reserved.

abcXYZ, 세종대왕,1234

abcXYZ, 세종대왕,1234