mirror of
https://github.com/domainaware/parsedmarc.git
synced 2026-03-12 01:31:26 +00:00
551 lines
18 KiB
ReStructuredText
551 lines
18 KiB
ReStructuredText
.. parsedmarc documentation master file, created by
|
|
sphinx-quickstart on Mon Feb 5 18:25:39 2018.
|
|
You can adapt this file completely to your liking, but it should at least
|
|
contain the root `toctree` directive.
|
|
|
|
======================================
|
|
Welcome to parsedmarc's documentation!
|
|
======================================
|
|
|
|
|Build Status|
|
|
|
|
.. image:: _static/screenshots/dmarc-summary-charts.png
|
|
:alt: A screenshot of DMARC summary charts in Kibana
|
|
:scale: 50 %
|
|
:align: center
|
|
:target: _static/screenshots/dmarc-summary-charts.png
|
|
|
|
``parsedmarc`` is a Python module and CLI utility for parsing DMARC reports.
|
|
|
|
Features
|
|
========
|
|
|
|
* Parses draft and 1.0 standard aggregate reports
|
|
* Parses forensic reports
|
|
* Can parse reports from an inbox over IMAP
|
|
* Transparently handles gzip or zip compressed reports
|
|
* Consistent data structures
|
|
* Simple JSON and/or CSV output
|
|
* Optionally email the results
|
|
* Optionally send the results to Elasticsearch, for use with premade Kibana dashboards
|
|
|
|
CLI help
|
|
========
|
|
|
|
::
|
|
|
|
usage: parsedmarc [-h] [-o OUTPUT] [-n NAMESERVERS [NAMESERVERS ...]]
|
|
[-t TIMEOUT] [-H HOST] [-u USER] [-p PASSWORD]
|
|
[-r REPORTS_FOLDER] [-a ARCHIVE_FOLDER] [-d]
|
|
[-E [ELASTICSEARCH_HOST [ELASTICSEARCH_HOST ...]]]
|
|
[--save-aggregate] [--save-forensic] [-O OUTGOING_HOST]
|
|
[-U OUTGOING_USER] [-P OUTGOING_PASSWORD] [-F OUTGOING_FROM]
|
|
[-T OUTGOING_TO [OUTGOING_TO ...]] [-S OUTGOING_SUBJECT]
|
|
[-A OUTGOING_ATTACHMENT] [-M OUTGOING_MESSAGE] [-w] [--test]
|
|
[-s] [--debug] [-v]
|
|
[file_path [file_path ...]]
|
|
|
|
Parses DMARC reports
|
|
|
|
positional arguments:
|
|
file_path one or more paths to aggregate or forensic report
|
|
files or emails
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-o OUTPUT, --output OUTPUT
|
|
Write output files to the given directory
|
|
-n NAMESERVERS [NAMESERVERS ...], --nameservers NAMESERVERS [NAMESERVERS ...]
|
|
nameservers to query (Default 8.8.8.8 4.4.4.4)
|
|
-t TIMEOUT, --timeout TIMEOUT
|
|
number of seconds to wait for an answer from DNS
|
|
(default 6.0)
|
|
-H HOST, --host HOST IMAP hostname or IP address
|
|
-u USER, --user USER IMAP user
|
|
-p PASSWORD, --password PASSWORD
|
|
IMAP password
|
|
-r REPORTS_FOLDER, --reports-folder REPORTS_FOLDER
|
|
The IMAP folder containing the reports Default: INBOX
|
|
-a ARCHIVE_FOLDER, --archive-folder ARCHIVE_FOLDER
|
|
Specifies the IMAP folder to move messages to after
|
|
processing them Default: Archive
|
|
-d, --delete Delete the reports after processing them
|
|
-E [ELASTICSEARCH_HOST [ELASTICSEARCH_HOST ...]], --elasticsearch-host [ELASTICSEARCH_HOST [ELASTICSEARCH_HOST ...]]
|
|
A list of one or more Elasticsearch hostnames or URLs
|
|
to use (Default localhost:9200)
|
|
--save-aggregate Save aggregate reports to Elasticsearch
|
|
--save-forensic Save forensic reports to Elasticsearch
|
|
-O OUTGOING_HOST, --outgoing-host OUTGOING_HOST
|
|
Email the results using this host
|
|
-U OUTGOING_USER, --outgoing-user OUTGOING_USER
|
|
Email the results using this user
|
|
-P OUTGOING_PASSWORD, --outgoing-password OUTGOING_PASSWORD
|
|
Email the results using this password
|
|
-F OUTGOING_FROM, --outgoing-from OUTGOING_FROM
|
|
Email the results using this from address
|
|
-T OUTGOING_TO [OUTGOING_TO ...], --outgoing-to OUTGOING_TO [OUTGOING_TO ...]
|
|
Email the results to these addresses
|
|
-S OUTGOING_SUBJECT, --outgoing-subject OUTGOING_SUBJECT
|
|
Email the results using this subject
|
|
-A OUTGOING_ATTACHMENT, --outgoing-attachment OUTGOING_ATTACHMENT
|
|
Email the results using this filename
|
|
-M OUTGOING_MESSAGE, --outgoing-message OUTGOING_MESSAGE
|
|
Email the results using this message
|
|
-w, --watch Use an IMAP IDLE connection to process reports as they
|
|
arrive in the inbox
|
|
--test Do not move or delete IMAP messages
|
|
-s, --silent Only print errors
|
|
--debug Print debugging information
|
|
-v, --version show program's version number and exit
|
|
|
|
SPF and DMARC record validation
|
|
===============================
|
|
|
|
If you are looking for SPF and DMARC record validation and parsing,
|
|
check out the sister project, `checkdmarc <https://domainaware.github.io/checkdmarc/>`_.
|
|
|
|
Sample aggregate report output
|
|
==============================
|
|
|
|
Here are the results from parsing the `example <https://dmarc.org/wiki/FAQ#I_need_to_implement_aggregate_reports.2C_what_do_they_look_like.3F>`_
|
|
report from the dmarc.org wiki. It's actually an older draft of the the 1.0
|
|
report schema standardized in
|
|
`RFC 7480 Appendix C <https://tools.ietf.org/html/rfc7489#appendix-C>`_.
|
|
This draft schema is still in wide use.
|
|
|
|
``parsedmarc`` produces consistent, normalized output, regardless of the report
|
|
schema.
|
|
|
|
JSON
|
|
----
|
|
|
|
.. code-block:: json
|
|
|
|
{
|
|
"xml_schema": "draft",
|
|
"report_metadata": {
|
|
"org_name": "acme.com",
|
|
"org_email": "noreply-dmarc-support@acme.com",
|
|
"org_extra_contact_info": "http://acme.com/dmarc/support",
|
|
"report_id": "9391651994964116463",
|
|
"begin_date": "2012-04-27 20:00:00",
|
|
"end_date": "2012-04-28 19:59:59",
|
|
"errors": []
|
|
},
|
|
"policy_published": {
|
|
"domain": "example.com",
|
|
"adkim": "r",
|
|
"aspf": "r",
|
|
"p": "none",
|
|
"sp": "none",
|
|
"pct": "100",
|
|
"fo": "0"
|
|
},
|
|
"records": [
|
|
{
|
|
"source": {
|
|
"ip_address": "72.150.241.94",
|
|
"country": "US",
|
|
"reverse_dns": "adsl-72-150-241-94.shv.bellsouth.net",
|
|
"base_domain": "bellsouth.net"
|
|
},
|
|
"count": 2,
|
|
"policy_evaluated": {
|
|
"disposition": "none",
|
|
"dkim": "fail",
|
|
"spf": "pass",
|
|
"policy_override_reasons": []
|
|
},
|
|
"identifiers": {
|
|
"header_from": "example.com",
|
|
"envelope_from": "example.com",
|
|
"envelope_to": null
|
|
},
|
|
"auth_results": {
|
|
"dkim": [
|
|
{
|
|
"domain": "example.com",
|
|
"selector": "none",
|
|
"result": "fail"
|
|
}
|
|
],
|
|
"spf": [
|
|
{
|
|
"domain": "example.com",
|
|
"scope": "mfrom",
|
|
"result": "pass"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
CSV
|
|
---
|
|
|
|
::
|
|
|
|
xml_schema,org_name,org_email,org_extra_contact_info,report_id,begin_date,end_date,errors,domain,adkim,aspf,p,sp,pct,fo,source_ip_address,source_country,source_reverse_dns,source_base_domain,count,disposition,dkim_alignment,spf_alignment,policy_override_reasons,policy_override_comments,envelope_from,header_from,envelope_to,dkim_domains,dkim_selectors,dkim_results,spf_domains,spf_scopes,spf_results
|
|
draft,acme.com,noreply-dmarc-support@acme.com,http://acme.com/dmarc/support,9391651994964116463,2012-04-27 20:00:00,2012-04-28 19:59:59,,example.com,r,r,none,none,100,0,72.150.241.94,US,adsl-72-150-241-94.shv.bellsouth.net,bellsouth.net,2,none,fail,pass,,,example.com,example.com,,example.com,none,fail,example.com,mfrom,pass
|
|
|
|
|
|
Sample forensic report output
|
|
=============================
|
|
|
|
I don't have a sample I can share for privacy reasons. If you have a sample
|
|
forensic report that you can share publicly, please contact me!
|
|
|
|
Bug reports
|
|
===========
|
|
|
|
Please report bugs on the GitHub issue tracker
|
|
|
|
https://github.com/domainaware/parsedmarc/issues
|
|
|
|
Installation
|
|
============
|
|
|
|
``parsedmarc`` works with Python 3 only.
|
|
|
|
On Debian or Ubuntu systems, run:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ sudo apt-get install python3-pip
|
|
|
|
|
|
Python 3 installers for Windows and macOS can be found at
|
|
https://www.python.org/downloads/
|
|
|
|
To install or upgrade to the latest stable release of ``parsedmarc`` on
|
|
macOS or Linux, run
|
|
|
|
.. code-block:: bash
|
|
|
|
$ sudo -H pip3 install -U parsedmarc
|
|
|
|
Or, install the latest development release directly from GitHub:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ sudo -H pip3 install -U git+https://github.com/domainaware/parsedmarc.git
|
|
|
|
.. note::
|
|
|
|
On Windows, ``pip3`` is ``pip``, even with Python 3. So on Windows, simply
|
|
substitute ``pip`` as an administrator in place of ``sudo pip3``, in the
|
|
above commands.
|
|
|
|
Optional dependencies
|
|
---------------------
|
|
|
|
If you would like to be able to parse emails saved from Microsoft Outlook
|
|
(i.e. OLE .msg files), install ``msgconvert``:
|
|
|
|
On Debian or Ubuntu systems, run:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ sudo apt-get install libemail-outlook-message-perl
|
|
|
|
Elasticsearch and Kibana
|
|
------------------------
|
|
|
|
To set up visual dashboards of DMARC data, install Elasticsearch and Kibana.
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo apt-get install -y openjdk-8-jre apt-transport-https
|
|
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
|
|
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.x.list
|
|
sudo apt-get update
|
|
sudo apt-get install -y elasticsearch kibana
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable elasticsearch.service
|
|
sudo systemctl enable kibana.service
|
|
sudo service start elasticsearch
|
|
sudo service start kibana
|
|
|
|
Without the commercial X-Pack_, Kibana does not have any authentication
|
|
mechanism of its own. You can use nginx as a reverse proxy that provides basic
|
|
authentication.
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo apt-get install -y nginx apache2-utils
|
|
|
|
Create a directory to store the certificates and keys:
|
|
|
|
.. code-block:: bash
|
|
|
|
mkdir ~/ssl
|
|
cd ~/ssl
|
|
|
|
To create a self-signed certificate, run:
|
|
|
|
.. code-block:: bash
|
|
|
|
openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout kibana.key -out kibana.crt
|
|
|
|
Or, to create a Certificate Signing Request (CSR) for a CA, run:
|
|
|
|
.. code-block:: bash
|
|
|
|
openssl req -newkey rsa:4096-nodes -keyout kibana.key -out kibana.csr
|
|
|
|
Fill in the prompts. Watch out for Common Name (e.g. server FQDN or YOUR
|
|
domain name), which is the IP address or domain name that you will be hosting
|
|
Kibana on. it is the most important field.
|
|
|
|
If you generated a CSR, remove the CSR after you have your certs
|
|
|
|
.. code-block:: bash
|
|
|
|
rm -f kibana.csr
|
|
|
|
|
|
Move the keys into place and secure them:
|
|
|
|
.. code-block:: bash
|
|
|
|
cd
|
|
sudo mv ssl /etc/nginx
|
|
sudo chown -R root:www-data /etc/nginx/ssl
|
|
sudo chmod -R u=rX,g=rX,o= /etc/nginx/ssl
|
|
|
|
Disable the default nginx configuration:
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo rm /etc/nginx/sites-enabled/default
|
|
|
|
Create the web server configuration
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo nano /etc/nginx/sites-available/kibana
|
|
|
|
.. code-block:: nginx
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
ssl_certificate /etc/nginx/ssl/kibana.crt;
|
|
ssl_certificate_key /etc/nginx/ssl/kibana.key;
|
|
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_tickets off;
|
|
|
|
ssl_protocols TLSv1.2;
|
|
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHAC ssl_prefer_server_ciphers on;
|
|
|
|
# Uncomment this next line if you are using a signed, trusted cert
|
|
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
|
|
add_header X-Frame-Options SAMEORIGIN;
|
|
add_header X-Content-Type-Options nosniff;
|
|
auth_basic "Login required";
|
|
auth_basic_user_file /etc/nginx/htpasswd;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:5601;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
|
|
Enable the nginx configuration for Kibana:
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo ln -s /etc/nginx/sites-available/kibana /etc/nginx/sites-enabled/kibana
|
|
|
|
Add a user to basic authentication:
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo htpasswd -c /etc/nginx/htpasswd exampleuser
|
|
|
|
Where ``exampleuser`` is the name of the user you want to add.
|
|
|
|
Secure the permissions of the httpasswd file:
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo chown root:www-data /etc/nginx/htpasswd
|
|
sudo chmod u=rw,g=r,o= /etc/nginx/htpasswd
|
|
|
|
Restart nginx:
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo service nginx restart
|
|
|
|
|
|
Now that Elasticsearch is up and running, use ``parsedmarc`` to send data to
|
|
it.
|
|
|
|
Om the same system as Elasticsearch, pass ``--save-aggregate`` and/or
|
|
``--save-forensic`` to ``parsedmarc`` save the results in Elasticsearch.
|
|
|
|
.. warning::
|
|
|
|
``--save-aggregate`` and ``--save-forensic`` are separate options because
|
|
you may not want to save forensic reports to your Elasticsearch instance,
|
|
particularly if you are in a highly-regulated industry that handles
|
|
sensitive data, such as healthcare or finance. If your legitimate outgoing
|
|
email fails DMARC, it is possible that email may appear later in a
|
|
forensic report.
|
|
|
|
Forensic reports contain the original headers of an email that failed a
|
|
DMARC check, and sometimes may also include the full message body,
|
|
depending on the policy of the reporting organisation.
|
|
|
|
Most reporting organisations do not send forensic reports of any kind for
|
|
privacy reasons. While aggregate DMARC reports are sent at least daily,
|
|
it is normal to receive very few forensic reports.
|
|
|
|
|
|
When you first visit Kibana, it will prompt you to create an index pattern.
|
|
Start by creating the index pattern ``dmarc_aggregate`` (without an ``*``),
|
|
and select ``date_range`` as the time field. Once the ``dmarc_aggregate``
|
|
index pattern ``dmarc_aggregate`` has been saved, create a ``dmarc_forensic``
|
|
index pattern, with ``arrival_date`` as the time field.
|
|
|
|
.. image:: _static/screenshots/define-dmarc-aggregate.png
|
|
:alt: A screenshot of defining the dmarc_aggregate index pattern
|
|
:align: center
|
|
:target: _static/screenshots/define-dmarc-aggregate.png
|
|
|
|
.. image:: _static/screenshots/dmarc-aggregate-time-field.png
|
|
:alt: A screenshot of setting the time field for the dmarc_aggregate index pattern
|
|
:align: center
|
|
:target: _static/screenshots/dmarc-aggregate-time-field.png
|
|
|
|
.. image:: _static/screenshots/define-dmarc-forensic.png
|
|
:alt: A screenshot of defining the dmarc_forensic index pattern
|
|
:align: center
|
|
:target: _static/screenshots/define-dmarc-forensic.png
|
|
|
|
.. image:: _static/screenshots/dmarc-forensic-time-field.png
|
|
:alt: A screenshot of setting the time field for the dmarc_forensic index pattern
|
|
:align: center
|
|
:target: _static/screenshots/dmarc-forensic-time-field.png
|
|
|
|
Once the index patterns have been created, you can import the dashboards.
|
|
|
|
Download (right click the link and click save as) kibana_saved_objects.json_.
|
|
|
|
Import ``kibana_saved_objects.json`` the Saved Objects tab of the management
|
|
page of Kibana.
|
|
|
|
It will give you the option to overwrite existing saved dashboards or
|
|
visualizations, which could be used to restore them if you or someone else
|
|
breaks them, as there are no permissions/access controls in Kibana without
|
|
the commercial X-Pack_.
|
|
|
|
.. image:: _static/screenshots/saved-objects.png
|
|
:alt: A screenshot of setting the Saved Objects management UI in Kibana
|
|
:align: center
|
|
:target: _static/screenshots/saved-objects.png
|
|
|
|
.. image:: _static/screenshots/confirm-overwrite.png
|
|
:alt: A screenshot of the overwrite conformation prompt
|
|
:align: center
|
|
:target: _static/screenshots/confirm-overwrite.png
|
|
|
|
Kibana will then ask you to match the newly imported objects to your index
|
|
patterns. Select ``dmarc_forensic`` for the set of forensic objects, and
|
|
select ``dmarc_aggregate`` for the other saved objects, as shown below.
|
|
|
|
|
|
.. image:: _static/screenshots/index-pattern-conflicts.png
|
|
:alt: A screenshot showing how to resolve index pattern conflicts after importing saved objects
|
|
:align: center
|
|
:target: _static/screenshots/index-pattern-conflicts.png
|
|
|
|
Running parsedmarc as a systemd service
|
|
---------------------------------------
|
|
|
|
Use systemd to run ``parsedmarc`` as a service and process reports as they
|
|
arrive.
|
|
|
|
Create the service configuration file
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo nano /etc/systemd/system/parsedmarc.service
|
|
|
|
Edit the command line options of ``parsedmarc`` in the service's ``ExecStart``
|
|
setting to suit your needs.
|
|
|
|
.. note::
|
|
|
|
Always pass the ``--watch`` option to ``parsedmarc`` when running it as a
|
|
service. Use ``--silent`` to only log errors.
|
|
|
|
.. code-block:: ini
|
|
|
|
[Unit]
|
|
Description=parsedmarc mailbox watcher
|
|
Documentation=https://domainaware.github.io/parsedmarc/
|
|
|
|
[Service]
|
|
ExecStart=/usr/local/bin/parsedmarc --watch --silent --save-aggregate --save-forensic -H "outlook.office365.com" -u "dmarc@example.com" -p "FooBar!"
|
|
Restart=always
|
|
RestartSec=5m
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
|
|
Then, enable the service
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable parsedmarc.service
|
|
sudo service parsedmarc restart
|
|
|
|
.. note::
|
|
|
|
You must also run the above commands whenever you edit
|
|
``parsedmarc.service``.
|
|
|
|
API
|
|
===
|
|
|
|
.. automodule:: parsedmarc
|
|
:members:
|
|
|
|
parsedmarc.elastic
|
|
------------------
|
|
|
|
.. automodule:: parsedmarc.elastic
|
|
:members:
|
|
|
|
.. toctree::
|
|
:maxdepth: 2
|
|
:caption: Contents:
|
|
|
|
Indices and tables
|
|
==================
|
|
|
|
* :ref:`genindex`
|
|
* :ref:`modindex`
|
|
* :ref:`search`
|
|
|
|
|
|
.. |Build Status| image:: https://travis-ci.org/domainaware/parsedmarc.svg?branch=master
|
|
:target: https://travis-ci.org/domainaware/parsedmarc
|
|
|
|
.. _X-Pack: https://www.elastic.co/products/x-pack
|
|
|
|
.. _kibana_saved_objects.json: https://raw.githubusercontent.com/domainaware/parsedmarc/master/kibana/kibana_saved_objects.json
|