diff --git a/.gitignore b/.gitignore index 89f65eb..7dfcf7e 100644 --- a/.gitignore +++ b/.gitignore @@ -107,12 +107,8 @@ ENV/ # I/O files output/ -*.zip -*.gz *.csv *.xls* -*.eml -*.msg # LibreOffice lock files .~* diff --git a/.travis.yml b/.travis.yml index cb9f03c..bce20d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python -sudo: false +sudo: true python: - '3.4' @@ -8,8 +8,11 @@ python: - '3.6' # commands to install dependencies +before_install: + - "sudo apt-get update" + - "sudo apt-get install -y libemail-outlook-message-perl" + install: - - "pip install flake8 pytest-cov pytest coveralls" - "pip install -r requirements.txt" # commands to run samples diff --git a/README.rst b/README.rst index af02632..d77810f 100644 --- a/README.rst +++ b/README.rst @@ -31,33 +31,50 @@ Features Resources ========= +DMARC guides +------------ + * `Demystifying DMARC`_ - A complete guide to SPF, DKIM, and DMARC +SPF and DMARC record validation +------------------------------- + +If you are looking for SPF and DMARC record validation and parsing, +check out the sister project, +`checkdmarc `_. + +Lookalike domains +----------------- + +DMARC protects against domain spoofing, not lookalike domains. for open source +lookalike domain monitoring, check out `DomainAware `_. + + CLI help ======== :: -usage: parsedmarc [-h] [-o OUTPUT] [-n NAMESERVERS [NAMESERVERS ...]] - [-t TIMEOUT] [-H HOST] [-u USER] [-p PASSWORD] - [--imap-port IMAP_PORT] [--imap-no-ssl] [-r REPORTS_FOLDER] - [-a ARCHIVE_FOLDER] [-d] - [-E [ELASTICSEARCH_HOST [ELASTICSEARCH_HOST ...]]] - [--elasticsearch-index-prefix ELASTICSEARCH_INDEX_PREFIX] - [--elasticsearch-index-suffix ELASTICSEARCH_INDEX_SUFFIX] - [--hec HEC] [--hec-token HEC_TOKEN] [--hec-index HEC_INDEX] - [--hec-skip-certificate-verification] - [-K [KAFKA_HOSTS [KAFKA_HOSTS ...]]] - [--kafka-aggregate-topic KAFKA_AGGREGATE_TOPIC] - [--kafka-forensic_topic KAFKA_FORENSIC_TOPIC] - [--save-aggregate] [--save-forensic] [-O OUTGOING_HOST] - [-U OUTGOING_USER] [-P OUTGOING_PASSWORD] - [--outgoing-port OUTGOING_PORT] - [--outgoing-ssl OUTGOING_SSL] [-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 ...]] + usage: parsedmarc [-h] [-o OUTPUT] [-n NAMESERVERS [NAMESERVERS ...]] + [-t TIMEOUT] [-H HOST] [-u USER] [-p PASSWORD] + [--imap-port IMAP_PORT] [--imap-no-ssl] [-r REPORTS_FOLDER] + [-a ARCHIVE_FOLDER] [-d] + [-E [ELASTICSEARCH_HOST [ELASTICSEARCH_HOST ...]]] + [--elasticsearch-index-prefix ELASTICSEARCH_INDEX_PREFIX] + [--elasticsearch-index-suffix ELASTICSEARCH_INDEX_SUFFIX] + [--hec HEC] [--hec-token HEC_TOKEN] [--hec-index HEC_INDEX] + [--hec-skip-certificate-verification] + [-K [KAFKA_HOSTS [KAFKA_HOSTS ...]]] + [--kafka-aggregate-topic KAFKA_AGGREGATE_TOPIC] + [--kafka-forensic_topic KAFKA_FORENSIC_TOPIC] + [--save-aggregate] [--save-forensic] [-O OUTGOING_HOST] + [-U OUTGOING_USER] [-P OUTGOING_PASSWORD] + [--outgoing-port OUTGOING_PORT] + [--outgoing-ssl OUTGOING_SSL] [-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 @@ -145,18 +162,6 @@ usage: parsedmarc [-h] [-o OUTPUT] [-n NAMESERVERS [NAMESERVERS ...]] --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 `_. - -Lookalike domains -================= - -DMARC protects against domain spoofing, not lookalike domains. for open source -lookalike domain monitoring, check out `DomainAware `_. Sample aggregate report output ============================== @@ -255,125 +260,6 @@ 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! -Installation -============ - -``parsedmarc`` works with Python 2 or 3, but Python 3 is preferred. - -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. - -Installation using pypy3 ------------------------- - -For the best possible processing speed, consider using `parsedmarc` inside a ``pypy3`` -virtualenv. First, `download the latest version of pypy3`_. Extract it to -``/opt/pypy3`` (``sudo mkdir /opt`` if ``/opt`` does not exist), then create a -symlink: - -.. code-block:: bash - - $ sudo ln -s /opt/pypy3/bin/pypy3 /usr/local/bin/pypy3 - -Install ``virtualenv`` on your system: - -.. code-block:: bash - - $ sudo apt-get install python3-pip - $ sudo -H pip3 install -U virtualenv - -Uninstall any instance of ``parsedmarc`` that you may have installed globally - -.. code-block:: bash - - $ sudo -H pip3 uninstall -y parsedmarc - -Next, create a ``pypy3`` virtualenv for parsedmarc - - -.. code-block:: bash - - $ sudo mkdir /opt/venvs - $ cd /opt/venvs - $ sudo -H pip3 install -U virtualenv - $ sudo virtualenv --download -p /usr/local/bin/pypy3 parsedmarc - $ sudo -H /opt/venvs/parsedmarc/bin/pip3 install -U parsedmarc - $ sudo ln -s /opt/venvs/parsedmarc/bin/parsedmarc /usr/local/bin/parsedmarc - -To upgrade ``parsedmarc`` inside the virtualenv, run: - - -.. code-block:: bash - - $ sudo -H /opt/venvs/parsedmarc/bin/pip3 install -U parsedmarc - -Or, install the latest development release directly from GitHub: - -.. code-block:: bash - - $ sudo -H /opt/venvs/parsedmarc/bin/pip3 install -U git+https://github.com/domainaware/parsedmarc.git - -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 - -DNS performance ---------------- - -You can often improve performance by providing one or more local nameservers -to the CLI or function calls, as long as those nameservers return the same -records as the public DNS. - - -.. note:: - - If you do not specify any nameservers, Cloudflare's public nameservers are - used by default, **not the system's default nameservers**. - - This is done to avoid a situation where records in a local nameserver do - not match records in the public DNS. - -Testing multiple report analyzers ---------------------------------- - -If you would like to test parsedmarc and another report processing solution -at the same time, you can have up to two mailto URIs each in the rua and ruf -tags tgs in your DMARC record, separated by commas. - Documentation ============= diff --git a/docs/index.rst b/docs/index.rst index 47e1b88..4330888 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,8 +37,24 @@ Features Resources ========= +DMARC guides +------------ + * `Demystifying DMARC`_ - A complete guide to SPF, DKIM, and DMARC +SPF and DMARC record validation +------------------------------- + +If you are looking for SPF and DMARC record validation and parsing, +check out the sister project, +`checkdmarc `_. + +Lookalike domains +----------------- + +DMARC protects against domain spoofing, not lookalike domains. for open source +lookalike domain monitoring, check out `DomainAware `_. + CLI help ======== @@ -152,20 +168,6 @@ CLI help --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 `_. - -SPF and DMARC record validation -=============================== - -If you are looking for SPF and DMARC record validation and parsing, -check out the sister project, -`checkdmarc `_. - Sample aggregate report output ============================== diff --git a/requirements.txt b/requirements.txt index 6d978e4..3087d84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,8 @@ elasticsearch>=6.3.0,<7.0.0 elasticsearch-dsl>=6.2.1,<7.0.0 kafka-python flake8 +pytest +pytest-cov sphinx==1.7.9 sphinx_rtd_theme collective.checkdocs diff --git a/samples/!example.com!1538204542!1538463818.xml.sample b/samples/aggregate/!example.com!1538204542!1538463818.xml similarity index 100% rename from samples/!example.com!1538204542!1538463818.xml.sample rename to samples/aggregate/!example.com!1538204542!1538463818.xml diff --git a/samples/addisonfoods.com!example.com!1536105600!1536191999.xml.sample b/samples/aggregate/addisonfoods.com!example.com!1536105600!1536191999.xml similarity index 100% rename from samples/addisonfoods.com!example.com!1536105600!1536191999.xml.sample rename to samples/aggregate/addisonfoods.com!example.com!1536105600!1536191999.xml diff --git a/samples/aggregate/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.zip b/samples/aggregate/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.zip new file mode 100644 index 0000000..beca144 Binary files /dev/null and b/samples/aggregate/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.zip differ diff --git a/samples/example.net!example.com!1529366400!1529452799.xml.sample b/samples/aggregate/example.net!example.com!1529366400!1529452799.xml similarity index 100% rename from samples/example.net!example.com!1529366400!1529452799.xml.sample rename to samples/aggregate/example.net!example.com!1529366400!1529452799.xml diff --git a/samples/fastmail.com!example.com!1516060800!1516147199!102675056.xml.gz.sample b/samples/aggregate/fastmail.com!example.com!1516060800!1516147199!102675056.xml.gz similarity index 100% rename from samples/fastmail.com!example.com!1516060800!1516147199!102675056.xml.gz.sample rename to samples/aggregate/fastmail.com!example.com!1516060800!1516147199!102675056.xml.gz diff --git a/samples/ikea.com!example.de!1538690400!1538776800.xml.sample b/samples/aggregate/ikea.com!example.de!1538690400!1538776800.xml similarity index 100% rename from samples/ikea.com!example.de!1538690400!1538776800.xml.sample rename to samples/aggregate/ikea.com!example.de!1538690400!1538776800.xml diff --git a/samples/old_draft_from_wiki.xml.sample b/samples/aggregate/old_draft_from_wiki.xml similarity index 100% rename from samples/old_draft_from_wiki.xml.sample rename to samples/aggregate/old_draft_from_wiki.xml diff --git a/samples/usssa.com!example.com!1538784000!1538870399.xml.sample b/samples/aggregate/usssa.com!example.com!1538784000!1538870399.xml similarity index 100% rename from samples/usssa.com!example.com!1538784000!1538870399.xml.sample rename to samples/aggregate/usssa.com!example.com!1538784000!1538870399.xml diff --git a/samples/veeam.com!example.com!1530133200!1530219600.xml.sample b/samples/aggregate/veeam.com!example.com!1530133200!1530219600.xml similarity index 100% rename from samples/veeam.com!example.com!1530133200!1530219600.xml.sample rename to samples/aggregate/veeam.com!example.com!1530133200!1530219600.xml diff --git a/samples/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.sample b/samples/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.sample deleted file mode 100644 index 795e9d9..0000000 --- a/samples/estadocuenta1.infonacot.gob.mx!example.com!1536853302!1536939702!2940.xml.sample +++ /dev/null @@ -1,34 +0,0 @@ - - - - 2940 - XYZ Corporation - admin@estadocuenta1.infonacot.gob.mx - http://estadocuenta1.infonacot.gob.mx - - 1536853302 - 1536939702 - - - - example.com -

none

-
- - - 148.243.137.254 - 1 - - none - fail - fail - - - - estadocuenta1.infonacot.gob.mx - example.com - - - - -
diff --git a/samples/forensic/subject.eml b/samples/forensic/subject.eml new file mode 100644 index 0000000..04a342b --- /dev/null +++ b/samples/forensic/subject.eml @@ -0,0 +1,95 @@ +MIME-Version: 1.0 +Received: from mailrelay.de ([234.234.234.234]) + by Servername.domain.local (IBM Domino Release 9.0.1FP10 HF197) + with ESMTP id 2018100111202767-369529 ; + Mon, 1 Oct 2018 11:20:27 +0200 +Return-Path: +Received: from [127.0.0.1] ([local]) + by mailrelay.de (envelope-from ) + (ecelerity 4.2.39.63080 r(Core:4.2.39.2)) with UNKNOWN + id 48/E7-30937-BD6E1BB5; Mon, 01 Oct 2018 11:20:27 +0200 +Subject: DMARC Failure Report for domain.de (mail-from=sharepoint@domain.de, ip=10.10.10.10) +To: dmarc-report@domain.de +From: dmarc-report@domain.de +X-MIMETrack: Itemize by SMTP Server on Servername/DOMAIN(Release 9.0.1FP10 HF197|April + 16, 2018) at 01.10.2018 11:20:27, + Serialize by Notes Client on Peter Pan/DOMAIN(Release 9.0.1|October + 14, 2013) at 05.10.2018 23:38:45 +X-Notes-Item: Memo; + name=Form +Date: Mon, 1 Oct 2018 11:20:27 +0200 +X-Notes-Item: CN=Servername/O=DOMAIN; + type=501; flags=44; name=$UpdatedBy +Message-ID: +X-Notes-Item: CN=Servername/O=DOMAIN, + CN=DE9899SL4/O=DOMAIN; + type=501; flags=0; name=RouteServers +X-Notes-Item: =?UTF-8?B?MDEtT2N0LTIwMTggMTE6MjA6MjcgQ0VEVC8wMS1PY3QtMjAxOCAxMToyMDo=?= + =?UTF-8?B?MjcgQ0VEVCwgMDEtT2N0LTIwMTggMTE6MjA6MjcgQ0VEVC8wMS1PY3Qt?= + =?UTF-8?B?MjAxOCAxMToyMDoyNyBDRURU?=; + type=401; flags=0; name=RouteTimes +X-Notes-Item: 587285BA:CB01D107-C1258319:00334FCF; + type=4; name=$Orig +X-Notes-Item: ; + type=501; name=Categories +X-Notes-Item: ; + type=401; name=$Revisions +X-Notes-Item: Mon, 1 Oct 2018 11:20:27 +0200; + type=400; name=DeliveredDate +X-Notes-Item: =?UTF-8?B?VGhpcyBpcyBhbiBlbWFpbCBhYnVzZSByZXBvcnQgZm9yIGFuIGVtYWls?= + =?UTF-8?B?IG1lc3NhZ2UgcmVjZWl2ZWQgZnJvbSBJUCAxMC4yLjMwLjEwMiBvbiBNb24=?= + =?UTF-8?B?LCAwMSBPY3QgMjAxOA==?=; + flags=6; name=$Abstract +X-Notes-Item: 587285BA:CB01D107-C1258319:00334FCF; + type=4; name=$TUA +X-Notes-Item: 1; + name=$NoteHasNativeMIME +Content-Type: multipart/report; report-type=feedback-report; + boundary="_----jqB1YyrX3TKBNru++5PX3w===_48/E7-30937-BD6E1BB5" + +--_----jqB1YyrX3TKBNru++5PX3w===_48/E7-30937-BD6E1BB5 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; charset="US-ASCII" + +This is an email abuse report for an email message received from IP 10.10.10.10 on Mon, 01 Oct 2018 11:20:27 +0200. +The message below did not meet the sending domain's DMARC policy. +For more information about this format please see http://tools.ietf.org/html/rfc6591 . + +--_----jqB1YyrX3TKBNru++5PX3w===_48/E7-30937-BD6E1BB5 +Content-Type: message/feedback-report; name=report + +Feedback-Type: auth-failure +User-Agent: Lua/1.0 +Version: 1.0 +Original-Mail-From: sharepoint@domain.de +Original-Rcpt-To: peter.pan@domain.de +Arrival-Date: Mon, 01 Oct 2018 11:20:27 +0200 +Message-ID: <38.E7.30937.BD6E1BB5@ mailrelay.de> +Authentication-Results: dmarc=fail (p=none, dis=none) header.from=domain.de +Source-IP: 10.10.10.10 +Delivery-Result: smg-policy-action +Auth-Failure: dmarc +Reported-Domain: domain.de + +--_----jqB1YyrX3TKBNru++5PX3w===_48/E7-30937-BD6E1BB5 +Content-Type: message/rfc822 +Content-Disposition: inline + +Received: from Servernameone.domain.local (Servernameone.domain.local [10.10.10.10]) + by mailrelay.de (mail.DOMAIN.de) with SMTP id 38.E7.30937.BD6E1BB5; Mon, 1 Oct 2018 11:20:27 +0200 (CEST) +Date: 01 Oct 2018 11:20:27 +0200 +Message-ID: <38.E7.30937.BD6E1BB5@ mailrelay.de> +To: +from: "=?utf-8?B?SW50ZXJha3RpdmUgV2V0dGJld2VyYmVyLcOcYmVyc2ljaHQ=?=" +Subject: Subject +MIME-Version: 1.0 +X-Mailer: Microsoft SharePoint Foundation 2010 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + + + +--_----jqB1YyrX3TKBNru++5PX3w===_48/E7-30937-BD6E1BB5-- \ No newline at end of file diff --git a/tests.py b/tests.py index 0a727fa..ddcb915 100644 --- a/tests.py +++ b/tests.py @@ -8,15 +8,28 @@ import parsedmarc class Test(unittest.TestCase): - def testSamples(self): - """Test sample aggregate DMARC reports""" - sample_paths = glob("samples/*.sample") - for sample_path in sample_paths: - print("Testing {0}...\n".format(sample_path)) - parsed_report = parsedmarc.parse_aggregate_report_file(sample_path) - print(json.dumps(parsed_report, ensure_ascii=False, indent=2)) - print("\n") - print(parsedmarc.parsed_aggregate_reports_to_csv(parsed_report)) + class Test(unittest.TestCase): + def testAggregateSamples(self): + """Test sample aggregate.rua DMARC reports""" + sample_paths = glob("samples/aggregate/*") + for sample_path in sample_paths: + print("Testing {0}...\n".format(sample_path)) + parsed_report = parsedmarc.parse_aggregate_report_file( + sample_path) + print(json.dumps(parsed_report, ensure_ascii=False, indent=2)) + print("\n") + print( + parsedmarc.parsed_aggregate_reports_to_csv(parsed_report)) + + def testForensicSamples(self): + """Test sample forensic/ruf/failure DMARC reports""" + sample_paths = glob("samples/forensic/*.eml") + for sample_path in sample_paths: + print("Testing {0}...\n".format(sample_path)) + parsed_report = parsedmarc.parse_report_email(sample_path) + print(json.dumps(parsed_report, ensure_ascii=False, indent=2)) + print("\n") + print(parsedmarc.parsed_forensic_reports_to_csv(parsed_report)) if __name__ == "__main__":