DR;AWSのコストアラートは適切に設定し、おかしいと思ったらちゃんと確認しようPostfixには適切な設定を行おうパスワードや鍵は定期的に交換しよう構成変更後は慎重に検証しよう自前での構築をやめて、サービスを使うことも検討しよう。背景5年ほど運用していたメールサーバが、2024年10月4日の午前6時ころに爆発的に利用されている痕跡を、当日の昼頃に発見しました。営業日だったので、ひとまず一次対応としてPostfixを止めて、終業後にキレながら対応しました。わたしからスパムメールを受け取った方、ご迷惑をおかけして大変申し訳ありませんでした。いまごろ被害を受けることになったのかですが、おそらく8月末に自宅のネットワーク構成を変更したことにあると睨んでいます。それまでは、自宅のネットワーク構成は以下のようになっていました。IPv6は使用可能)それらをコスト削減のために、一本化することにし、後者の回線を残しました。ここで、任意のポートを開放することができないためサーバ公開ができない、という問題がありました。そもそもそれが、2回線残っていた理由です。ここでひらめきました。して踏み台ができ、しっかりと機能していることが確認できたため、PPPoE回線の契約は解除できました。契約更新月ではなかったので違約金は発生しましたが、更新月まで待つのと比較しても違約金のほうが安く、目的は達成できました。このときのネットワークをざっくりと図に表したものが以下です。なかったことに、1ヶ月経過してから気が付きました。いうヒントを出しておきます。勘の良い読者の皆様ならこれだけで真実にたどり着けることでしょう。これを受けて、Postfixの設定を再度見直しました。!この記事は、わたしが「この設定をしました」というだけのものであり、推奨設定を説明しているわけではありません。個人的な解釈を含めています。受けた被害についてまとめます。SESを使用しています。当日、月初であるのにも関わらず、先月よりも高額になるというコストアラートが早くも発砲され、訝しんだ私はマネジメントコンソールに潜りました。Postfixを停止しました。そのスクショは取り忘れたのですが、下記はCloudWatchのログです。見ると、9月26日にも兆候がありました。被害に気が付き停止してからPostfixのキューを見ると、40万件ほど溜まっていました。少なくともあると思います。その根拠が、スパムの宛先が複数であること(=バウンスではなさそう?)実在するユーザ名での送信ではなかったことです。攻撃を受け、行なった対応を列挙していきます。localhostになってしまうことは気がついたからどう対策しようくらいの状況です。!最終的に、トンネルは必要だったので残しました。一時的にメールサーバをIPv6のみにしたのですが、モバイル回線がIPv4しか使用できなかったためです。そのため、出先からメールサーバにアクセスするには、どうしても踏み台の存在が必要不可欠でした。サーバの再起動を行う不具合があったら再起動しようではないです。サーバに対し、不正なセッションが残っている可能性を考慮し、物理的にセッションを破棄するため、再起動を行いました。鍵が漏洩した可能性を考慮し、すべての鍵を再生成しました。これには、サーバへのアクセスに使用している鍵だけでなく、EC2の鍵や、GitHubに登録している鍵などを含みます。未使用ユーザの削除メールを受信するためのユーザとして、マシンには複数のユーザが存在していました。nologinシェルを割り当てていたので、SSHでのログインはできないはずですが、SMTPのユーザとしては利用可能です。そのため、今回は本当に必要なアカウントを除いて削除しました。未使用ユーザを削除したあとに残ったユーザは、パスワードを変更しました。最終的には、目的別に5個くらいだったものを、2つにしました。必要があればまた作ります。SESの認証情報が漏洩した可能性を考慮し、既存の認証情報を破棄して新たな認証情報を発行しました。システムスキャンマルウェアやルートキットの検出を行うため、chkrootkitやrkhunterを用いてシステム全体をスキャンしました。ClamAVによるスキャンも行いました。ここでわかるのは既知の攻撃を受けていないはずということです。Postfixの設定変更ざっくりと、以下の対応を行いました。この項目が今回の本題です。具体的にどんなことをしたのかをつらつらと書いていきます。下記は、対応後の送信側制約です。permitまず、以下の項目です。bl.spamcop.net,これらは、リアルタイムブラックホールリスト(RBL)を参照してブロックするための設定です。RBLを指定することで、ここに登録されているスパム送信者のIPからの送信の場合、拒否することができます。今回は、ふたつのリストを適用しています。トンネル側に接続された場合、localhostなので意味をなしません!!つづいて、送信者の確認を行います。unix:private/sender-policy,これらは、sender_accessに従って検証を行うもの(ひとつめ)と、スクリプトなどを使用して検証を行うもの(ふたつめ)です。以下のように設定されています。sender_policy_checkmilkcocoa.infoというドメインに対して、検証を行うという設定です。ここで実行されるsender_policy_checkは、以下のように定義されています。sender-policyはというと、以下のように定められています。/etc/postfix/sender_policy_service.pyPythonスクリプトが割り当てられていますね。中身は以下のスクリプトです。(長いので折りたたんでいます。認証されている場合、認証ユーザと送信者が一致するかどうかの検証です。(改めて設定をよく見ると重複しているので、milkcocoa.infoのときに二重チェックされ、それ以外のときでもチェックが入ることになっている。)sender_accessは、postmapを使用してハッシュ化しておきます。/etc/postfix/sender_mapさらに以下の項目では、認証済みのユーザによる送信を許可しています。/etc/postfix/main.cfpermit_sasl_authenticatedポリシーチェックのあとに持ってきたのは、認証済みでも不正なユーザの場合に弾くようにするためです。順序を入れ替えると、認証が通ってしまえば不正なユーザでも通過してしまいます。その後段で、不明なドメインの送信者を弾いたあとに、最終的には許可をしています。ここまで突破できたら大丈夫だろうという慢心です。localhostは問答無用で送信許可されます。続いて、受信者側の制約です。reject_unverified_recipientまずは、以下の項目です。何かしらのファイルを参照していますね。このファイルは以下のようになっています。(長いので折りたたんでいます。これを、システムユーザすべてで列挙しています。このファイルを忘れずにハッシュ化しましょう。/etc/postfix/recipient_accessこれを前提に以下の設定を冒頭で行うことで、このリストにあるユーザあてのメールはまっさきに破棄されることになります。hash:/etc/postfix/recipient_access,続いて以下の設定では、自分宛てのメールのとき、マシンに存在しないユーザ向けであれば破棄するという設定です。/etc/postfix/main.cfreject_unlisted_recipient先ほどの設定と合わせると、マシンに存在していても、システムユーザであれば破棄するマシンに存在していなければ、拒否するとなり、自分宛てのメールは存在する一般ユーザのみということになります。ここまで来たら認証済みユーザからのメールは許可しておきましょう。/etc/postfix/main.cfpermit_sasl_authenticated,このあたりの順番は諸説あると思いますが、少なくともこれから述べるポリシーチェックよりは前段に持っていきたいです。早い気もしますが、認証済みのユーザの場合は送信者と認証情報が一致することを送信者側の制約で保証しているので、良しとします。unix:private/policyd-spf,これらは、グレイリスティングとSPF検証を行なっています。(設定方法は後述します。permit_sasl_authenticatedのほうが後段にあると、使用する可能性のあるクライアントすべてをSPFレコードに書かないといけない状況になり、すこしめんどくさいです。そのため、認証済みのユーザでの送信は先に許可しています。この後段で最終的な拒否制約をいくつかかけています。受信者が検証できない場合に拒否(外部サーバに対する検証はほぼ必ず失敗するので、外部あてはここまでに突破しておきたい。