Postfix × SES × ClamAV サーバーのウイルススキャン~アラートメール送信 を自動化してみた

# はじめに
どうも、インフラエンジニア見習い ~~(というか社会人見習い。。)~~ の新卒１年目です🙇

職場にて SMTPについて学ぶ機会があったので、プライベートで実践的に復習してみようと思い少し遊んでみました。

自分が再度、同様の環境を構築する際の手順書を作る気持ちで執筆しています。

## 環境
- サーバー : EC2 t2.micro(AmazonLinux2)
- Postfix : ver 2.10.1

# 作業

## 1. SMTPクレデンシャルの発行

Postfixからのメール送信を SES経由にする際に必要です。
まずはSESのセットアップ画面を開きましょう

SMTP設定 を開きます

![スクリーンショット 2024-08-18 17.56.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/699cc6d9-29a9-daac-b0d8-8d253b0f0cce.png)

SMTP認証情報の作成 をクリック

![スクリーンショット 2024-08-18 17.59.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/9c037f6f-f9a0-aece-f758-0323c6dc5906.png)

IAMユーザーの作成画面が開かれるので、メールの送信権限があるか確認し、ユーザーの作成 をクリック

![スクリーンショット 2024-08-18 18.01.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/38bcb077-db22-a414-9b03-5826689bcc3d.png)

ユーザーの作成が完了したら SMTPクレデンシャルの秘匿情報が表示されます。
次の 2. Postfix の設定 で使用するので保存しておきましょう
必要な情報は以下２つです
- SMTP ユーザー名
- SMTP パスワード

![スクリーンショット 2024-08-18 18.05.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/75295137-a944-f55c-acc8-b1083dcf68f5.png)


## 2. Postfix の設定

AmazonLinux には Postfixがプリインストールされているので 手動でのインストールは不要です
まずはバージョンを確認してみましょう
```bash
$ postconf | grep mail_version
mail_version = 2.10.1
```

自動起動も有効化してしまいましょう

```bash
$ sudo systemctl enable postfix
```

次に、Postfixの設定をしていきます
Postfix の設定は  `/etc/postfix/main.cf` を編集、記載します

```
# メールサーバーの情報を記載
myhostname = mail.example.com
mydomain = example.com
mydestination =

# SESを経由させる設定を記載
relayhost = [email-smtp.ap-northeast-1.amazonaws.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_use_tls = yes
smtp_tls_security_level = encrypt
smtp_tls_note_starttls_offer = yes
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt
```
設定パラメータの内容は [＜Tips＞ 設定パラメータの詳細](https://qiita.com/drafts/d87c0b687b4f191aac55/edit#tips-%E8%A8%AD%E5%AE%9A%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E8%A9%B3%E7%B4%B0) にまとめましたのでそちらを参考になさって下さい🙏

次に、Postfix に SESコンソール で作成した SMTPクレデンシャル を認識させるためのファイルを作成します。

```bash
$ sudo vim /etc/postfix/sasl_passwd
```

[1. SMTPクレデンシャルの発行](https://qiita.com/drafts/d87c0b687b4f191aac55/edit#1-smtp%E3%82%AF%E3%83%AC%E3%83%87%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E3%81%AE%E7%99%BA%E8%A1%8C) で保存しておいた秘匿情報を記載しましょう

```
[email-smtp.ap-northeast-1.amazonaws.com]:587 <SMTP ユーザーネーム>:<SMTP パスワード>
```
:::note warn
「ap-northeast-1」 部分はご自身の SES が属するリージョンに合わせて記載を変更して下さい
SESコンソール 画像矢印部分をクリックすると リージョンが確認できます

![スクリーンショット 2024-08-18 18.20.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/fee1e203-cb41-afc8-8b04-fae022f9e706.png)
:::

作成したファイルを `postmap`コマンドでハッシュ化しましょう。 引数`hash:` を指定してハッシュ化します

```bash
＄ sudo postmap hash:/etc/postfix/sasl_passwd
```

ファイルを 管理者のみ閲覧、編集できるように権限変更をしましょう

```bash
$ sudo chown root:root /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
$ sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
```

:::note info
**chmod 600 って何？**

chmod コマンドではファイルの権限を編集します(キーペア作成時とかよく使うよね)
以下のようになっています
- 100の位 : 管理者ユーザーの権限
- 10の位 : グループアクセスの権限
- 1の位 : その他のユーザーのアクセス権限

また、各位の数字は権限に対応する数字の合計となっています
- 4	: 読み出し許可
- 2	: 書き込み許可
- 1	: 実行許可
- 0	: 権限なし

結論
管理者ユーザーの位(100の位)のみ、 読み出し許可(4) + 書き込み許可(2) = 6 
となっているのです(他の位は権限なしで 0)

参考記事: [chmod 600 ←600ってなんやねん](https://qiita.com/aaabb6211/items/30ddad48773f37affcb9)
:::

最後に、Postfix に変更を読み込ませるために再起動をしましょう

```bash
$ sudo systemctl restart postfix
```

### ＜Tips＞ 設定パラメータの詳細

`/etc/postfix/main.cf` に設定したパラメータの詳細を記載します。
すでに内容把握されている方は読み飛ばして下さい 🙏

※適宜、お手元の環境の設定値として正しいか 以下公式ドキュメントの日本語訳 と照らし合わせて設定して下さい。

https://www.postfix-jp.info/trans-2.3/jhtml/postconf.5.html#myhostname

<dl>
<dt>myhostname</dt>
<dd>SMTP サーバーの FQDN を指定</dd>
<dd>デフォルト値: "postconf -d" の出力を参照</dd>
</dl>

:::note info

FQDN(Fully Qualified Domain Name) とは

ホスト(サーバーの名前) + ドメイン(サーバーが所属するグループの名前) で構成されたサーバーのアドレスのこと

```
例 : mail(ホスト) + excompany.com(ドメイン) -> mail.excompany.com(FQDN)
```
参考記事 : [ホスト名とは？調べ方やURLやIPアドレスとの関連性を解説](https://www.itmanage.co.jp/column/host-name/#:~:text=%E3%83%9B%E3%82%B9%E3%83%88%E5%90%8D%E3%81%A8%E3%81%AF%E3%80%81%E6%96%87%E6%9B%B8,%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%AA%E3%81%A9%E3%82%92%E6%8C%87%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)
:::

<dl>
<dt>mydomain</dt>
<dd>myhostname のドメイン部分を指定</dd>
<dd>デフォルト値: "postconf -d" の出力を参照</dd>
<dt>mydestination</dt>
<dd>他のマシンに転送するのではなく、 メールをローカルで受信するドメイン名を指定</dd>
<dd>デフォルト値: $myhostname, localhost.$mydomain, localhost</dd>
<dd>今回は、同じドメイン向けにメールを配信するため、空欄にします(指定しているとSESリレーされない)</dd>
<dt>relayhost</dt>
<dd>next-hop配送先(中継サーバー)を指定</dd>
<dd>デフォルト値: empty</dd>
<dd>SMTP の場合、ドメイン名、ホスト名、hostname:port、[hostname]:port、 [hostaddress] または [hostaddress]:port を指定</dd>
<dt>smtp_sasl_auth_enable</dt>
<dd>Postfix SMTPクライアントの SASL 認証を有効化</dd>
<dd>デフォルト値: no</dd>
<dt>smtp_sasl_security_options</dt>
<dd>SASL認証のオプションを設定</dd>
<dd>デフォルト値: noanonymous</dd>
<dd>以下オプションがある

- noplaintext
    - 平文パスワードを使う方法を許可しない
- noactive
    - active (非辞書) 攻撃を受けるような方法を許可しない
- nodictionary
    - passive (辞書) 攻撃を受けるような方法を許可しない
- noanonymous
    - 匿名認証を認めるような方法を許可しない
- mutual_auth
    - 相互認証を提供する方法のみを許可 **する**
</dd>
<dt>smtp_sasl_password_maps</dt>
<dd>SASLの認証情報が記載されたファイルの場所を指定</dd>
<dd>デフォルト値: empty</dd>
<dt>smtp_use_tls</dt>
<dd>リモートSMTPサーバがSTARTTLSサポートを案内したら TLSを使い、案内がなければ平文でメールを送る (Postfix 2.2以降)</dd>
<dd>SES では STARTTLS がサポートされているので TLS を使うために有効化する</dd>
<dd>デフォルト値: no</dd>

:::note info
STARTTLS とは

平文での通信 を SSL/TLS暗号化通信 に切り替えてくれる「コマンド」

参考記事: 
[STARTTLSとは](https://baremail.jp/blog/2022/08/24/2735/#:~:text=STARTTLSの概要-,STARTTLSとは、暗号化されていない平文で,が可能になります%E3%80%82)
[Amazon SESSMTPエンドポイントへの接続](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/smtp-connect.html)
:::
<dt>smtp_tls_security_level</dt>
<dd>Postfix SMTPクライアントでのSMTP TLSセキュリティレベル を設定</dd>
<dd>デフォルト値: empty</dd>
<dd>以下設定値

- (empty):
    - smtp_tls_policy_maps で特定の配送先に対してTLSが有効にされない限り、TLSは使われない
- may:
    - サーバがサポートしていればTLSが使われる
    - すべてのプロトコルが許可され、"export" グレード以上の暗号(1990年代の頃の強度40bit以下の弱い暗号化形式らしい)が使われる
- encrypt:
    - TLS暗号化を強制
    - 暗号化ができなければ、その相手にはメールを送れない
    - もっとも安全だが、すべての相手が対応しているとは限らないので注意が必要
- verify:
    - TLS暗号化を強制
    - 相手のサーバーが本物かどうかもチェックする(DNS MX検索)
- secure:
    - もっとも厳しい設定
    - DNS MXを使わず、postfix内でnext-hopのサーバ証明書の検証を行う
    - メール送信するだけなら厳しすぎる設定
</dd>
<dt>smtp_tls_note_starttls_offer</dt>
<dd>TLSが有効になっていない場合、STARTTLSを提供するリモートSMTPサーバのホスト名をログ出力させる(Postfix 2.2以降)</dd>
<dd>デフォルト値: no</dd>
<dd>以下のようなログが出る</dd>

```b
postfix/smtp[pid]:  Host offered STARTTLS: [name.of.host]
```

<dt>smtp_tls_CAfile</dt>
<dd>Postfix SMTPクライアント証明書を発行した認証局 (CA) の証明書を持つファイルの場所を指定(Postfix 2.2以降)</dd>
<dd>(-> Postfix が 送信先のSMTPサーバーの正当性を確認するために必要らしい)</dd>
<dd>デフォルト値: empty</dd>

## 3. ClamAVのアラートメールを飛ばすスクリプト実装

まずは、ClamAVをインストールしましょう

```
$ sudo yum install -y clamav clamav-daemon libclamunrar9
```
mailコマンドを使うので mailx をインストールします

```
sudo yum -y mailx
```
次に、ウイルス検知時にメールを飛ばすスクリプトを実装しましょう

[こちら](https://inaba-serverdesign.jp/blog/20170919/clamav_scan_virus_manage.html)の記事を参考にし、メモリ使用量を減らすようにしています


```sh:/usr/local/bin/run-clamscan.sh
#!/bin/bash
EXCLUDE_LIST_FILE=/usr/local/bin/scanvirus_exclude_list.txt

LOGFILE=/var/log/scanvirus.log
SCAN_RESULT=/var/log/scanresult.txt

MAX_FILESIZE=200M
MAX_SCANSIZE=200M

MAILFROM=sender@example.com
MAILTO=recipient@example.com

export LANG=C

echo "===== Scan Virus =====" >> ${LOGFILE}

echo "`date` Scan Virus start" >> ${LOGFILE}

# 1. ClamAVプログラムのアップデート
echo "`date` Update ClamAV start" >> ${LOGFILE}

yum -y update clamav 1>> ${LOGFILE} 2>&1

echo "`date` Update ClamAV end" >> ${LOGFILE}

# 2. ウイルス定義の更新
echo "`date` Update Database start" >> ${LOGFILE}

/usr/bin/freshclam 1>> ${LOGFILE} 2>&1

echo "`date` Update Database end" >> ${LOGFILE}

# 3. ウイルススキャンの実施
echo "`date` Do Scan Virus start" >> ${LOGFILE}

## スキャン除外リストの展開
while read LINE
do
    if [ $(echo "${LINE}"|grep \/$) ]; then
        i=`echo ${LINE}|sed -e 's/^\([^ ]*\)\/$/\1/p' -e d`
        excludeopt="${excludeopt} --exclude-dir=^${LINE}"
    else
        excludeopt="${excludeopt} --exclude=^${LINE}"
    fi
done < ${EXCLUDE_LIST_FILE}

echo "excludeopt: ${excludeopt}" >> ${LOGFILE}

## ウイルススキャンの実行
/usr/bin/clamscan / \
--max-filesize=${MAX_FILESIZE} --max-scansize=${MAX_SCANSIZE} \
--infected --recursive ~/tmp 1> ${SCAN_RESULT} 2>> ${LOGFILE}

## ウイルスを検知したときのみ、アラートメールで通知する
[ ! -z "$(grep FOUND$ ${SCAN_RESULT})" ] && \
cat ${SCAN_RESULT} | mail -s "Virus Found in `hostname`" -r ${MAILFROM} ${MAILTO}

cat ${SCAN_RESULT} >> ${LOGFILE}

echo "`date` Do Scan Virus end" >> ${LOGFILE}

echo "`date` Scan Virus complete" >> ${LOGFILE}
```

最後に、実行権限の付与も忘れずに🙏

```bash
$ sudo chmod 700 /usr/local/bin/run-clamscan.sh
```

## 4. 動作確認

まずはテスト用マルウェアファイルを配置しましょう
eicar がファイルを配布しているのでそちらを使います。

```bash
$ wget https://secure.eicar.org/eicar.com
$ wget https://secure.eicar.org/eicar.com.txt
$ wget https://secure.eicar.org/eicar_com.zip
$ wget https://secure.eicar.org/eicarcom2.zip
```

スクリプトを起動して動作確認をしてみましょう

```bash
$ sudo /usr/local/bin/run-clamscan.sh
```
:::note warn
kill プロセスが発生してしまう場合
ClamAV は スキャン実行時に 2~3GB のメモリを食うようです
メモリが不足する場合は以下参考に スワップメモリを増やしてみましょう。

[EC2をt2.microで立てたときにやることメモ](https://qiita.com/teracy164/items/c1dfc09901c7f242a13f)
:::


以下のように、 SMTPプロセス で `status=sent` となっていればOKです。
ログから、SESの経由に成功していることがわかります👍

```:bash
$ sudo tail -n 4 /var/log/maillog
(中略)
Aug 18 01:06:59 ip-***-**-**-*** postfix/smtp[4077]: 22A8BC1144E: to=<recipient@example.com>, 
  relay=email-smtp.ap-northeast-1.amazonaws.com[**.***.**.**]:587, delay=100, delays=99/0.01/0.18/0.14, 
  dsn=2.0.0, status=sent (250 Ok 010601915f68ccad-a5019b3d-e29a-4f84-a357-64c82345799d-000000)
```

実際にメールが届いていることも確認できましたね 🙌
![スクリーンショット 2024-08-18 11.03.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/5f938245-5836-e44d-6e7b-3dec6c16f0c0.png)

SESコンソールでも該当の時間にメール送信したことを確認できました！ これでかんぺき〜
![スクリーンショット 2024-08-18 11.13.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862494/5e683261-54b2-7090-7407-543de2e7b3b5.png)

# さいごに

長々とお付き合いいただきありがとうございました🙇
今度は自宅のローカルサーバーに同様の構築をし、 `crontab` で定期的にウイルススキャンさせるようにしてみたいと思います。

# 参考文献リスト
- Postfixの設定
    - [25番ポートの申請不要　SES に SMTP リレーをする Postfix サーバーを構築してみた](https://qiita.com/sugimount-a/items/4784439d1275abd4b9cc)
    - [Postfix設定パラメータ(公式ドキュメント日本語訳)](https://www.postfix-jp.info/trans-2.3/jhtml/postconf.5.html)
    - [Amazon SESSMTPエンドポイントへの接続(SES公式ドキュメント)](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/smtp-connect.html)
- ClamAVの使い方
    - [Linux向けウィルス対策ソフトClamAVの使い方](https://zenn.dev/gladevise/articles/clamav-usage)
- スクリプトの書き方
    - [ClamAVによるリアルタイムスキャンの設定](https://inaba-serverdesign.jp/blog/20210409/clamav-realtime-scan.html)
    - [ClamAVによる定期ウイルススキャンの運用における工夫](https://inaba-serverdesign.jp/blog/20170919/clamav_scan_virus_manage.html)
    - [シェルスクリプトでmailコマンドによるメール送信](https://www.tetsis.com/blog/sh-mail/)
- EC2インスタンスでのメモリ不足回避方法
    - [EC2をt2.microで立てたときにやることメモ](https://qiita.com/teracy164/items/c1dfc09901c7f242a13f)