フロントエンドGLDを使用したPostfix外部アクセスルール

私が働いている組織では、Postfixがメールサーバーとして使用されていました。 これと組み合わせて、ツールを使用して、スパムとウイルスのSpamassassin、Amavisd-new、ClamAVをフィルタリングします。 これに加えて、グレーリストはGLDを使用して実装されます。 後者は設定が簡単で、「簡単に」操作できますが、これには欠点が1つあります。柔軟性が十分ではありません。 これを克服するために、Postfixの興味深い機能であるPostfix SMTPアクセスポリシー委任を見ました。 このトピックに関する優れた強力な情報はほとんどありません。 データをGLDに転送する前にチェックを追加する方法や、お気に入りまたはおなじみの言語とツールを使用してPostfixで「外部」ルールを実装する方法を気にする人は、猫の下でお願いします。





入門



すべては、一部のユーザーがスパム対策フィルターの重大度についてますます不満を募るようになったという事実から始まりました。 彼らはすべてのチェックをオフにすることを要求し、1通の手紙を紛失しないようにすべてのスパムを受信する準備ができていると主張しました。 私たちのビジネスは管理者です、それは必要です。 そして、amavisd-newではこれが簡単に実装されていることがわかりましたが、GLDではそのような可能性はありません。 ホワイトリストはありますが、送信者のデータに基づいています。1つのホワイトリストを1か所に保持し、同時に2つをサポートしたくないため、受信者のアドレスで機能するかどうかは完全にはわかりませんでした。 また、タスクは変更される可能性があり、将来的には、PostfixとGLDの間に特定のレイヤーが必要でした。



理論



GLDの仕組み(ネットワークソケットを介して、ポート2525でリッスンする)を念頭に置いて、 check_policy_serviceメカニズムを調査しました 。 Postfixは、ディレクティブで指定されたアドレスにSMTPセッションデータを送信します。 ソケットをリッスンする独自のデーモンを作成したくない場合、Postfixにはツールがあります。これは、inetdのように機能するspawnデーモンです。 彼自身が必要なソケットをリッスンし、受信したすべてを指定されたトランスポートに転送します。



データの形式はname = valueで 、1行に1つです。データパケットの終わりは空の行で示されます。 回答はaction = valueの形式の1行で構成する必要があります。この行の後には空の行が続く必要があります。 アクションは、PostfixリストのOKまたはREJECT標準、および他のフィルターでチェックを続けることを意味するDUNNOの両方を受け入れることができます。この作業は、 DEFER_IF_PERMIT Some text ...によって終了しました

公式文書によると、輸送のSTDINで受信したデータは次のとおりです。



Postfix version 2.1 and later: request=smtpd_access_policy protocol_state=RCPT protocol_name=SMTP helo_name=some.domain.tld queue_id=8045F2AB23 sender=foo@bar.tld recipient=bar@foo.tld recipient_count=0 client_address=1.2.3.4 client_name=another.domain.tld reverse_client_name=another.domain.tld instance=123.456.7 Postfix version 2.2 and later: sasl_method=plain sasl_username=you sasl_sender= size=12345 ccert_subject=solaris9.porcupine.org ccert_issuer=Wietse+20Venema ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04 Postfix version 2.3 and later: encryption_protocol=TLSv1/SSLv3 encryption_cipher=DHE-RSA-AES256-SHA encryption_keysize=256 etrn_domain= Postfix version 2.5 and later: stress= Postfix version 2.9 and later: ccert_pubkey_fingerprint=68:B3:29:DA:98:93:E3:40:99:C7:D8:AD:5C:B9:C9:40 [empty line]
      
      







ビジネスへ



だから、ツールで武装。 個人的に、私はPerlに精通しています。 他の人のperl言語の読み方が好きで知っているなら、この記事のタブを閉じて、Postfixで提供されている例からgreylist.plを勉強してください 。 さらに、有能な例のために、インデントとコメントを付けて明確に書かれています。 Postfixのセットアップに移ります。



後置


すべては聖約に従って行われます。 ドキュメント。 まず、master.cfを編集して、新しいトランスポートを最後に追加します。



 # Greylist policy daemon filter gld unix - nn - 0 spawn user=nobody argv=/home/bender/scripts/gld.pl
      
      





これは単なる説明です。このトランスポートを使用するには、main.cfに次のように記述します。



 smtpd_recipient_restrictions = ... reject_unauth_destination, check_policy_service unix:private/gld gld_time_limit = 3600
      
      





reject_unauth_destinationcheck_policy_serviceの前にあることに注意してください。



Perl


まず、スクリプトフレームワークを提供します。これに基づいて、ここまで読んで、必要なすべてを実行できます。



 #!/usr/bin/perl $dump = ''; $defaultAction = 'DUNNO'; #   . select((select(STDOUT), $| = 1)[0]); ##################################################################### #   ##################################################################### while (<STDIN>) { if ($_ eq "\n") { #  ,  if (meetSomeReq($dump)) { #  ,  DUNNO print STDOUT "action=$defaultAction\n\n"; } else { #   print STDOUT "action=DEFER_IF_PERMIT Service temporary unavailable\n\n"; } $dump = ''; } else { #    $dump .= $_; } } ##################################################################### #  ##################################################################### sub meetSomeReq { my $dump = shift(); my $line = ''; my %param = (); my $result = 1; #  ,   Postfix   foreach $line (split(/\n/, $dump)) { chomp($line); my ($key, $val) = split(/=/, $line); $param{$key} = $val; } #  -       $result   return $result; }
      
      







結果



トピック全般、つまり私の場合(PostfixとGLD間の調停など)に興味のない人のために、結果の(そして実際に動作する)スクリプトの全文を提供します。



 #!/usr/bin/perl use IO::Socket; use DBI; my $dbh = DBI->connect("DBI:mysql:host=localhost;database=amavisd", "amavisadmin", "amavisadminpw") or die "Couldn't connect to server !$ \n"; $dump = ''; $defaultAction = 'DUNNO'; # Unbuffer standard output. select((select(STDOUT), $| = 1)[0]); ##################################################################### # Main loop ##################################################################### while (<STDIN>) { if ($_ eq "\n") { if (inWhiteList($dump)) { print STDOUT "action=$defaultAction\n\n"; } else { print STDOUT passToGLD($dump); } $dump = ''; } else { $dump .= $_; } } $dbh->disconnect(); ##################################################################### # # Subs # ##################################################################### sub passToGLD { my $dump = shift(); $dump .= "\n\n"; my $sock = new IO::Socket::INET( PeerAddr => '127.0.0.1', PeerPort => '2525', Proto => 'tcp', ); die "Could not create socket: $!\n" unless $sock; print $sock $dump; $resp = <$sock>; close($sock); return $resp."\n"; } sub inWhiteList { my $dump = shift(); my $line = ''; my %param = (); my $result = 1; my $maxSize = 65536; # # Convert text dump to hash # foreach $line (split(/\n/, $dump)) { chomp($line); my ($key, $val) = split(/=/, $line); $param{$key} = $val; } # # Check user's policy # if ($param{'size'} < $maxSize) # Pass large mails without check { my ($user, $domain) = split(/@/, $param{'recipient'}); my $qry = "SELECT count(email) FROM users WHERE policy_id='3' AND (email=? OR email=?)"; my $sth = $dbh->prepare($qry); $sth->execute($param{'recipient'}, '@'.$domain); my @row = $sth->fetchrow_array(); $sth->finish(); $result = $row[0]; } return $result; }
      
      







おわりに



記事を書く過程でのみ、GLDを放棄し、その代理としてスクリプトを開発することについて考え始めました。 GLDはCで記述されていますが、MySQLを使用してデータを保存するため、パフォーマンスの向上はそれほど大きくありません。 まあ、2006年5月の最後の更新は、プロジェクトが完全に生きているわけではないことを示唆しています。 興味深いことに、私以外で誰かがそれを使用していますか?



一般的に、私はコメントを待っています。 そして、長いテキストの愛好者を許してください! 私自身もそうです。



All Articles