LoginSignup
0
0

DMARCレポートをdmarc-visualizerで可視化する

Last updated at Posted at 2024-02-22

DMARCレポートをdmarc-visualizerで可視化する

Gmail様の仰せに従ってせっかくメールサーバーの設定を変更してDKIMやDMARCに対応してGoogle様から日々DMARCのレポートが送られてくるのに、それをただただメールボックスの肥やしにするのは勿体無い!
と言うことで、DMARCレポートを可視化してくれるdmarc-visualizerをインストールしてみました

dmarc-visualizerについて

Githubのdmarc-visualizerページ
https://github.com/debricked/dmarc-visualizer

[dmarc-visualizerのブログ](https://debricked.com/blog/analyze-and-visualize-dmarc-report/)で解説されているようにdmarc-visualizerはDMARCレポートを解析するparsedmarc、解析した情報を格納・検索するためのelasticsearch、elasticsearchに格納されたデータをグラフ化するためのgrafanaの3つのコンテナで構成されています

dmarc-visualizerのインストールと実行

dmarc-visualizerのインストールはdocker compose一発で必要なコンテナイメージがDocker Hubからインストールされるので簡単に利用を開始することができます
と言ったものの、実際にはdmarc-visualizerがすでに2年以上もメンテナンスされていないこともあって、諸々の関連アプリkーションのバージョンを調整しないと動作しませんでした。

不要な設定の削除とelasticsearchのバージョンアップ(8.12.0)

docker-compose.yml
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,22 +5,18 @@ services:
     volumes:
       - ./files:/input:ro
       - ./output_files:/output
-      - ./parsedmarc/parsedmarc.ini:/parsedmarc.ini:ro
-      - /etc/localtime:/etc/localtime:ro
     command: parsedmarc -c /parsedmarc.ini /input/* --debug
-    environment:
-      - TZ=Asia/Tokyo
     depends_on:
       - elasticsearch
     restart: on-failure

   elasticsearch:
-    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.5
+    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
     environment:
       - discovery.type=single-node
-      - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
+      - xpack.security.enabled=false
     volumes:
-      - elasticsearch-data:/usr/share/elasticsearch/data
+      - ./elastic_data:/usr/share/elasticsearch/data

   grafana:
     build: ./grafana/
@@ -30,7 +26,3 @@ services:
     environment:
       GF_INSTALL_PLUGINS: grafana-piechart-panel,grafana-worldmap-panel
       GF_AUTH_ANONYMOUS_ENABLED: 'true'
-
-volumes:
-  elasticsearch-data:
-    driver: local

grafanaの最新版へのバージョンアップ

grafana/Dockerfile
--- a/grafana/Dockerfile
+++ b/grafana/Dockerfile
@@ -1,4 +1,4 @@
-FROM grafana/grafana:8.5.2
+FROM grafana/grafana:latest

 ADD --chown=grafana:root https://raw.githubusercontent.com/domainaware/parsedmarc/master/grafana/Grafana-DMARC_Reports.json /var/lib/grafana/dashboards/
 RUN chmod 644 /etc/grafana/provisioning

elasticsearchのバージョン設定の変更

grafana/grafana-provisioning/datasources/all.yml
--- a/grafana/grafana-provisioning/datasources/all.yml
+++ b/grafana/grafana-provisioning/datasources/all.yml
@@ -9,7 +9,7 @@ datasources:
   database: '[dmarc_aggregate-]YYYY-MM-DD'
   isDefault: true
   jsonData:
-    esVersion: 70
+    esVersion: 8.12.0
     timeField: 'date_range'
     interval: 'Daily'
   version: 1
@@ -22,7 +22,7 @@ datasources:
   database: '[dmarc_forensic-]YYYY-MM-DD'
   isDefault: false
   jsonData:
-    esVersion: 70
+    esVersion: 8.12.0
     timeField: 'arrival_date'
     interval: 'Daily'
   version: 1

Pythonのバージョンアップ(3.9.18-alpine3.19)とmsgraph-coreのバージョンダウン(<1.0.0)

parsedmarc/Dockerfile
--- a/parsedmarc/Dockerfile
+++ b/parsedmarc/Dockerfile
@@ -1,7 +1,6 @@
-FROM python:3.9-alpine3.16
+FROM python:3.9.18-alpine3.19

-RUN apk add build-base libxml2-dev libxslt-dev libffi-dev tzdata \
-    && pip install parsedmarc
+RUN apk add --update --no-cache libxml2-dev libxslt-dev build-base libffi-dev \
+    && pip install parsedmarc 'msgraph-core<1.0.0'

 COPY parsedmarc.ini /
-#COPY GeoLite2-Country.mmdb /usr/share/GeoIP/GeoLite2-Country.mmdb

コンテナのビルド

上記のように必要な設定ファイルの修正が終わったならばdocker compose buildで必要なコンテナをビルドします

docker build

macOSではDocker Hubのアクセスなどでキーチェーンの情報にアクセスする必要があるため、sshでリモートログインしている場合には以下のようにキーチェーンのロックを解除するように要求される場合があるので、その場合にはsecurity -v unlock-keychainでコンテナのビルドに先立ってキーチェーンをアンロックしておいてください

macOSの場合

macOSでsshリモートログインしてDocker Hubを使うとキーチェーンをアンロックできずエラーになる

docker compose build
sshでのリモートだとエラーになる
[+] Building 0.6s (4/4) FINISHED                           docker:desktop-linux
 => [grafana internal] load build definition from Dockerfile               0.0s
 => => transferring dockerfile: 316B                                       0.0s
 => [parsedmarc internal] load build definition from Dockerfile            0.0s
 => => transferring dockerfile: 224B                                       0.0s
 => ERROR [grafana internal] load metadata for docker.io/grafana/grafana:  0.6s
 => ERROR [parsedmarc internal] load metadata for docker.io/library/pytho  0.6s
------
 > [grafana internal] load metadata for docker.io/grafana/grafana:latest:
------
------
 > [parsedmarc internal] load metadata for docker.io/library/python:3.9.18-alpine3.19:
------
failed to solve: grafana/grafana:latest: error getting credentials - err: exit status 1, out: `error getting credentials - err: exit status 1, out: `keychain cannot be accessed because the current session does not allow user interaction. The keychain may be locked; unlock it by running "security -v unlock-keychain ~/Library/Keychains/login.keychain-db" and try again``
キーチェーンのアンロック

事前にキーチェーンをアンロックしておく

security -v unlock-keychain ~/Library/Keychains/login.keychain-db
キーチェーンのパスワードを聞かれるので入力
unlock-keychain "/Users/user/Library/Keychains/login.keychain-db"
password to unlock /Users/user/Library/Keychains/login.keychain-db:

parsedmarcの設定ファイルを作成

DMARCレポートを解析するためのコンテナparsedmarcの設定ファイルのサンプルを元に自分が使う環境に合わせて設定ファイルparsedmarc.iniを作成します

parsedmarcのサンプル設定ファイルparsedmarc.sample.iniparsedmarc.iniにコピーします

cp parsedmarc/parsedmarc.sample.ini parsedmarc/parsedmarc.ini

parsedmarc設定のドキュメント

設定ファイルの使い方についてはparsedmarcのGithubページparsedmarcのドキュメントファイルを参考にして設定します

ローカルのDMARCレポートファイルを読み込んでレポートを生成する場合

DMARCレポートは事前に何らかの方法でDMARCレポートを受信しているメールサーバーからダウンロードしておき、dmarc-visualizerを展開したフォルダーにfilesという名前のフォルダを作り、ZIP付圧縮されたままの状態で構わないのでDMARCレポートの添付ファイルをこのフォルダ内にコピーしておきます

parsedmarc/parsedmarc.ini
[general]
save_aggregate = False
save_forensic = False

[elasticsearch]
hosts = elasticsearch:9200
ssl = False
IMAP4サーバーから直接DMARCのレポートメールを読み込んでレポートを生成する場合

ただし、parsedmarcのIMAP4クライアントは複数のメールサーバーへのアクセスなど、細かな設定はできないので別途自分でスクリプトを書くなどしてDMARCレポートのメールをダウンロードする方が使いやすいと感じました

parsedmarc/parsedmarc.ini
[general]
save_aggregate = True
save_forensic = True
output = /output/

[imap]
host = mail.example.com
user = dmarc@mail.example.com
password = password
watch = True

[elasticsearch]
hosts = elasticsearch:9200
ssl = False
IMAP4サーバーからDMARCレポートを取得するスクリプト

以下はPythonでちょろちょろっと書いた複数のIMAPサーバーからDMARCレポートをダウンロードしてfilesフォルダに集めるためのスクリプトです
先の「ローカルのDMARCレポートファイルを読み込んでレポートを生成する場合」の設定との組み合わせで利用することが出来ます
スクリプトはGmailからのDMARCレポートをZIP圧縮で受け取った場合にしか配慮していないし、パスワードとかハードコードなので色々ツッコミどころ満載ですが、とりあえずの実験用です
このスクリプトでIMAPサーバーからDMARCレポートを取得後にdocker compose upでコンテナを起動することでダウンロードしたレポートを可視化することが出来ます

fetch_dmarc_reports.py
#!/usr/bin/python3

import imaplib
import email
from email.header import decode_header, make_header
import re

# IMAPユーザー、サーバー、パスワードのペア
imap_accounts = {
    'dmarc-reports@hogehoge.tld': ['imap.hogehoge.tld', 'ABCdEFGhIJ'],
    'dmarc-reports@gmail.com': ['imap.gmail.com', 'abcd 1234 EFgH IjkL']}

# IMAPサーバーを順次処理
for imap_user in imap_accounts:
    imap_server = imap_accounts[imap_user][0]
    imap_password = imap_accounts[imap_user][1]
    imap_search_subject = imap_accounts[imap_user][2]

    # Gmailにログイン
    # ログインにはGmailで発行したアプリパスワードが必要なことに注意
    imap = imaplib.IMAP4_SSL(imap_server)
    imap.login(imap_user, imap_password)

    # IMAP4サーバー(Gmail)からメールボックス一覧を取得
    mboxes = imap.list()
    print(mboxes)

    # メールボックスを順次処理
    for mbox in mboxes[1]:
        # パターンマッチしたいのでバイナリから文字列に
        s = mbox.decode()
        print(s)
        # メールボックスの情報を属性、階層区切り文字、メールボックス名に分割
        m = re.match('\((\\\\.*)\) (".*") (.*)$', s)
        mbox_attribute = m.group(1)
        # 属性が '\Noselect' (選択不可)ならスキップ
        if "\\Noselect" in mbox_attribute:
            continue
        mbox_name = m.group(3)
        # メールボックスを選択
        imap.select(mbox_name)
        # メールボックスからDMARCのレポートを検索
        response, messages = imap.search(None, imap_search_subject)
        messages = messages[0].split(b' ')
        # 検索したメールを順番に処理
        for mail in messages:
            # メールの内容を取得できなければスキップ
            if not mail:
                continue
            _, data = imap.fetch(mail, '(RFC822)')
            # メールの中身をパースしてmsg構造体に格納
            msg = email.message_from_bytes(data[0][1])
            # Subjectからレポート対象ドメインを取得、レポート送信者
            # 、レポートIDを取得
            subject = str(make_header(decode_header(msg["Subject"])))
            subject.replace('\n', '')
            print(subject)
            # と思ったが、予想以上にSubjectのフォーマットが複雑なので後回しw
            #m = re.match('Report domain: (.*) Submitter: (.*) Report-ID: (.*)$', subject)
            #if m is not None:
            #    report_domain = m.group(1)

            # メッセージのパートから添付ファイルを検出
            for part in msg.walk():
                filename = part.get_filename()
                if (filename and
                    (part.get_content_type() == 'application/zip' or
                     part.get_content_type() == 'application/gzip')):
                    # ZIPファイル(DKIMレポート)や
                    #.gzファイル(Aggregate Report)が添付されていたら
                    # ファイルに保存
                    with open('files/' + filename, 'wb') as f:
                        f.write(part.get_payload(decode=True))

imap.close()
imap.logout()

DMARCレポート解析用コンテナの設定ファイル(parsedmarc.ini)が用意できてfilesの下へとDMARCレポートをダウンロードしたならdocker compose upでコンテナを実行しレポートを生成すします

docker compose up -d
[+] Running 11/11
 ✔ elasticsearch 10 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled            24.1s
   ✔ e4d291082c72 Pull complete                                            1.2s
   ✔ e6947a0eeb2c Pull complete                                            0.8s
   ✔ af7f7cf6eb22 Pull complete                                            0.6s
   ✔ 89732bc75041 Pull complete                                            1.1s
   ✔ d38d8a4afa78 Pull complete                                           10.6s
   ✔ fb65ac828db6 Pull complete                                            1.6s
   ✔ d3b6064b8ab8 Pull complete                                            1.7s
   ✔ f8484251cc74 Pull complete                                            2.1s
   ✔ 9ffe1c016006 Pull complete                                            2.4s
   ✔ e40fcac62c91 Pull complete                                            2.6s
[+] Running 3/4
 ⠙ Network dmarc-visualizer_default            Created                     1.1s
 ✔ Container dmarc-visualizer-grafana-1        Started                     0.8s
 ✔ Container dmarc-visualizer-elasticsearch-1  Started                     0.8s
 ✔ Container dmarc-visualizer-parsedmarc-1     Started                     0.5s

docker composeでのコンテナ起動直後にはDMARCレポートを解析するためのparsedmarc、データの格納と検索のためのelasticsearch、解析後にelasticsearchに格納されたデータをグラフ化するためのgrafanaの3つのコンテナが起動されます

docker ps
docker psからの応答
CONTAINER ID   IMAGE                                                  COMMAND                   CREATED         STATUS                  PORTS                    NAMES
0a5f47b805f4   dmarc-visualizer-parsedmarc                            "parsedmarc -c /pars…"   3 seconds ago   Up Less than a second                            dmarc-visualizer-parsedmarc-1
35300cde4ad9   docker.elastic.co/elasticsearch/elasticsearch:8.12.0   "/bin/tini -- /usr/l…"   3 seconds ago   Up 2 seconds            9200/tcp, 9300/tcp       dmarc-visualizer-elasticsearch-1
99e791bd0010   dmarc-visualizer-grafana                               "/run.sh"                 3 seconds ago   Up 2 seconds            0.0.0.0:3000->3000/tcp   dmarc-visualizer-grafana-1

parsedmarcによるDMARCレポートの解析が終わるとコンテナparsedmarcは終了し、elasticsearchとgrafinaの2つのコンテナだけが残ります

docker ps
docker psからの応答
CONTAINER ID   IMAGE                                                  COMMAND                   CREATED          STATUS          PORTS                    NAMES
35300cde4ad9   docker.elastic.co/elasticsearch/elasticsearch:8.12.0   "/bin/tini -- /usr/l…"   36 seconds ago   Up 35 seconds   9200/tcp, 9300/tcp       dmarc-visualizer-elasticsearch-1
99e791bd0010   dmarc-visualizer-grafana                               "/run.sh"                 36 seconds ago   Up 35 seconds   0.0.0.0:3000->3000/tcp   dmarc-visualizer-grafana-1

解析したDMARCレポートをブラウザで表示する

コンテナを立ち上げたホストOSで http://localhost:3000/ へとアクセスすることでgrafanaのレポートを表示することが出来ます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0