Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Postfix CentOS 7 - OpenDMARC anbinden (opendmarc-milter)
DMARC ist an sich kein eigenständiger Prozess in der e-Mail-Verarbeitung, vielmehr erweitert DMARC die Ergebnisprüfung der beiden Techniken SPF und OpenDKIM. DMARC ergänzt somit SPF und OpenDKIM, ohne die DMARC nicht funktionieren kann.
DMARC ergänzt SPF und OpenDKIM, indem festgelegt wird, was der empfangende e-Mail-Server mit einer e-Mail tun soll, welche die SPF oder OpenDKIM Prüfung nicht erfolgreich bestanden hat. Hierfür sind zwei Dinge notwendig:
- Ein DNS TXT Record, der dem empfangenden e-Mail-Server sagt, was zu tun ist
Beschreibung | Externer Link |
---|---|
Homepage | http://www.trusteddomain.org/opendmarc/ |
Dokumentation | http://www.trusteddomain.org/opendmarc/opendmarc-README |
Ab hier werden zur Ausführung nachfolgender Befehle root
-Rechte benötigt. Um der Benutzer root
zu werden, melden Sie sich bitte als root
-Benutzer am System an, oder wechseln mit nachfolgendem Befehl zum Benutzer root
:
$ su - Password:
Voraussetzungen
Die Instalaltion und korrekt Konfiguration von nachfolgneden Diensten/Daemon bzw. MILTER ist erforderlich, da ohne diese OpenDMARC nicht funktionieren kann!
- SPF,
- Siehe auch nachfolgenden internen Link: Postfix CentOS 7 - SPF anbinden (spf-milter)
-
- Siehe auch nachfolgenden internen Link: Postfix CentOS 7 - OpenDKIM anbinden (opendkim-milter)
OpenDMARC führt seine Auswertungen nach den Einträgen im e-Mail-Header welche von SPF und OpenDKIM durch und trifft aufgrund dieser Einträge auch letztendlich die Entscheidung ob eine e-Mail angenommen oder abgelehnt wird.
Herunterladen
Nachfolgend sollen das Drittanbieter-Repository von EPEL, welches wie unter nachfolgendem internen Link dargestellt, eingebunden werden:
Installation
Nachfolgendes rpm
-Paket ist zur Installation erforderlich:
Zusätzlich wird nachfolgendes rpm
-Paket als Abhängigkeit zusätzlich installiert:
Die Installation von opendmarc
, kann durch ausführen des nachfolgenden Befehls durchgeführt werden:
# yum install opendmarc Loaded plugins: changelog, priorities 149 packages excluded due to repository priority protections Resolving Dependencies --> Running transaction check ---> Package opendmarc.x86_64 0:1.3.1-13.el7 will be installed --> Processing Dependency: libopendmarc(x86-64) = 1.3.1-13.el7 for package: opendmarc-1.3.1-13.el7.x86_64 --> Processing Dependency: perl(Switch) for package: opendmarc-1.3.1-13.el7.x86_64 --> Processing Dependency: perl(DBD::mysql) for package: opendmarc-1.3.1-13.el7.x86_64 --> Processing Dependency: libopendmarc.so.2()(64bit) for package: opendmarc-1.3.1-13.el7.x86_64 --> Running transaction check ---> Package libopendmarc.x86_64 0:1.3.1-13.el7 will be installed ---> Package perl-DBD-MySQL.x86_64 0:4.023-5.el7 will be installed ---> Package perl-Switch.noarch 0:2.16-7.el7 will be installed --> Finished Dependency Resolution Changes in packages about to be updated: Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: opendmarc x86_64 1.3.1-13.el7 epel 121 k Installing for dependencies: libopendmarc x86_64 1.3.1-13.el7 epel 27 k perl-DBD-MySQL x86_64 4.023-5.el7 base 140 k perl-Switch noarch 2.16-7.el7 base 22 k Transaction Summary ================================================================================ Install 1 Package (+3 Dependent packages) Total download size: 310 k Installed size: 757 k Is this ok [y/d/N]: y Downloading packages: (1/4): libopendmarc-1.3.1-13.el7.x86_64.rpm | 27 kB 00:00 (2/4): opendmarc-1.3.1-13.el7.x86_64.rpm | 121 kB 00:00 (3/4): perl-DBD-MySQL-4.023-5.el7.x86_64.rpm | 140 kB 00:00 (4/4): perl-Switch-2.16-7.el7.noarch.rpm | 22 kB 00:00 -------------------------------------------------------------------------------- Total 712 kB/s | 310 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : perl-Switch-2.16-7.el7.noarch 1/4 Installing : libopendmarc-1.3.1-13.el7.x86_64 2/4 Installing : perl-DBD-MySQL-4.023-5.el7.x86_64 3/4 Installing : opendmarc-1.3.1-13.el7.x86_64 4/4 Verifying : perl-DBD-MySQL-4.023-5.el7.x86_64 1/4 Verifying : opendmarc-1.3.1-13.el7.x86_64 2/4 Verifying : libopendmarc-1.3.1-13.el7.x86_64 3/4 Verifying : perl-Switch-2.16-7.el7.noarch 4/4 Installed: opendmarc.x86_64 0:1.3.1-13.el7 Dependency Installed: libopendmarc.x86_64 0:1.3.1-13.el7 perl-DBD-MySQL.x86_64 0:4.023-5.el7 perl-Switch.noarch 0:2.16-7.el7 Complete!
Die Installation von opendmarc
, kann durch ausführen des nachfolgenden Befehls durchgeführt werden:
# rpm -qil opendmarc Name : opendmarc Version : 1.3.1 Release : 13.el7 Architecture: x86_64 Install Date: Wed 21 Oct 2015 02:44:46 PM CEST Group : System Environment/Daemons Size : 356054 License : BSD and Sendmail Signature : RSA/SHA256, Thu 30 Apr 2015 03:55:25 PM CEST, Key ID 6a2faea2352c64e5 Source RPM : opendmarc-1.3.1-13.el7.src.rpm Build Date : Thu 30 Apr 2015 02:54:59 AM CEST Build Host : buildvm-17.phx2.fedoraproject.org Relocations : (not relocatable) Packager : Fedora Project Vendor : Fedora Project URL : http://www.trusteddomain.org/opendmarc.html Summary : A Domain-based Message Authentication, Reporting & Conformance (DMARC) milter and library Description : OpenDMARC (Domain-based Message Authentication, Reporting & Conformance) provides an open source library that implements the DMARC verification service plus a milter-based filter application that can plug in to any milter-aware MTA, including sendmail, Postfix, or any other MTA that supports the milter protocol. The DMARC sender authentication system is still a draft standard, working towards RFC status. /etc/opendmarc /etc/opendmarc.conf /etc/sysconfig/opendmarc /etc/tmpfiles.d/opendmarc.conf /usr/lib/systemd/system/opendmarc.service /usr/sbin/opendmarc /usr/sbin/opendmarc-check /usr/sbin/opendmarc-expire /usr/sbin/opendmarc-import /usr/sbin/opendmarc-importstats /usr/sbin/opendmarc-params /usr/sbin/opendmarc-reports /usr/share/doc/opendmarc-1.3.1 /usr/share/doc/opendmarc-1.3.1/README /usr/share/doc/opendmarc-1.3.1/README.schema /usr/share/doc/opendmarc-1.3.1/RELEASE_NOTES /usr/share/doc/opendmarc-1.3.1/draft-dmarc-base-13.txt /usr/share/doc/opendmarc-1.3.1/schema.mysql /usr/share/licenses/opendmarc-1.3.1 /usr/share/licenses/opendmarc-1.3.1/LICENSE /usr/share/licenses/opendmarc-1.3.1/LICENSE.Sendmail /usr/share/man/man5/opendmarc.conf.5.gz /usr/share/man/man8/opendmarc-check.8.gz /usr/share/man/man8/opendmarc-expire.8.gz /usr/share/man/man8/opendmarc-import.8.gz /usr/share/man/man8/opendmarc-importstats.8.gz /usr/share/man/man8/opendmarc-params.8.gz /usr/share/man/man8/opendmarc-reports.8.gz /usr/share/man/man8/opendmarc.8.gz /var/run/opendmarc /var/spool/opendmarc
iptables Regel
Damit der SPF auch über den Postfix - spf-milter
erreichbar ist und nicht das Empfangen der IP-Paket vom Paketfilter iptables
blockiert wird, muss nachfolgende Regel zum iptables
-Regelwerk hinzugefügt werden.
Um die aktuellen iptables
-Regeln erweitern zu können, sollten diese erst einmal aufgelistet werden, was mit nachfolgendem Befehl durchgeführt werden kann:
# iptables -L -nv --line-numbers Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 2 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 3 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 4 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 5 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
Nachfolgender Befehl, fügt folgende iptables
-Regel dem iptables
-Regelwerk nach der Position 4 hinzu, ohne das der Paketfilter angehalten werden muss:
-A INPUT -p tcp --dport 10013 -j ACCEPT
und hier der Befehl:
# iptables -I INPUT 5 -p tcp --dport 10013 -j ACCEPT
Ein erneute Abfrage des iptables
-Regelwerts, sollte dann nachfolgend dargestellte Ausgabe ergeben, was mit folgendem Befehl durchgeführt werden kann:
# iptables -L -nv --line-numbers Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 2 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 3 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 4 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 5 0 0 ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:10013 state NEW 6 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
Die neue Zeile ist an Position 5 (INPUT) zu sehen, hier nachfolgend zur Verdeutlichung noch einmal dargestellt (nur relevanter Ausschnitt):
... 5 0 0 ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:10013 state NEW ...
Um diese iptables
-Regel dauerhaft, auch nach einem Neustart des Server, weiterhin im iptables
-Regelwerk zu speichern, muss nachfolgend dargestellter Befehl abschließend noch ausgeführt werden:
# /usr/sbin/iptables-save > /etc/sysconfig/iptables
Konfiguration: DNS
Damit bei ausgehenden e-Mails auch unsere DMARC-Übrprüfung erfolgreich ist, wir nachfolgender Eintrag im DNS benötigt, damit der Abfragende bestimmen kann, wie er mit Nachrichten von uns selbst umgehen soll, wenn die DMACR-Überprüfung seinerseits fehl schlägt.
Unter nachfolgendem externen Link, kann ein TXT-Record, welcher DMARC-Informationen enthalten soll, komfortabel erstellt werden:
Nach der Erstellung über oben genannten Assistenten, kann der TXT-RECORD zur DMARC-Überprüfung wie folgt aussehen:
v=DMARC1; p=none; rua=mailto:dmarc-aggregate@tachtler.net; ruf=mailto:dmarc-incorrect@tachtler.net; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=none
Parameter | Wert | Erklärung |
---|---|---|
v | DMARC1 | Identifiziert den Datensatz als DMARC-Record. Der Parameter muss als erster wert im DMARC-Record gesetzt sein. Fehlt dieser ist der gesamte DMARC-Record zu verwerfen! |
p | none | Beschreibt, wie der Empfänger auf die Nachrichten des Domain-Inhabers reagieren soll, wenn die DMARC-Überprüfung fehl schlägt. Dies gilt auch für die Subdomains, sofern für diese mit dem Parameter sp keine separate Definition vorliegt. Mögliche Werte sind: none : Der Domaininhaber hat keine Vorgaben zur Verabeitung/Zustellung der e-Mails gemacht. quarantine : Der Domaininhaber wünscht, dass e-Mails, als verdächtig gewertet werden sollen. Abhängig vom Empfänger können diese als verdächtig markiert werden, mit weiteren/zusätzlichen SPAM Prüfungen zu belegen ist, oder in einen SPAM-Ordner verschoben werden soll. reject : Der Domaininhaber wünscht, dass e-Mails abgewiesen, also nicht angenommen werden sollen. Dies sollte, wenn möglich, noch während des SMTP-Dialogs passieren. |
rua | mailto:dmarc-aggregate@tachtler.net | Der Domaininhaber wünscht, per e-Mail mit aufbereiteten Statistikdaten der verarbeiteten e-Mails informiert zu werden. Ist dieser Parameter nicht gesetzt, braucht der Empfänger keine Statistikdaten aufzubereiten. |
ruf | mailto:dmarc-incorrect@tachtler.net | Der Domaininhaber wünscht, per e-Mail mit detaillierten forensischen Statistikdaten der verarbeiteten e-Mails informiert zu werden. Ist dieser Parameter nicht gesetzt, braucht der Empfänger keine Statistikdaten aufzubereiten. |
fo | 0 | Optionen zu den Fehlerreports, welche die gewünschte Detaillierung angeben. |
adkim | r | Definiert, wie konservativ das Ergebnis der DKIM-Signaturüberprüfung bewertet werden soll. Mögliche Werte sind: r=relaxed s=strikt |
aspf | r | Definiert, wie konservativ das Ergebnis der SPF-Üüberprüfung bewertet werden soll. Mögliche Werte sind: r=relaxed s=strikt |
pct | 100 | Der Domaininhaber wünscht, dass der angegebene Prozentwert an Nachrichten von den DMARC-Überprüfungen benutzt werden soll. Der Wert beschreibt nicht das Verhältnis in den DMARC-Reports! |
rf | afrf | Der Domaininhaber wünscht, dass die aufbereiteten forensischen Prüfberichte im Format AFRF oder IODEF zu erhalten, wenn sowohl die DKIM- wie auch der SPF-Überprüfung negativ ausfällt. |
ri | 86400 | Der Domaininhaber wünscht, dass die aufbereiteten Statistik- und Forensikdaten spätestens alle 86400 Sekunden verschickt werden sollen (86400 Sekunden = 1 Tag) |
sp | none | Beschreibt, wie der Empfänger die Nachrichten des Domain-Inhabers einer Subdomäne verwerten soll. Der Parameter beschreibt nur die abgefragte Subdomäne, nicht die Domäne an sich! Mögliche Werte sind: none : Der Domaininhaber hat keine Vorgaben zur Verabeitung/Zustellung der e-Mails gemacht. quarantine : Der Domaininhaber wünscht, dass e-Mails, als verdächtig gewertet werden sollen. Abhängig vom Empfänger können diese als verdächtig markiert werden, mit weiteren/zusätzlichen SPAM Prüfungen zu belegen ist, oder in einen SPAM-Ordner verschoben werden soll. reject : Der Domaininhaber wünscht, dass e-Mails abgewiesen, also nicht angenommen werden sollen. Dies sollte, wenn möglich, noch während des SMTP-Dialogs passieren. |
Dieser so erstelle Inhalt des TXT-RECORD zur DMARC-Überprüfung, muss nun im DNS veröffentlicht werden und zwar unter nachfolgendem Eintrag-Schema im DNS:
_dmarc.<DOMAIN>.<TLD>
Nachfolgend würde ein Eintrag für die Domäne tachtler.net
, wie folgt aussehen:
# dig _dmarc.tachtler.net TX ; <<>> DiG 9.9.4-RedHat-9.9.4-18.el7_1.5 <<>> _dmarc.tachtler.net TXT ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1217 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;_dmarc.tachtler.net. IN TXT ;; ANSWER SECTION: _dmarc.tachtler.net. 3591 IN TXT "v=DMARC1\; p=none\; rua=mailto:dmarc-aggregate@tachtler.net\; ruf=mailto:dmarc-incorrect@tachtler.net\; fo=0\; adkim=r\; aspf=r\; pct=100\; rf=afrf\; ri=86400\; sp=none" ;; Query time: 3 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu Oct 22 14:20:04 2015 ;; MSG SIZE rcvd: 208
Als absendende Domains für die DMARC-Reports an andere, sollte ein SUB-Domain verwendet werden, welche selbst keine
rua
ruf
Einträge besitzt.
v=DMARC1; p=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=none
Dieser so erstelle Inhalt des TXT-RECORD zur DMARC-Überprüfung, muss nun im DNS veröffentlicht werden und zwar unter nachfolgendem Eintrag-Schema im DNS:
_dmarc.<SUB>.<DOMAIN>.<TLD>
# dig _dmarc.dmarcreports.tachtler.net TXT ; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.3 <<>> @127.0.0.1 _dmarc.dmarcreports.tachtler.net TXT ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14708 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;_dmarc.dmarcreports.tachtler.net. IN TXT ;; ANSWER SECTION: _dmarc.dmarcreports.tachtler.net. 10800 IN TXT "v=DMARC1\; p=none\; fo=0\; adkim=r\; aspf=r\; pct=100\; rf=afrf\; ri=86400\; sp=none" ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Mon May 01 09:43:27 CEST 2017 ;; MSG SIZE rcvd: 189
* Mein Dank für die Hinweise geht an Juri Haberland
Konfiguration: OpenDMARC
/etc/sysconfig/opendmarc
Nachfolgende Konfigurationsdatei von OpenDMARC setzt Standardparameter für den Start des OpenDMARC Dienst/Daemons.
HINWEIS - Änderungen an dieser Konfigurationsdatei sind grundsätzlich nicht erforderlich!
# Set the necessary startup options OPTIONS="-c /etc/opendmarc.conf -P /var/run/opendmarc/opendmarc.pid"
/etc/opendmarc.conf
Standardmäßig wird nach der Installation von OpenDMARC - opendmarc-milter
in nachfolgendem Verzeichnis mit nachfolgendem Namen die Hauptkonfigurationsdatei für den OpenDMARC - opendmarc-milter
hinterlegt:
/etc/opendmarc.conf
Nachfolgende Änderungen sind an der Konfigurationsdatei /etc/opendmarc.conf
durchzuführen:
(Komplette Konfigurationsdatei)
# cat /etc/opendmarc.conf ## ## opendmarc.conf -- configuration file for OpenDMARC filter ## ## Copyright (c) 2012-2015, The Trusted Domain Project. All rights reserved. ## ## AuthservID (string) ## defaults to MTA name ## ## Sets the "authserv-id" to use when generating the Authentication-Results: ## header field after verifying a message. If the string "HOSTNAME" is ## provided, the name of the host running the filter (as returned by the ## gethostname(3) function) will be used. # # Tachtler # default: # AuthservID name AuthservID mx1.tachtler.net ## AuthservIDWithJobID { true | false } ## default "false" ## ## If "true", requests that the authserv-id portion of the added ## Authentication-Results header fields contain the job ID of the message ## being evaluated. # # Tachtler # default: # AuthservIDWithJobID false AuthservIDWithJobID true ## AutoRestart { true | false } ## default "false" ## ## Automatically re-start on failures. Use with caution; if the filter fails ## instantly after it starts, this can cause a tight fork(2) loop. # # AutoRestart false ## AutoRestartCount n ## default 0 ## ## Sets the maximum automatic restart count. After this number of automatic ## restarts, the filter will give up and terminate. A value of 0 implies no ## limit. # # AutoRestartCount 0 ## AutoRestartRate n/t[u] ## default (no limit) ## ## Sets the maximum automatic restart rate. If the filter begins restarting ## faster than the rate defined here, it will give up and terminate. This ## is a string of the form n/t[u] where n is an integer limiting the count ## of restarts in the given interval and t[u] defines the time interval ## through which the rate is calculated; t is an integer and u defines the ## units thus represented ("s" or "S" for seconds, the default; "m" or "M" ## for minutes; "h" or "H" for hours; "d" or "D" for days). For example, a ## value of "10/1h" limits the restarts to 10 in one hour. There is no ## default, meaning restart rate is not limited. # # AutoRestartRate n/t[u] ## Background { true | false } ## default "true" ## ## Causes opendmarc to fork and exits immediately, leaving the service ## running in the background. # # Background true ## BaseDirectory (string) ## default (none) ## ## If set, instructs the filter to change to the specified directory using ## chdir(2) before doing anything else. This means any files referenced ## elsewhere in the configuration file can be specified relative to this ## directory. It's also useful for arranging that any crash dumps will be ## saved to a specific location. # # BaseDirectory /var/run/opendmarc ## ChangeRootDirectory (string) ## default (none) ## ## Requests that the operating system change the effective root directory of ## the process to the one specified here prior to beginning execution. ## chroot(2) requires superuser access. A warning will be generated if ## UserID is not also set. # # ChangeRootDirectory /var/chroot/opendmarc ## CopyFailuresTo (string) ## default (none) ## ## Requests addition of the specified email address to the envelope of ## any message that fails the DMARC evaluation. # # Tachtler # default: # CopyFailuresTo postmaster@localhost CopyFailuresTo postmaster@tachtler.net ## DNSTimeout (integer) ## default 5 ## ## Sets the DNS timeout in seconds. A value of 0 causes an infinite wait. ## (NOT YET IMPLEMENTED) # # DNSTimeout 5 ## EnableCoredumps { true | false } ## default "false" ## ## On systems that have such support, make an explicit request to the kernel ## to dump cores when the filter crashes for some reason. Some modern UNIX ## systems suppress core dumps during crashes for security reasons if the ## user ID has changed during the lifetime of the process. Currently only ## supported on Linux. # # EnableCoreDumps false ## FailureReports { true | false } ## default "false" ## ## Enables generation of failure reports when the DMARC test fails and the ## purported sender of the message has requested such reports. Reports are ## formatted per RFC6591. # # Tachtler # default: # FailureReports false FailureReports true ## FailureReportsBcc (string) ## default (none) ## ## When failure reports are enabled and one is to be generated, always ## send one to the address(es) specified here. If a failure report is ## requested by the domain owner, the address(es) are added in a Bcc: field. ## If no request is made, they address(es) are used in a To: field. There ## is no default. # # Tachtler # default: # FailureReportsBcc postmaster@example.coom FailureReportsBcc postmaster@tachtler.net ## FailureReportsOnNone { true | false } ## default "false" ## ## Supplements the "FailureReports" setting by generating reports for ## domains that advertise "none" policies. By default, reports are only ## generated (when enabled) for sending domains advertising a "quarantine" ## or "reject" policy. # # FailureReportsOnNone false ## FailureReportsSentBy string ## default "USER@HOSTNAME" ## ## Specifies the email address to use in the From: field of failure ## reports generated by the filter. The default is to use the userid of ## the user running the filter and the local hostname to construct an ## email address. "postmaster" is used in place of the userid if a name ## could not be determined. # # Tachtler # default: # FailureReportsSentBy USER@HOSTNAME FailureReportsSentBy postmaster@dmarcreports.tachtler.net ## HistoryFile path ## default (none) ## ## If set, specifies the location of a text file to which records are written ## that can be used to generate DMARC aggregate reports. Records are groups ## of rows containing information about a single received message, and ## include all relevant information needed to generate a DMARC aggregate ## report. It is expected that this will not be used in its raw form, but ## rather periodically imported into a relational database from which the ## aggregate reports can be extracted by a tool such as opendmarc-import(8). # # Tachtler # default: # HistoryFile /var/spool/opendmarc/opendmarc.dat HistoryFile /var/spool/opendmarc/opendmarc.dat ## IgnoreAuthenticatedClients { true | false } ## default "false" ## ## If set, causes mail from authenticated clients (i.e., those that used ## SMTP AUTH) to be ignored by the filter. # # IgnoreAuthenticatedClients false ## IgnoreHosts path ## default (internal) ## ## Specifies the path to a file that contains a list of hostnames, IP ## addresses, and/or CIDR expressions identifying hosts whose SMTP ## connections are to be ignored by the filter. If not specified, defaults ## to "127.0.0.1" only. # # Tachtler # default: # IgnoreHosts /etc/opendmarc/ignore.hosts IgnoreHosts /etc/opendmarc/ignore.hosts ## IgnoreMailFrom domain[,...] ## default (none) ## ## Gives a list of domain names whose mail (based on the From: domain) is to ## be ignored by the filter. The list should be comma-separated. Matching ## against this list is case-insensitive. The default is an empty list, ## meaning no mail is ignored. # # IgnoreMailFrom example.com ## MilterDebug (integer) ## default 0 ## ## Sets the debug level to be requested from the milter library. # # Tachtler # default: # MilterDebug 0 MilterDebug 5 ## PidFile path ## default (none) ## ## Specifies the path to a file that should be created at process start ## containing the process ID. ## # # PidFile /var/run/opendmarc.pid ## PublicSuffixList path ## default (none) ## ## Specifies the path to a file that contains top-level domains (TLDs) that ## will be used to compute the Organizational Domain for a given domain name, ## as described in the DMARC specification. If not provided, the filter will ## not be able to determine the Organizational Domain and only the presented ## domain will be evaluated. # # PublicSuffixList path ## RecordAllMessages { true | false } ## default "false" ## ## If set and "HistoryFile" is in use, all received messages are recorded ## to the history file. If not set (the default), only messages for which ## the From: domain published a DMARC record will be recorded in the ## history file. # # RecordAllMessages false ## RejectFailures { true | false } ## default "false" ## ## If set, messages will be rejected if they fail the DMARC evaluation, or ## temp-failed if evaluation could not be completed. By default, no message ## will be rejected or temp-failed regardless of the outcome of the DMARC ## evaluation of the message. Instead, an Authentication-Results header ## field will be added. # # RejectFailures false ## ReportCommand string ## default "/usr/sbin/sendmail -t" ## ## Indicates the shell command to which failure reports should be passed for ## delivery when "FailureReports" is enabled. # # ReportCommand /usr/sbin/sendmail -t ## RequiredHeaders { true | false } ## default "false" ## ## If set, the filter will ensure the header of the message conforms to the ## basic header field count restrictions laid out in RFC5322, Section 3.6. ## Messages failing this test are rejected without further processing. A ## From: field from which no domain name could be extracted will also be ## rejected. # # RequiredHeaders false ## Socket socketspec ## default (none) ## ## Specifies the socket that should be established by the filter to receive ## connections from sendmail(8) in order to provide service. socketspec is ## in one of two forms: local:path, which creates a UNIX domain socket at ## the specified path, or inet:port[@host] or inet6:port[@host] which creates ## a TCP socket on the specified port for the appropriate protocol family. ## If the host is not given as either a hostname or an IP address, the ## socket will be listening on all interfaces. This option is mandatory ## either in the configuration file or on the command line. If an IP ## address is used, it must be enclosed in square brackets. # # Tachtler # default: Socket inet:8893@localhost Socket inet:10013@192.168.0.70 ## SoftwareHeader { true | false } ## default "false" ## ## Causes the filter to add a "DMARC-Filter" header field indicating the ## presence of this filter in the path of the message from injection to ## delivery. The product's name, version, and the job ID are included in ## the header field's contents. # SoftwareHeader true ## SPFIgnoreResults { true | false } ## default "false" ## ## Causes the filter to ignore any SPF results in the header of the ## message. This is useful if you want the filter to perfrom SPF checks ## itself, or because you don't trust the arriving header. # # Tachtler # default: SPFIgnoreResults true SPFIgnoreResults false ## SPFSelfValidate { true | false } ## default false ## ## Enable internal spf checking with --with-spf ## To use libspf2 instead: --with-spf --with-spf2-include=path --with-spf2-lib=path ## ## Causes the filter to perform a fallback SPF check itself when ## it can find no SPF results in the message header. If SPFIgnoreResults ## is also set, it never looks for SPF results in headers and ## always performs the SPF check itself when this is set. # # Tachtler # default: SPFSelfValidate true SPFSelfValidate false ## Syslog { true | false } ## default "false" ## ## Log via calls to syslog(3) any interesting activity. # Syslog true ## SyslogFacility facility-name ## default "mail" ## ## Log via calls to syslog(3) using the named facility. The facility names ## are the same as the ones allowed in syslog.conf(5). # # SyslogFacility mail ## TrustedAuthservIDs string ## default HOSTNAME ## ## Specifies one or more "authserv-id" values to trust as relaying true ## upstream DKIM and SPF results. The default is to use the name of ## the MTA processing the message. To specify a list, separate each entry ## with a comma. The key word "HOSTNAME" will be replaced by the name of ## the host running the filter as reported by the gethostname(3) function. # # TrustedAuthservIDs HOSTNAME ## UMask mask ## default (none) ## ## Requests a specific permissions mask to be used for file creation. This ## only really applies to creation of the socket when Socket specifies a ## UNIX domain socket, and to the HistoryFile and PidFile (if any); temporary ## files are normally created by the mkstemp(3) function that enforces a ## specific file mode on creation regardless of the process umask. See ## umask(2) for more information. # UMask 007 ## UserID user[:group] ## default (none) ## ## Attempts to become the specified userid before starting operations. ## The process will be assigned all of the groups and primary group ID of ## the named userid unless an alternate group is specified. # UserID opendmarc:mail
Nachfolgende Änderungen sollten vorgenommen werden:
AuthservID mx1.tachtler.net
Explizites setzen des HOST-Namen, welcher bei Einträgen durch OpenDMARC verwendet werden soll. Standardmäßig würde hier der FQDN des Servers verwendet werden, auf dem der OpenDMARC ausgeführt wird.
AuthservIDWithJobID true
Die „Authserv-ID“ soll in den e-Mail-Header bei der Eintragung des Authentikations-Ergebnisses durchgeführt werden. Es wird die ID der e-Mail verwendet.
CopyFailuresTo postmaster@tachtler.net
Sendet eine Kopie der e-Mail an die angegeben e-Mail-Adresse, wenn eine DMARC-Überprüfung fehlgeschlagenen ist.
FailureReports true
Es soll eine Fehlerreport erstellt werden, falls die DMARC-Fehlerüberprüfung fehl schlägt und der Absender der e-Mail solch einen Report angefordert hat. Die Reports werden im so formatiert, wie dies im RFC6591 festgelegt wurde.
FailureReportsBcc postmaster@tachtler.net
Falls die Erstellung von Fehlerreports durchgeführt wird und ein Fehlerreport erstellt wird, soll an nachfolgende e-Mailadresse eine Blindkopie dieses Fehlerreports gesendet werden, wenn der Absender einen solchen Report angefordert hat. Falls der Absender keinen Report angefordert hat, wird eine e-Mail an die angegebene Adresse als Empfänger gesendet.
FailureReportsSentBy postmaster@dmarcreports.tachtler.net
Absender e-Mail-Adresse des Reports der per e-Mail an den Absender geht, bei dem die DMARC-Überprüfung fehlgeschlagen ist und der Absender einen solchen Report angefordert hat.
Es sollte eine SUB-Domain verwendet werden, da sonst die DMARC-Reports und -Failure-Messages von der Haupt-Domain verschickt werden, generiert jeder Report bei den Empfängern einen Report zurück. Das ist an sich nicht schlimm, aber generiert unter Umständen sinnlos Reports die nur sich selber als Report in sich tragen. Deshalb sollten die DMARC-Reports und -Failure-Messages von einer Subdomain aus zu verschickt werden, die eine eigenen DMARC-Record ohne rua- und ruf-Tags hat.
HistoryFile /var/spool/opendmarc/opendmarc.dat
Falls hier die Angabe einer Datei ggf. mit Pfadangabe durchgeführt wird, wird eine Textdatei erstellt, deren Inhalt ein DMARC Summenreport ist. Diese Datei kann dann mit dem opendmarc-import
Programm extrahiert werden und in eine Datenbank geschrieben werden um daraus später weitere Reports erstellen zu können.
IgnoreHosts /etc/opendmarc/ignore.hosts
Bezeichnet den Pfad zu einer Datei, welche als Inhalt HOST-Namen, IP-Adressen oder auch Netzwerk-Angaben beinhaltet, welche von der DMARC-Überprüfung ausgeschlossen werden sollen. Falls keine Angaben hier erfolgt, wird Standardmäßig nur die IP-Adresse: 127.0.0.1 ausgeschlossen.
HINWEIS - Diese Datei sollte für später vorbereitet werden, um ggf. Ausschlüsse von der Prüfung durchführen zu können!
MilterDebug 5
Setzen des Levels der Informationsausgabe, welche in das LOG geschrieben werden.
Socket inet:10013@192.168.0.70
Setzen des Sockets bzw. der IP-Adresse und des Ports, über den der MILTER angesprochen werden kann.
SPFIgnoreResults false
Die DMARC-Überprüfung von bereits vorhandenen e-Mail-Header Einträgen zur SPF-Überprüfung sollen nicht ignoriert werden, da diese bereits im Vorfeld in diesem Beispiel durch den SPF-MILTER gesetzt wurden. Hier könnte auch eine SPF-Überprüfung durch den DMARC-MILTER durchgeführt werden, was jedoch aus Stabilitätsgründen hier nicht erfolgen soll.
SPFSelfValidate false
Deaktiviert die SPF-Überprüfung, welche auch durch den DMARC-MILTER durchgeführt werden kann, wenn nicht bereits im e-Mail-Header SPF-Überprüfungsinformationen enhalten sind, was jedoch aus Stabilitätsgründen hier nicht erfolgen soll.
/etc/opendmarc/ignore.hosts
Nachfolgende Konfigurationsdatei in nachfolgendem Verzeichnis mit ebenfalls nachfolgendem Namen:
/etc/opendmarc/ignore.hosts
beinhaltet HOST-Namen, IP-Adressen oder ganze Netze, für die keine DMARC-Überprüfung durchgeführt werden soll.
Bevor die Angaben zu den HOST-Namen, IP-Adressen oder ganze Netze, für die keine DMARC-Überprüfung durchgeführt werden soll gemacht werden können, muss die Konfigurationsdatei mit nachfolgendem Befehl angelegt werden:
# touch /etc/opendmarc/ignore.hosts
Nachfolgende Einstellungen sind an der Konfigurationsdatei /etc/opendkim/TrustedHosts
durchzuführen:
(Komplette Konfigurationsdatei)
## File that contains a list of hostnames, IP addresses, and/or CIDR expressions ## identifying hosts whose SMTP connections are to be ignored by the filter. 127.0.0.1
OpenDMARC Dienst/Daemon-Start einrichten
Um den OpenDMARC der als Dienst/Deamon als Hintergrundprozess läuft, auch nach einem Neustart des Servers zur Verfügung zu haben, soll der Dienst/Daemon mit dem Server mit gestartet werden, was mit nachfolgendem Befehl realisiert werden kann:
# systemctl enable opendmarc ln -s '/usr/lib/systemd/system/opendmarc.service' '/etc/systemd/system/multi-user.target.wants/opendmarc.service'
Eine Überprüfung, ob beim Neustart des Server der opendmarc
-Dienst/Deamon wirklich mit gestartet wird, kann mit nachfolgendem Befehl erfolgen und sollte eine Anzeige, wie ebenfalls nachfolgend dargestellt ausgeben:
# systemctl list-unit-files --type=service | grep -e opendmarc opendmarc.service enabled
bzw.
# systemctl is-enabled opendmarc enabled
Erster Start OpenDMARC
Um den OpenDMARC zu starten kann nachfolgender Befehl angewandt werden:
# systemctl start opendmarc
Eine Überprüfung ob der Start des OpenDMARC erfolgreich war kann mit nachfolgendem Befehl durchgeführt werden, welcher eine Ausgabe in etwa wie nachfolgende erzeugen sollte:
# systemctl status opendmarc opendmarc.service - Domain-based Message Authentication, Reporting & Conformance (DMARC) Milter Loaded: loaded (/usr/lib/systemd/system/opendmarc.service; enabled) Active: active (running) since Thu 2015-10-22 09:23:26 CEST; 4s ago Docs: man:opendmarc(8) man:opendmarc.conf(5) man:opendmarc-import(8) man:opendmarc-reports(8) http://www.trusteddomain.org/opendmarc/ Process: 2481 ExecStart=/usr/sbin/opendmarc $OPTIONS (code=exited, status=0/SUCCESS) Main PID: 2482 (opendmarc) CGroup: /system.slice/opendmarc.service └─2482 /usr/sbin/opendmarc -c /etc/opendmarc.conf -P /var/run/open... Oct 22 09:23:26 server70.idmz.tachtler.net systemd[1]: Starting Domain-based ... Oct 22 09:23:26 server70.idmz.tachtler.net opendmarc[2482]: OpenDMARC Filter ... Oct 22 09:23:26 server70.idmz.tachtler.net opendmarc[2482]: additional truste... Oct 22 09:23:26 server70.idmz.tachtler.net systemd[1]: Started Domain-based M... Hint: Some lines were ellipsized, use -l to show in full.
bzw. mit nachfolgendem Befehl, ob der Dienst/Daemon in der Prozessliste erscheint:
# ps aux | grep opendmarc opendma+ 2482 0.0 0.0 39884 712 ? Ssl 09:23 0:00 /usr/sbin/opendmarc -c /etc/opendmarc.conf -P /var/run/opendmarc/opendmarc.pid root 2487 0.0 0.0 112640 928 pts/0 S+ 09:24 0:00 grep --color=auto opendmarc
Eine weitere Möglichkeit ist die Überprüfung des journal
, was mit nachfolgendem Befehl durchgeführt werden kann:
Oct 22 09:23:26 server70.idmz.tachtler.net systemd[1]: Starting Domain-based Mes -- Subject: Unit opendmarc.service has begun with start-up -- Defined-By: systemd -- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel -- -- Unit opendmarc.service has begun starting up. Oct 22 09:23:26 server70.idmz.tachtler.net opendmarc[2481]: OpenDMARC Filter: Op Oct 22 09:23:26 server70.idmz.tachtler.net opendmarc[2482]: OpenDMARC Filter v1. Oct 22 09:23:26 server70.idmz.tachtler.net opendmarc[2482]: additional trusted a Oct 22 09:23:26 server70.idmz.tachtler.net systemd[1]: Started Domain-based Mess -- Subject: Unit opendmarc.service has finished start-up -- Defined-By: systemd -- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel -- -- Unit opendmarc.service has finished starting up. -- -- The start-up result is done.
Konfiguration: opendmarc-milter
Nachfolgende Änderungen werden an den Konfigurationsdateien
/etc/postfix/main.cf
/etc/postfix/master.cf
durchgeführt, um eine Anbindung des Postfix an den OpenDMARC zu realisieren.
Dabei soll die Anbindung von Postfix an den OpenDMARC mit dem Verfahren
opendmarc-milter
erfolgen.
/etc/postfix/main.cf
Hier die Änderungen an der Konfigurationsdatei /etc/postfix/main.cf
(Nur relevanter Ausschnitt):
... # OpenDMARC (opendmarc-milter) opendmarc_milter = inet:192.168.0.70:10013 ...
/etc/postfix/master.cf
Hier die Änderungen an der Konfigurationsdatei /etc/postfix/master.cf
(Nur relevanter Ausschnitt):
# # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master" or # on-line: http://www.postfix.org/master.5.html). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== # Tachtler - disabled - #smtp inet n - n - - smtpd # Tachtler - new - # Incoming traffic from untrust networks, with postscreen. 192.168.1.60:2525 inet n - n - 1 postscreen # Tachtler - enabled - # Incoming traffic passed from untrust networks, with postscreen. smtpd pass - - n - - smtpd -o smtpd_milters=${opendmarc_milter} ...
Nachfolgend Erklärungen zu den WICHTIGSTEN Konfigurationen:
-o smtpd_milters=${opendmarc_milter}
Die Option sorgt dafür, dass dem Parameter smtpd_milter
der Inhalt des Parameters opendmarc_milter
übergeben wird. Falls mehrere MILTER zum Einsatz kommen, wird hier die Reihenfolge festgelegt, in der diese aufgerufen werden!
Neustart
Falls vorstehende Änderungen (natürlich an die jeweiligen Bedürfnisse angepasst) durchgeführt wurden, muss ein Neustart von Postfix durchgeführt werden.
Danach kann der postfix-Server mit nachfolgendem Befehle neu gestartet werden:
# systemctl restart postfix
Mit nachfolgendem Befehl kann der Status des abgefragt werden:
# systemctl status postfix postfix.service - Postfix Mail Transport Agent Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled) Active: active (running) since Thu 2015-10-15 11:11:26 CEST; 7s ago Process: 1128 ExecStop=/usr/sbin/postfix stop (code=exited, status=0/SUCCESS) Process: 1144 ExecStart=/usr/sbin/postfix start (code=exited, status=0/SUCCESS) Process: 1141 ExecStartPre=/usr/libexec/postfix/chroot-update (code=exited, status=0/SUCCESS) Process: 1138 ExecStartPre=/usr/libexec/postfix/aliasesdb (code=exited, status=0/SUCCESS) Main PID: 1216 (master) CGroup: /system.slice/postfix.service ├─1216 /usr/libexec/postfix/master -w ├─1217 pickup -l -t unix -u -o content_filter=lmtp:[192.168.0.70]... └─1218 qmgr -l -t unix -u Oct 15 11:11:26 server60.idmz.tachtler.net systemd[1]: Starting Postfix Mail... Oct 15 11:11:26 server60.idmz.tachtler.net postfix/postfix-script[1214]: sta... Oct 15 11:11:26 server60.idmz.tachtler.net postfix/master[1216]: daemon star... Oct 15 11:11:26 server60.idmz.tachtler.net systemd[1]: Started Postfix Mail ... Hint: Some lines were ellipsized, use -l to show in full.
Test
Nachfolgend soll ein Test darin bestehen, dass eine e-Mail von einem externen Server an Postfix gesendet wird, und dieser dann die DKIM-Signatur des absendenden e-Mail-Servers prüft.
Wichtig sind zwei Einträge, in
- den Header-Zeilen der eingehenden e-Mail
- die LOG-Einträge im Server, auf dem OpenDMARC -
opendmarc-milter
läuft:
Überprüfung: Header-Zeilen
Nachfolgender Eintrag sollte in den Header-Zeilen einer eingehenden e-Mail zu finden sein, um das Ergebnis der OpenDMARC - opendmarc-milter
Überprüfung zu zeigen:
DMARC-Filter: OpenDMARC Filter v1.3.1 mx1.tachtler.net EDCFE1800089 Authentication-Results: mx1.tachtler.net/EDCFE1800089; dmarc=pass header.from=nausch.org
* Das Ergbenis kann hier pass
, fail
und none
sein.
Überprüfung: /var/log/maillog
Nachfolgender Eintrag sollte in den LOG-Einträgen des Servers auf dem der OpenDMARC - opendmarc-milter
läuft bei einer eingehenden e-Mail zu finden sein, um das Ergebnis der OpenDMARC - opendmarc-milter
Überprüfung zu dokumentieren:
Oct 22 09:39:23 server70 opendmarc[2482]: implicit authentication service: mx1.tachtler.net Oct 22 09:39:23 server70 opendmarc[2482]: EDCFE1800089: nausch.org pass
Konfiguration: Reports ausgehend
Um Reports erstellen zu können, welche durch einliefernde e-Mail-Server im Rahmen von OpenDMARC angefordert werden, ist es vorgesehen, dass die Daten zuerst von OpenDMARC in eine Datei geschrieben werden, welche sich in nachfolgendem Verzeichnis mit nachfolgendem Namen befindet.
/var/spool/opendmarc/opendmarc.dat
Die in der oben genannten Datei befindlichen Daten müssen allerdings zuerst in eine Datenbank geschrieben werden, um daraus dann die gewünschten Reports erstellen zu können und anschließend auch versenden zu können. Nicht zu vergessen ist hierbei auch, das aus der Datenbank auch wieder entsprechend nach einer gewissen Zeit, ältere Daten gelöscht werden, damit diese nicht überlaufen kann.
Nachfolgende Konfiguration ist erforderlich, damit Daten durch das Skript in nachfolgendem Verzeichnis mit nachfolgendem Namen
/usr/sbin/opendmarc-import
in die Datenbank geschrieben werden können. Dazu ist die entsprechende Datenbank und die benötigten Tabellen und Felder anzulegen.
Dies kann durch ein, ebenfalls im rpm
-Paket mitgeliefertes Skript, welches in nachfolgendem Verzeichnis mit nachfolgendem Namen zu finden ist:
/usr/share/doc/opendmarc-1.3.1/schema.mysql
durchgeführt werden.
HINWEIS - Falls von einem HOST anstelle von localhost
Daten in die Datenbank geschrieben werden sollen, muss das Skript entsprechend erweitert bzw. angepasst werden!
/usr/share/doc/opendmarc-1.3.1/schema.mysql
HINWEIS - Nachfolgend sollen vom HOST mit der IP-Adresse 192.168.0.70
die Daten in die Datenbank geschrieben werden und nicht von localhost
wie dies standardmäßig der Fall ist!
Die erforderlichen Anpassungen, befinden sich am Ende des Skripts, welches sonst nicht angepasst werden sollte!
-- OpenDMARC database schema -- -- Copyright (c) 2012, The Trusted Domain Project. -- All rights reserved. CREATE DATABASE IF NOT EXISTS opendmarc; USE opendmarc; -- A table for mapping domain names and their DMARC policies to IDs CREATE TABLE IF NOT EXISTS domains ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL, firstseen TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), UNIQUE KEY(name) ); -- A table for logging reporting requests CREATE TABLE IF NOT EXISTS requests ( id INT NOT NULL AUTO_INCREMENT, domain INT NOT NULL, repuri VARCHAR(255) NOT NULL, adkim TINYINT NOT NULL, aspf TINYINT NOT NULL, policy TINYINT NOT NULL, spolicy TINYINT NOT NULL, pct TINYINT NOT NULL, locked TINYINT NOT NULL, firstseen TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, lastsent TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY(id), KEY(lastsent), UNIQUE KEY(domain) ); -- A table for reporting hosts CREATE TABLE IF NOT EXISTS reporters ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL, firstseen TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), UNIQUE KEY(name) ); -- A table for IP addresses CREATE TABLE IF NOT EXISTS ipaddr ( id INT NOT NULL AUTO_INCREMENT, addr VARCHAR(64) NOT NULL, firstseen TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), UNIQUE KEY(addr) ); -- A table for messages CREATE TABLE IF NOT EXISTS messages ( id INT NOT NULL AUTO_INCREMENT, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, jobid VARCHAR(128) NOT NULL, reporter INT UNSIGNED NOT NULL, policy TINYINT UNSIGNED NOT NULL, disp TINYINT UNSIGNED NOT NULL, ip INT UNSIGNED NOT NULL, env_domain INT UNSIGNED NOT NULL, from_domain INT UNSIGNED NOT NULL, policy_domain INT UNSIGNED NOT NULL, spf TINYINT UNSIGNED NOT NULL, align_dkim TINYINT UNSIGNED NOT NULL, align_spf TINYINT UNSIGNED NOT NULL, sigcount TINYINT UNSIGNED NOT NULL, PRIMARY KEY(id), KEY(date), UNIQUE KEY(reporter, date, jobid) ); -- A table for signatures CREATE TABLE IF NOT EXISTS signatures ( id INT NOT NULL AUTO_INCREMENT, message INT NOT NULL, domain INT NOT NULL, pass TINYINT NOT NULL, error TINYINT NOT NULL, PRIMARY KEY(id), KEY(message) ); -- CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'changeme'; -- GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost'; -- # Tachtler - Create new users. CREATE USER 'opendmarc_user'@'192.168.0.70' IDENTIFIED BY 'password'; CREATE USER 'opendmarc_user'@'server70.idmz.tachtler.net' IDENTIFIED BY 'password'; -- # Tachtler - Grant ALL privileges to new users. GRANT ALL PRIVILEGES ON *.* TO 'opendmarc_user'@'192.168.0.70' IDENTIFIED BY 'password' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON *.* TO 'opendmarc_user'@'server70.idmz.tachtler.net' IDENTIFIED BY 'password' WITH GRANT OPTION; -- # Tachtler - Make sure that priviliges are reloaded. FLUSH PRIVILEGES;
Nachfolgende Änderungen wurden am Skript durchgeführt:
Erstellen von zwei neuen Benutzer, opendmarc_user'@'192.168.0.70
und opendmarc_user'@'server70.idmz.tachtler.net
, welche via IP-Adresse oder FQDN vom Server 192.168.0.70
bzw. server70.idmz.tachtler.net
agieren dürfen und ein Passwort besitzen
Den beiden Benutzern opendmarc_user'@'192.168.0.70
und opendmarc_user'@'server70.idmz.tachtler.net
werden alle Privilegien in auf die Datenbank opendmarc
gewährt.
-- # Tachtler - Make sure that priviliges are reloaded. FLUSH PRIVILEGES;
Die Benutzerrechte, inklusive der neu angelegten Benutzer mit den entsprechenden Rechten, sollen aktiviert werden, in dem die Rechte neu eingelesen werden sollen.
Die Ausführung des Skriptes und die damit verbunden Anlage der Datenbank, der Tabellen und Felder und der Nutzer, kann durch Ausführung des nachfolgenden Befehls durchgeführt werden, zudem jedoch das Kennwort zum Datenbankbenutzer root
erforderlich ist!:
# /usr/bin/mysql -u root -p < /usr/share/doc/opendmarc-1.3.1/schema.mysql Enter password:
/etc/opendmarc/opendmarc_report_out.sh
Nachfolgend soll ein Skript erstellt werden, welches alle drei nachfolgende Aufgaben durchführen soll:
- Übernahme der Daten aus der Konfigurationsdatei
/var/run/opendmarc/opendmarc.dat
in die Datenbank - Erstellen und versenden der Reports, zu den externen e-Mail-Servern, auf dessen Anforderung hin
- Aufräumen der Datenbank und löschen der älteren Datensätze um einem Überlauf der Datenbank vorzubeugen
#!/bin/bash ############################################################################## # Script-Name : opendmarc_report_out.sh # # Description : Add opendmarc report data from file # # - /var/spool/opendmarc/opendmarc.dat # # to a Database. Generate and sent reports to recipients who # # request a report. Delete old data from Database to protect # # the Database from overflow. # # # # # Last update : 23.10.2015 # # Version : 1.00 # ############################################################################## ############################################################################## # H I S T O R Y # ############################################################################## # Version : x.xx # # Description : <Description> # # -------------------------------------------------------------------------- # # Version : x.xx # # Description : <Description> # # -------------------------------------------------------------------------- # ############################################################################## # Source function library. . /etc/init.d/functions # Variable declarations. ############################################################################## # >>> Please edit following lines for personal command and/or configuration! # ############################################################################## # CUSTOM - Script-Name. SCRIPT_NAME='opendmarc_report_out.sh' # CUSTOM - Variables. FILE_OPENDMARC_DAT='/var/spool/opendmarc/opendmarc.dat' FILE_OPENDMARC_USER='opendmarc' FILE_OPENDMARC_PERMISSION='660' REPORT_INTERVAL='86400' REPORT_SENDER='dmarc-sender@dmarcreports.tachtler.net' REPORT_ORG='tachtler.net' REPORT_EXPIRE='90' # CUSTOM - Database. DB_HOST='mariadb.idmz.tachtler.net' DB_PORT='3306' DB_NAME='opendmarc' DB_USER='opendmarc_user' DB_PSWD='geheim' # CUSTOM - Mail-Recipient. MAIL_RECIPIENT='you@example.com' # CUSTOM - Status-Mail [Y|N]. MAIL_STATUS='N' ############################################################################## # >>> Normaly there is no need to change anything below this comment line. ! # ############################################################################## # Binarys. BIN_OPENDMARC_IMPORT=`command -v opendmarc-import` BIN_OPENDMARC_REPORTS=`command -v opendmarc-reports` BIN_OPENDMARC_EXPIRE=`command -v opendmarc-expire` # Variables. TOUCH_COMMAND=`command -v touch` RM_COMMAND=`command -v rm` CAT_COMMAND=`command -v cat` DATE_COMMAND=`command -v date` PROG_SENDMAIL=`command -v sendmail` CHOWN_COMMAND=`command -v chown` CHMOD_COMMAND=`command -v chmod` FILE_LOCK='/tmp/'$SCRIPT_NAME'.lock' FILE_LOG='/var/log/'$SCRIPT_NAME'.log' FILE_LAST_LOG='/tmp/'$SCRIPT_NAME'.log' FILE_MAIL='/tmp/'$SCRIPT_NAME'.mail' VAR_HOSTNAME=`uname -n` VAR_SENDER='root@'$VAR_HOSTNAME VAR_EMAILDATE=`$DATE_COMMAND '+%a, %d %b %Y %H:%M:%S (%Z)'` # Functions. function log() { echo $1 echo `$DATE_COMMAND '+%Y/%m/%d %H:%M:%S'` " INFO:" $1 >>${FILE_LAST_LOG} } function retval() { if [ "$?" != "0" ]; then case "$?" in *) log "ERROR: Unknown error $?" ;; esac fi } function movelog() { $CAT_COMMAND $FILE_LAST_LOG >> $FILE_LOG $RM_COMMAND -f $FILE_LAST_LOG $RM_COMMAND -f $FILE_LOCK } function sendmail() { case "$1" in 'STATUS') MAIL_SUBJECT='Status execution '$SCRIPT_NAME' script.' ;; *) MAIL_SUBJECT='ERROR while execution '$SCRIPT_NAME' script !!!' ;; esac $CAT_COMMAND <<MAIL >$FILE_MAIL Subject: $MAIL_SUBJECT Date: $VAR_EMAILDATE From: $VAR_SENDER To: $MAIL_RECIPIENT MAIL $CAT_COMMAND $FILE_LAST_LOG >> $FILE_MAIL $PROG_SENDMAIL -f $VAR_SENDER -t $MAIL_RECIPIENT < $FILE_MAIL $RM_COMMAND -f $FILE_MAIL } # Main. log "" log "+-----------------------------------------------------------------+" log "| Start processing opendmarc database jobs and e-Mail-report gen. |" log "+-----------------------------------------------------------------+" log "" log "Run script with following parameter:" log "" log "SCRIPT_NAME...........: $SCRIPT_NAME" log "" log "MAIL_RECIPIENT........: $MAIL_RECIPIENT" log "MAIL_STATUS...........: $MAIL_STATUS" log "" log "FILE_OPENDMARC_DAT....: $FILE_OPENDMARC_DAT" log "" log "" # Check if command (file) NOT exist OR IS empty. if [ ! -s "$TOUCH_COMMAND" ]; then log "Check if command '$TOUCH_COMMAND' was found....................[FAILED]" sendmail ERROR movelog exit 10 else log "Check if command '$TOUCH_COMMAND' was found....................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$RM_COMMAND" ]; then log "Check if command '$RM_COMMAND' was found.......................[FAILED]" sendmail ERROR movelog exit 11 else log "Check if command '$RM_COMMAND' was found.......................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$CAT_COMMAND" ]; then log "Check if command '$CAT_COMMAND' was found......................[FAILED]" sendmail ERROR movelog exit 12 else log "Check if command '$CAT_COMMAND' was found......................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$DATE_COMMAND" ]; then log "Check if command '$DATE_COMMAND' was found.....................[FAILED]" sendmail ERROR movelog exit 13 else log "Check if command '$DATE_COMMAND' was found.....................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$PROG_SENDMAIL" ]; then log "Check if command '$PROG_SENDMAIL' was found................[FAILED]" sendmail ERROR movelog exit 14 else log "Check if command '$PROG_SENDMAIL' was found................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$CHOWN_COMMAND" ]; then log "Check if command '$CHOWN_COMMAND' was found....................[FAILED]" sendmail ERROR movelog exit 15 else log "Check if command '$CHOWN_COMMAND' was found....................[ OK ]" fi # Check if command (file) NOT exist OR IS empty. if [ ! -s "$CHMOD_COMMAND" ]; then log "Check if command '$CHMOD_COMMAND' was found....................[FAILED]" sendmail ERROR movelog exit 16 else log "Check if command '$CHMOD_COMMAND' was found....................[ OK ]" fi # Check if LOCK file NOT exist. if [ ! -e "$FILE_LOCK" ]; then log "Check if script is NOT already runnig .....................[ OK ]" $TOUCH_COMMAND $FILE_LOCK else log "Check if script is NOT already runnig .....................[FAILED]" log "" log "ERROR: The script was already running, or LOCK file already exists!" log "" sendmail ERROR movelog exit 20 fi # Check if NOT exist. if [ ! -e "$FILE_OPENDMARC_DAT" ]; then log "Check if $FILE_OPENDMARC_DAT exists ........[FAILED]" log "" log "INFO : The file $FILE_OPENDMARC_DAT does NOT exists!!!" log "INFO : Nothing to do." log "INFO : Exit script." log "" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 50 else log "Check if $FILE_OPENDMARC_DAT exists ........[ OK ]" fi # Start process. log "" log "+-----------------------------------------------------------------+" log "| Run process from $SCRIPT_NAME ...................... |" log "+-----------------------------------------------------------------+" log "" # Import DMARC data into the database from file. $BIN_OPENDMARC_IMPORT --dbhost=$DB_HOST --dbport=$DB_PORT --dbname=$DB_NAME --dbuser=$DB_USER --dbpasswd=$DB_PSWD --verbose < $FILE_OPENDMARC_DAT if [ "$?" != 0 ]; then retval $? log "Import DMARC data into the database from file .............[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 51 else log "Import DMARC data into the database from file .............[ OK ]" fi log "" # Generate reports and sent them. $BIN_OPENDMARC_REPORTS --dbhost=$DB_HOST --dbport=$DB_PORT --dbname=$DB_NAME --dbuser=$DB_USER --dbpasswd=$DB_PSWD --verbose --interval=$REPORT_INTERVAL --report-email=$REPORT_SENDER --report-org=$REPORT_ORG if [ "$?" != 0 ]; then retval $? log "Generate reports and sent them ............................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 52 else log "Generate reports and sent them ............................[ OK ]" fi log "" # Clean the database. $BIN_OPENDMARC_EXPIRE --alltables --dbhost=$DB_HOST --dbport=$DB_PORT --dbname=$DB_NAME --dbuser=$DB_USER --dbpasswd=$DB_PSWD --verbose --expire=$REPORT_EXPIRE if [ "$?" != 0 ]; then retval $? log "Clean the database ........................................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 53 else log "Clean the database ........................................[ OK ]" fi log "" # Remove OpenDMARC file. $RM_COMMAND $FILE_OPENDMARC_DAT if [ "$?" != 0 ]; then retval $? log "Remove OpenDMARC HistoryFile ..............................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 54 else log "Remove OpenDMARC HistoryFile ..............................[ OK ]" fi # Create OpenDMARC file. $TOUCH_COMMAND $FILE_OPENDMARC_DAT if [ "$?" != 0 ]; then retval $? log "Create OpenDMARC HistoryFile ..............................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 55 else log "Create OpenDMARC HistoryFile ..............................[ OK ]" fi # Set owner to OpenDMARC file. $CHOWN_COMMAND $FILE_OPENDMARC_USER:$FILE_OPENDMARC_USER $FILE_OPENDMARC_DAT if [ "$?" != 0 ]; then retval $? log "Set owner to OpenDMARC HistoryFile ........................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 56 else log "Set owner to OpenDMARC HistoryFile ........................[ OK ]" fi # Set rights to OpenDMARC file. $CHMOD_COMMAND $FILE_OPENDMARC_PERMISSION $FILE_OPENDMARC_DAT if [ "$?" != 0 ]; then retval $? log "Set rights to OpenDMARC HistoryFile .......................[FAILED]" $RM_COMMAND -f $FILE_LOCK sendmail ERROR movelog exit 57 else log "Set rights to OpenDMARC HistoryFile .......................[ OK ]" fi # Finish process. log "" log "+-----------------------------------------------------------------+" log "| End process from $SCRIPT_NAME ...................... |" log "+-----------------------------------------------------------------+" log "" # Status e-mail. if [ $MAIL_STATUS = 'Y' ]; then sendmail STATUS fi # Move temporary log to permanent log movelog exit 0
Nachfolgender Befehl setzte die Datei- und Besitzrechte für das Skript wie folgt:
# chmod 750 /etc/opendmarc/opendmarc_report_out.sh
und
# chown root:root /etc/opendmarc/opendmarc_report_out.sh
Nach der erfolgreichen Ausführung des Skripts, wie nachfolgender manueller Aufruf zeigt, sind die Daten in der Datenbank und eine e-Mail an den einliefernden e-Mail-Server, welcher einen Report angefordert hat wurde ebenfalls generiert. Zum Abschluss ist dann noch eine Datenbankbereinigung angestoßen worden:
+-----------------------------------------------------------------+ | Start processing opendmarc database jobs and e-Mail-report gen. | +-----------------------------------------------------------------+ Run script with following parameter: SCRIPT_NAME...........: opendmarc_report_out.sh MAIL_RECIPIENT........: root@tachtler.net MAIL_STATUS...........: N FILE_OPENDMARC_DAT....: /var/spool/opendmarc/opendmarc.dat Check if command '/bin/touch' was found....................[ OK ] Check if command '/bin/rm' was found.......................[ OK ] Check if command '/bin/cat' was found......................[ OK ] Check if command '/bin/date' was found.....................[ OK ] Check if command '/sbin/sendmail' was found................[ OK ] Check if script is NOT already runnig .....................[ OK ] Check if /var/spool/opendmarc/opendmarc.dat exists ........[ OK ] +-----------------------------------------------------------------+ | Run process from opendmarc_report_out.sh ...................... | +-----------------------------------------------------------------+ Import DMARC data into the database from file .............[ OK ] opendmarc-reports: started at Fri Oct 23 11:10:33 2015 opendmarc-reports: selected 1 domain(s) opendmarc-reports: sent report for nausch.org to dmarc-reports@nausch.org (2.0.0 Ok: queued as 27894805BB4) opendmarc-reports: terminating at Fri Oct 23 11:10:34 2015 Generate reports and sent them ............................[ OK ] opendmarc-expire: started at Fri Oct 23 11:10:34 2015 opendmarc-expire: connected to database opendmarc-expire: expiring messages older than 90 day(s) opendmarc-expire: no rows deleted opendmarc-expire: expiring signatures on expired messages (id < 1) opendmarc-expire: no rows deleted opendmarc-expire: expiring request data older than 90 days opendmarc-expire: no rows deleted opendmarc-expire: terminating at Fri Oct 23 11:10:34 2015 Clean the database ........................................[ OK ] Remove OpenDMARC HistoryFile ..............................[ OK ] Create OpenDMARC HistoryFile ..............................[ OK ] Set owner to OpenDMARC HistoryFile ........................[ OK ] Set rights to OpenDMARC HistoryFile .......................[ OK ] +-----------------------------------------------------------------+ | End process from opendmarc_report_out.sh ...................... | +-----------------------------------------------------------------+
/etc/crontab
Abschließend soll das Skript nun ein mal pro Tag ausgeführt werden. Die soll nicht durch den anacron
-job-Lauf erfolgen, sondern durch einen klassiscen cron
-job.
Dazu soll nachfolgender Eintrag in der Konfigurationsdatei:
/etc/crontab
eingefügt werden:
(Komplette Konfigurationsdatei:)
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed # Tachtler # OpenDMARC data import, report generation and e-Mail sent process and database cleaning. 0 4 * * * root /root/bin/opendmarc_report_out.sh 1>/dev/null 2>&1
* Das Skript soll jeden Tag um 4:00 Uhr ausgeführt werden!
Konfiguration: Reports eingehend
Um eingehende OpenDMARC-Reports, welche als e-Mail mit einem ZIP-Anhang versandt werden, ebenfalls verarbeiten zu können und in einer Datenbank speichern zu können, sind nachfolgende Schritte notwendig.
Die original Anleitung zu diesem Thema ist unter nachfolgendem externen Link zu finden:
Dank der Vorarbeiten und den Skripten von John Levine, ist die Einbindung einfacher möglich geworden, da hier sonst keine Unterstützung von Seiten OpenDMARC geboten wird.
Installation: Skriptabhängigkeiten
Nachfolgende rpm
-Pakete müssen als Abhängigkeiten zum Skript readdmarc.pl
zusätzlich installiert werden:
Die Installation der Skriptabhängigkeiten, kann durch ausführen des nachfolgenden Befehls durchgeführt werden:
# yum install perl-DBI perl-Getopt-Simple perl-IO-Socket-INET6 perl-MIME-tools perl-NetAddr-IP perl-PerlIO-gzip perl-XML-Parser perl-XML-SAX perl-XML-Simple Loaded plugins: changelog, priorities 149 packages excluded due to repository priority protections Package perl-DBI-1.627-4.el7.x86_64 already installed and latest version Package perl-IO-Socket-INET6-2.69-5.el7.noarch already installed and latest version Package perl-MIME-tools-5.505-1.el7.noarch already installed and latest version Package perl-NetAddr-IP-4.069-3.el7.x86_64 already installed and latest version Resolving Dependencies --> Running transaction check ---> Package perl-PerlIO-gzip.x86_64 0:0.19-1.el7 will be installed ---> Package perl-XML-Parser.x86_64 0:2.41-10.el7 will be installed ---> Package perl-XML-SAX.noarch 0:0.99-9.el7 will be installed ---> Package perl-XML-Simple.noarch 0:2.20-5.el7 will be installed --> Finished Dependency Resolution Changes in packages about to be updated: Dependencies Resolved =============================================================================== Package Arch Version Repository Size =============================================================================== Installing: perl-PerlIO-gzip x86_64 0.19-1.el7 epel 23 k perl-XML-Parser x86_64 2.41-10.el7 base 223 k perl-XML-SAX noarch 0.99-9.el7 base 63 k perl-XML-Simple noarch 2.20-5.el7 base 74 k Transaction Summary =============================================================================== Install 4 Packages Total download size: 384 k Installed size: 943 k Is this ok [y/d/N]: y Downloading packages: (1/4): perl-PerlIO-gzip-0.19-1.el7.x86_64.rpm | 23 kB 00:00 (2/4): perl-XML-Parser-2.41-10.el7.x86_64.rpm | 223 kB 00:00 (3/4): perl-XML-Simple-2.20-5.el7.noarch.rpm | 74 kB 00:00 (4/4): perl-XML-SAX-0.99-9.el7.noarch.rpm | 63 kB 00:00 ------------------------------------------------------------------------------- Total 1.1 MB/s | 384 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : perl-XML-Parser-2.41-10.el7.x86_64 1/4 Installing : perl-XML-SAX-0.99-9.el7.noarch 2/4 Installing : perl-XML-Simple-2.20-5.el7.noarch 3/4 Installing : perl-PerlIO-gzip-0.19-1.el7.x86_64 4/4 Verifying : perl-XML-Simple-2.20-5.el7.noarch 1/4 Verifying : perl-PerlIO-gzip-0.19-1.el7.x86_64 2/4 Verifying : perl-XML-SAX-0.99-9.el7.noarch 3/4 Verifying : perl-XML-Parser-2.41-10.el7.x86_64 4/4 Installed: perl-PerlIO-gzip.x86_64 0:0.19-1.el7 perl-XML-Parser.x86_64 0:2.41-10.el7 perl-XML-SAX.noarch 0:0.99-9.el7 perl-XML-Simple.noarch 0:2.20-5.el7 Complete!
Nachfolgende rpm
-Pakete müssen als Abhängigkeiten zum Skript readdmarcfailure.pl
zusätzlich installiert werden:
Die Installation der Skriptabhängigkeiten, kann durch ausführen des nachfolgenden Befehls durchgeführt werden:
# yum install perl-DBI perl-Email-Address perl-Email-Date-Format perl-Email-MIME perl-Email-MIME-ContentType perl-Email-MIME-Encodings perl-Email-MessageID perl-Email-Simple perl-MIME-tools perl-MIME-Types perl-XML- Parser Loaded plugins: changelog, priorities 149 packages excluded due to repository priority protections Package perl-DBI-1.627-4.el7.x86_64 already installed and latest version Package perl-MIME-tools-5.505-1.el7.noarch already installed and latest version Package perl-XML-Parser-2.41-10.el7.x86_64 already installed and latest version Resolving Dependencies --> Running transaction check ---> Package perl-Email-Address.noarch 0:1.898-3.el7 will be installed ---> Package perl-Email-Date-Format.noarch 0:1.002-15.el7 will be installed ---> Package perl-Email-MIME.noarch 0:1.926-1.el7 will be installed ---> Package perl-Email-MIME-ContentType.noarch 0:1.017-1.el7 will be installed ---> Package perl-Email-MIME-Encodings.noarch 0:1.315-1.el7 will be installed ---> Package perl-Email-MessageID.noarch 0:1.404-1.el7 will be installed ---> Package perl-Email-Simple.noarch 0:2.203-1.el7 will be installed ---> Package perl-MIME-Types.noarch 0:1.38-2.el7 will be installed --> Finished Dependency Resolution Changes in packages about to be updated: Dependencies Resolved =============================================================================== Package Arch Version Repository Size =============================================================================== Installing: perl-Email-Address noarch 1.898-3.el7 base 39 k perl-Email-Date-Format noarch 1.002-15.el7 epel 17 k perl-Email-MIME noarch 1.926-1.el7 epel 45 k perl-Email-MIME-ContentType noarch 1.017-1.el7 epel 19 k perl-Email-MIME-Encodings noarch 1.315-1.el7 epel 18 k perl-Email-MessageID noarch 1.404-1.el7 epel 18 k perl-Email-Simple noarch 2.203-1.el7 epel 33 k perl-MIME-Types noarch 1.38-2.el7 epel 38 k Transaction Summary =============================================================================== Install 8 Packages Total download size: 227 k Installed size: 426 k Is this ok [y/d/N]: y Downloading packages: (1/8): perl-Email-Address-1.898-3.el7.noarch.rpm | 39 kB 00:00 (2/8): perl-Email-Date-Format-1.002-15.el7.noarch.rpm | 17 kB 00:00 (3/8): perl-Email-MIME-1.926-1.el7.noarch.rpm | 45 kB 00:00 (4/8): perl-Email-MIME-Encodings-1.315-1.el7.noarch.rpm | 18 kB 00:00 (5/8): perl-Email-MIME-ContentType-1.017-1.el7.noarch.rpm | 19 kB 00:00 (6/8): perl-Email-MessageID-1.404-1.el7.noarch.rpm | 18 kB 00:00 (7/8): perl-Email-Simple-2.203-1.el7.noarch.rpm | 33 kB 00:00 (8/8): perl-MIME-Types-1.38-2.el7.noarch.rpm | 38 kB 00:00 ------------------------------------------------------------------------------- Total 536 kB/s | 227 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : perl-Email-Address-1.898-3.el7.noarch 1/8 Installing : perl-Email-MessageID-1.404-1.el7.noarch 2/8 Installing : perl-Email-Date-Format-1.002-15.el7.noarch 3/8 Installing : perl-Email-Simple-2.203-1.el7.noarch 4/8 Installing : perl-Email-MIME-Encodings-1.315-1.el7.noarch 5/8 Installing : perl-Email-MIME-ContentType-1.017-1.el7.noarch 6/8 Installing : perl-MIME-Types-1.38-2.el7.noarch 7/8 Installing : perl-Email-MIME-1.926-1.el7.noarch 8/8 Verifying : perl-MIME-Types-1.38-2.el7.noarch 1/8 Verifying : perl-Email-MIME-1.926-1.el7.noarch 2/8 Verifying : perl-Email-MIME-ContentType-1.017-1.el7.noarch 3/8 Verifying : perl-Email-MessageID-1.404-1.el7.noarch 4/8 Verifying : perl-Email-MIME-Encodings-1.315-1.el7.noarch 5/8 Verifying : perl-Email-Simple-2.203-1.el7.noarch 6/8 Verifying : perl-Email-Address-1.898-3.el7.noarch 7/8 Verifying : perl-Email-Date-Format-1.002-15.el7.noarch 8/8 Installed: perl-Email-Address.noarch 0:1.898-3.el7 perl-Email-Date-Format.noarch 0:1.002-15.el7 perl-Email-MIME.noarch 0:1.926-1.el7 perl-Email-MIME-ContentType.noarch 0:1.017-1.el7 perl-Email-MIME-Encodings.noarch 0:1.315-1.el7 perl-Email-MessageID.noarch 0:1.404-1.el7 perl-Email-Simple.noarch 0:2.203-1.el7 perl-MIME-Types.noarch 0:1.38-2.el7 Complete!
Nachfolgende rpm
-Pakete müssen als Abhängigkeiten zum Skript readdmarcfailure.py
zusätzlich installiert werden:
Die Installation der Skriptabhängigkeiten, kann durch ausführen des nachfolgenden Befehls durchgeführt werden:
# yum install MySQL-python Loaded plugins: changelog, priorities 262 packages excluded due to repository priority protections Resolving Dependencies --> Running transaction check ---> Package MySQL-python.x86_64 0:1.2.5-1.el7 will be installed --> Finished Dependency Resolution Changes in packages about to be updated: Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: MySQL-python x86_64 1.2.5-1.el7 base 90 k Transaction Summary ================================================================================ Install 1 Package Total download size: 90 k Installed size: 284 k Is this ok [y/d/N]: y Downloading packages: MySQL-python-1.2.5-1.el7.x86_64.rpm | 90 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : MySQL-python-1.2.5-1.el7.x86_64 1/1 Verifying : MySQL-python-1.2.5-1.el7.x86_64 1/1 Installed: MySQL-python.x86_64 0:1.2.5-1.el7 Complete!
/etc/opendmarc/mkdmarc
Das Skript mkdmarc
erstellt eine Datenbank, Tabellen und die dazugehörigen Felder um DMARC-Reports, die per e-Mail eintreffen in die Datenbank zu transferieren.
HINWEIS - Nachfolgende Änderungen sollten am Skript vorgenommen werden, um die Datenbank, Tabellen und Felder möglichst einfach anlegen zu können
Alle Anpassungen am Skript sind mit
-- # Tachtler
gekennzeichnet.
-- Create database for DMARC data -- Copyright 2012, 2016, Taughannock Networks. All rights reserved. -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -- AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY -- WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -- POSSIBILITY OF SUCH DAMAGE. -- # Tachtler CREATE DATABASE IF NOT EXISTS dmarc; USE dmarc; -- # Tachtler CREATE TABLE IF NOT EXISTS report ( serial int(10) unsigned NOT NULL AUTO_INCREMENT, mindate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, maxdate timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', domain varchar(255) NOT NULL, org varchar(255) NOT NULL, reportid varchar(255) NOT NULL, email varchar(255), extra_contact_info varchar(255), policy_adkim varchar(20), policy_aspf varchar(20), policy_p varchar(20), policy_sp varchar(20), policy_pct tinyint unsigned, PRIMARY KEY (serial), UNIQUE KEY domain (domain,reportid) ); -- Use these commands to change the old IPv4 only DMARC table to the new one /*** alter table rptrecord modify ip int(10) unsigned; alter table rptrecord add column ip6 binary(16) after ip; alter table rptrecord add key serial6(serial,ip6); ***/ -- Use these commands to load in the optional IPv6 formatting functions /*** CREATE FUNCTION inet_6top RETURNS STRING SONAME 'mysql_ip6.so'; CREATE FUNCTION inet_pto6 RETURNS STRING SONAME 'mysql_ip6.so'; ***/ -- # Tachtler CREATE TABLE IF NOT EXISTS rptrecord ( serial int(10) unsigned NOT NULL, ip int(10) unsigned, ip6 binary(16), rcount int(10) unsigned NOT NULL, disposition enum('none','quarantine','reject'), reason varchar(255), dkimdomain varchar(255), dkimresult enum('none','pass','fail','neutral','policy','temperror','permerror'), spfdomain varchar(255), spfresult enum('none','neutral','pass','fail','softfail','temperror','permerror'), spf_align enum('fail', 'pass'), dkim_align enum('fail', 'pass'), identifier_hfrom varchar(255), KEY serial (serial,ip), KEY serial6 (serial,ip6) ) ENGINE=MyISAM; -- # Tachtler CREATE TABLE IF NOT EXISTS failure ( serial int(10) unsigned NOT NULL AUTO_INCREMENT, org varchar(255) NOT NULL, -- reported-domain bouncedomain varchar(255), -- MAIL FROM bouncebox@bouncedomain bouncebox varchar(255), fromdomain varchar(255), -- From: frombox@fromdomain frombox varchar(255), arrival TIMESTAMP, sourceip int unsigned, -- inet_aton(source-ip) sourceip6 BINARY(16), -- inet_6top(source-ip) headers TEXT, authres TEXT, PRIMARY KEY(serial), KEY(sourceip), KEY(fromdomain), KEY(bouncedomain) ) charset=utf8; -- # Tachtler -- GRANT all on dmarc.* to dmarc identified by 'xxx'; -- GRANT all on dmarc.* to dmarc@localhost identified by 'xxx'; -- # Tachtler - Create new users. CREATE USER 'dmarc_user'@'192.168.0.70' IDENTIFIED BY 'password'; CREATE USER 'dmarc_user'@'server70.idmz.tachtler.net' IDENTIFIED BY 'password'; -- # Tachtler - Grant ALL privileges to new users. GRANT ALL PRIVILEGES ON *.* TO 'dmarc_user'@'192.168.0.70' IDENTIFIED BY 'password' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON *.* TO 'dmarc_user'@'server70.idmz.tachtler.net' IDENTIFIED BY 'password' WITH GRANT OPTION; -- # Tachtler - Make sure that priviliges are reloaded. FLUSH PRIVILEGES;
Nachfolgende Änderungen wurden am Skript durchgeführt:
Erstellen der Datenbank dmarc
, wenn diese nocht nicht existieren sollte und anbringen eines ;
(Semikolon) hinter USE dmarc
.
Austausch des Syntax CREATE TABLE <Tabellenname>
gegen CREATE TABLE IF NOT EXISTS <Tabellenname>
.
Erstellen von zwei neuen Benutzer, dmarc_user'@'192.168.0.70
und dmarc_user'@'server70.idmz.tachtler.net
, welche via IP-Adresse oder FQDN vom Server 192.168.0.70
bzw. server70.idmz.tachtler.net
agieren dürfen und ein Passwort besitzen
Den beiden Benutzern dmarc_user'@'192.168.0.70
und dmarc_user'@'server70.idmz.tachtler.net
werden alle Privilegien in auf die Datenbank dmarc
gewährt.
-- # Tachtler - Make sure that priviliges are reloaded. FLUSH PRIVILEGES;
Die Benutzerrechte, inklusive der neu angelegten Benutzer mit den entsprechenden Rechten, sollen aktiviert werden, in dem die Rechte neu eingelesen werden sollen.
Die Ausführung des Skriptes und die damit verbunden Anlage der Datenbank, der Tabellen und Felder und der Nutzer, kann durch Ausführung des nachfolgenden Befehls durchgeführt werden, zudem jedoch das Kennwort zum Datenbankbenutzer root
erforderlich ist!:
# /usr/bin/mysql -u root -p < /etc/opendmarc/mkdmarc Enter password:
/usr/local/bin/readdmarc.pl
Das Skript readdmarc.pl
liest eine eingehende e-Mail, welche einen Report in einer ZIP-Datei enthält direkt vom Postfix dadurch ein, das Postfix die e-Mail direkt an das Skript übergibt und dieses dann alle Informationen extrahiert und in die Datenbank schreibt.
HINWIES - Anpassungen müssen beim Datenbankzugriff durchgeführt werden!
#!/usr/bin/perl # -*- perl -*- # $Header: /home/johnl/hack/dmarc/RCS/rddmarc,v 1.6 2016/05/01 21:43:40 johnl Exp $ # # Script to read DMARC aggregate reports and put summary info # into a database # Options: # -d print debug info # -x read XML files rather than mail messages # -r replace existing report rather than failing # Copyright 2012, 2016, Taughannock Networks. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. use strict; use Getopt::Std; use MIME::Parser; use MIME::Words qw(:all); use XML::Simple; use DBI; use Socket qw{:addrinfo inet_ntop inet_pton AF_INET6 AF_INET}; use PerlIO::gzip; use vars qw{$opt_d $opt_r $opt_x}; getopts('drx'); # Tachtler my $db_host = "mariadb.idmz.tachtler.net"; my $db_port = "3306"; my $db_name = "dmarc"; my $db_user = "dmarc_user"; my $db_pass = ""; my $dbh = DBI -> connect ("DBI:mysql:database=$db_name;host=$db_host;port=$db_port","$db_user",'geheim') or die "Cannot connect to database\n"; foreach my $i (@ARGV) { my ($zip, $ent, $isgzip); print "parsing $i\n"; if($opt_x) { open(XML, $i) or die "Cannot open XML file $i"; } else { my $parser = new MIME::Parser; $parser->output_dir("/tmp"); $ent = $parser->parse_open($i); my $body = $ent->bodyhandle; $zip = $body; my $mtype = $ent->mime_type; my $subj = decode_mimewords($ent->get('subject')); print " $subj"; # if multipart/whatever, look through the parts to find a ZIP if(lc $mtype =~ "multipart/") { print "Look through $mtype\n"; $zip = undef; my $npart = $ent->parts; for my $n (0..($npart-1)) { my $part = $ent->parts($n); if(lc $part->mime_type eq "application/gzip") { $zip = $part->bodyhandle; $isgzip = 1; last; } elsif(lc $part->mime_type eq "application/zip" or lc $part->mime_type eq "application/x-zip-compressed" or lc $part->mime_type eq "application/octet-stream") { $zip = $part->bodyhandle; last; } else { $part->bodyhandle->purge; # not useful } } die "no zip" unless $zip; } elsif(lc $mtype ne "application/zip") { print "don't understand $mtype\n"; next; } if(defined($zip->path)) { print "body is in " . $zip->path . "\n" if $opt_d; } else { print "body is nowhere\n"; next; } if($isgzip) { open(XML, "<:gzip", $zip->path) or die "cannot ungzip $zip->path"; } else { open(XML,"unzip -p " . $zip->path . " |") or die "cannot unzip $zip->path"; } } my $xml = ""; $xml .= $_ while <XML>; close XML; $ent->purge if $ent; $zip->purge if $zip; my $xs = XML::Simple->new(); print "XML is ======\n$xml\n=====\n" if $opt_d; my $ref = $xs->XMLin($xml, SuppressEmpty => ''); my %xml = %{$ref}; #print join "\n",keys %xml; #print "\n"; my $from = $xml{'report_metadata'}->{'date_range'}->{'begin'}; my $to = $xml{'report_metadata'}->{'date_range'}->{'end'}; my $org = $xml{'report_metadata'}->{'org_name'}; my $id = $xml{'report_metadata'}->{'report_id'}; my $email = $xml{'report_metadata'}->{'email'}; my $extra = $xml{'report_metadata'}->{'extra_contact_info'}; my $domain = $xml{'policy_published'}->{'domain'}; my $policy_adkim = $xml{'policy_published'}->{'adkim'}; my $policy_aspf = $xml{'policy_published'}->{'aspf'}; my $policy_p = $xml{'policy_published'}->{'p'}; my $policy_sp = $xml{'policy_published'}->{'sp'}; my $policy_pct = $xml{'policy_published'}->{'pct'}; print "report $org ($id) $from to $to for $domain\n" if $opt_d; # see if already stored my ($xorg, $xid, $serial) = $dbh->selectrow_array(qq{SELECT org,reportid,serial FROM report WHERE reportid=?}, undef, $id); if($xorg) { if($opt_r) { print "Replacing $xorg $xid\n"; $dbh->do(qq{DELETE from rptrecord WHERE serial=?}, undef, $serial) or die "cannot delete old records" . $dbh->errstr; } else { print "Already have $xorg $xid, skipped\n"; next; } } my $sql = qq{INSERT INTO report(serial,mindate,maxdate,domain,org,reportid,email,extra_contact_info,policy_adkim,policy_aspf,policy_p,policy_sp,policy_pct) VALUES(NULL,FROM_UNIXTIME(?),FROM_UNIXTIME(?),?,?,?,?,?,?,?,?,?,?)}; $sql = qq{REPLACE INTO report(serial,mindate,maxdate,domain,org,reportid,email,extra_contact_info,policy_adkim,policy_aspf,policy_p,policy_sp,policy_pct) VALUES('$serial',FROM_UNIXTIME(?),FROM_UNIXTIME(?),?,?,?,?,?,?,?,?,?,?)} if $xorg; $dbh->do($sql, undef, $from, $to, $domain, $org, $id, $email, $extra, $policy_adkim, $policy_aspf, $policy_p, $policy_sp, $policy_pct) or die "cannot make report" . $dbh->errstr; $serial = $dbh->{'mysql_insertid'} || $dbh->{'insertid'} unless $xorg; print " serial $serial "; my $record = $xml{'record'}; sub dorow($$) { my ($serial,$recp) = @_; my %r = %$recp; my $ip = $r{'row'}->{'source_ip'}; my $count = $r{'row'}->{'count'}; my $disp = $r{'row'}->{'policy_evaluated'}->{'disposition'}; my $dkim_align = $r{'row'}->{'policy_evaluated'}->{'dkim'}; my $spf_align = $r{'row'}->{'policy_evaluated'}->{'spf'}; my $identifier_hfrom = $r{'identifiers'}->{'header_from'}; print "\nip $ip, count $count, disp $disp" if $opt_d; my ($dkim, $dkimresult, $spf, $spfresult, $reason); my $rp = $r{'auth_results'}->{'dkim'}; printf " rp $rp\n" if $opt_d; if(ref $rp eq "HASH") { $dkim = $rp->{'domain'}; $dkim = undef if ref $dkim eq "HASH"; $dkimresult = $rp->{'result'}; } else { # array # glom sigs together, report first result $dkim = join '/',map { my $d = $_->{'domain'}; ref $d eq "HASH"?"": $d } @$rp; $dkimresult = $rp->[0]->{'result'}; } $rp = $r{'auth_results'}->{'spf'}; if(ref $rp eq "HASH") { $spf = $rp->{'domain'}; $spfresult = $rp->{'result'}; } else { # array # glom domains together, report first result $spf = join '/',map { my $d = $_->{'domain'}; ref $d eq "HASH"? "": $d } @$rp; $spfresult = $rp->[0]->{'result'}; } $rp = $r{'row'}->{'policy_evaluated'}->{'reason'}; if(ref $rp eq "HASH") { $reason = $rp->{'type'}; } else { $reason = join '/',map { $_->{'type'} } @$rp; } #print "ip=$ip, count=$count, disp=$disp, r=$reason,"; #print "dkim=$dkim/$dkimresult, spf=$spf/$spfresult\n"; # figure out if it's IPv4 or IPv6 my ($nip, $iptype, $ipval); if($nip = inet_pton(AF_INET, $ip)) { $ipval = unpack "N", $nip; $iptype = "ip"; } elsif($nip = inet_pton(AF_INET6, $ip)) { $ipval = "X'" . unpack("H*",$nip) . "'"; $iptype = "ip6"; } else { print "??? mystery ip $ip\n"; next; } print "$iptype = $ipval, spf $spf_align, dkim $dkim_align\n" if $opt_d; $dbh->do(qq{INSERT INTO rptrecord(serial,$iptype,rcount,disposition,spf_align,dkim_align,reason,dkimdomain,dkimresult,spfdomain,spfresult,identifier_hfrom) VALUES(?,$ipval,?,?,?,?,?,?,?,?,?,?)},undef, $serial,$count,$disp,$spf_align,$dkim_align,$reason,$dkim,$dkimresult,$spf,$spfresult,$identifier_hfrom) or die "cannot insert record " . $dbh->{'mysql_error'}; } # dorow if(ref $record eq "HASH") { print "single record\n"; dorow($serial,$record); } elsif(ref $record eq "ARRAY") { print "multi record\n"; foreach my $row (@$record) { dorow($serial,$row); } } else { print "mystery type " . ref($record) . "\n"; } }
Nachfolgende Änderungen wurden am Skript durchgeführt:
Anpassen des Datenbankzugriffs in Bezug auf den Datenbank-Server, Datenbank-Port, den Benutzernamen und das Passwort.
HINWEIS - Die Variable $db_pass
wird hier nicht genutzt, sondern das Passwort wird direkt in den Verbindungsaufbau eingetragen, da bei komplexen Passwörtern mit Sonderzeichenkombinationen sonst Probleme auftreten könnten!
Nachfolgender Befehl setzte die Datei- und Besitzrechte für das Skript wie folgt:
# chmod 755 /usr/local/bin/readdmarc.pl
und
# chown root:root /usr/local/bin/readdmarc.pl
/usr/local/bin/readdmarcfailure.pl
Das Skript readdmarcfailure.pl
liest eine eingehende e-Mail, welche einen Fehlerreport in einer ZIP-Datei enthält direkt vom Postfix dadurch ein, das Postfix die e-Mail direkt an das Skript übergibt und dieses dann alle Informationen extrahiert und in die Datenbank schreibt.
HINWIES - Anpassungen müssen beim Datenbankzugriff durchgeführt werden!
#!/usr/bin/perl # Script zum automatischen Verarbeiten der DMARC-Forensic-Mails in die mySQL-Datenbank dmarc # basierend auf den DMARC Reporting scripts (http://www.taugh.com/rddmarc) von John Levine (http://www.johnlevine.com/) # Über STDIN wird dem Script readdmarcfailure die eMail übergeben, also z.B.: $ readdmarcfailure < mailtext # 2014-05-17 : V.01 by Django (django@mailserver.guru) use strict; use MIME::Parser; use MIME::Words qw(:all); use DBI; use Email::Address; use Email::MIME; my $buffer = ''; my $input = ''; my $db_host = "mariadb.idmz.tachtler.net"; my $db_port = "3306"; my $db_name = "dmarc"; my $db_user = "dmarc_user"; my $db_pass = ""; my $dbh = DBI -> connect ("DBI:mysql:database=$db_name;host=$db_host;port=$db_port","$db_user",'geheim') or die "Cannot connect to database\n"; while (sysread(STDIN, $input, 1024) > 0) { $buffer .= $input; } my ($zip, $ent, $isgzip); my $parser = new MIME::Parser; $parser->output_dir("/tmp"); $ent = $parser->parse_data($buffer); my $body = $ent->bodyhandle; $zip = $body; my $mtype = $ent->mime_type; my $subject = decode_mimewords($ent->get('subject')); my $date = decode_mimewords($ent->get('date')); my $from = decode_mimewords($ent->get('from')); # Nachricht eine valider Report? $buffer =~ /(Content-Type:\smessage\/feedback-report)/; my $valid_report = $1; if ($valid_report ne "Content-Type: message/feedback-report") { print "no valid report!\n"; exit; } else { print "Parsing eMail (Header & Body)\n\n"; } # Daten aus Content-Type"message/feedback-report" extrahieren: my $content_block = Email::MIME->new($buffer); my ($fr) = grep { $_->content_type =~ m{^message/feedback-report} } $content_block->subparts; my $mfr = $fr->body; $from =~ /<(.+?)@(.+?)>/; my $domrep = $2; $mfr =~ /(Feedback-Type:\ )(.*)/; my $type = $2; $mfr =~ /(Version:\s)(.*)/; my $version = $2; $mfr =~ /(User-Agent:\s)(.*)/; my $useragent = $2; $mfr =~ /(Auth-Failure:\s)(.*)/; my $authfailure = $2; $mfr =~ /(Authentication-Results:\s)(.*)/; my $result = $2; $mfr =~ /(Original-Envelope-Id:\s)(.*)/; my $envid = $2; $mfr =~ /(Original-Mail-From:\s)(.*)@(.*)/; my $origbox = $2; my $origdom = $3; $mfr =~ /(Source-IP:\s)(.*)/; my $source = $2; $mfr =~ /(Reported-Domain:\s)(.*)/; my $domain = $2; # Daten aus Content-Type"test/rfc822-headers" extrahieren: my ($rfc822_headers) = grep { $_->content_type =~ m{^text/rfc822-headers} } $content_block->subparts; my $org_headers = $rfc822_headers->body; $org_headers =~ /(From:\s)(.*<)(.+?)@(.+?)(>)/; my $frombox = $3; my $fromdom = $4; # Daten in die mySQL-Datenbank schreiben my $sql = "INSERT INTO `dmarc`.`failure` (`serial`, `reporter`, `domain`, `type`, `authfailure`, `result`, `messageid`, `bouncedomain`, `bouncebox`, `fromdomain`, `frombox`, `arrival`, `sourceip`, `headers`) VALUES (NULL, '$domrep', '$domain', '$type', '$authfailure', '$result', '$envid', '$origdom', '$origbox', '$fromdom', '$frombox', NOW(), '$source', '$org_headers')"; $dbh->do($sql) or die "cannot store report to database!" . $dbh->errstr;
Nachfolgende Änderungen wurden am Skript durchgeführt:
Anpassen des Datenbankzugriffs in Bezug auf den Datenbank-Server, Datenbank-Port, den Benutzernamen und das Passwort.
my $sql = "INSERT INTO `dmarc`.`failure` (`serial`, `reporter`, `domain`, `type`, `authfailure`, `result`, `messageid`, `bouncedomain`, `bouncebox`, `fromdomain`, `frombox`, `arrival`, `sourceip`, `headers`) VALUES (NULL, '$domrep', '$domain', '$type', '$authfailure', '$result', '$envid', '$origdom', '$origbox', '$fromdom', '$frombox', NOW(), '$source', '$org_headers')";
Anpassen des Namens der Datenbank auf dmarc
(nach „INSERT INTO `dmarc`…).
HINWEIS - Die Variable $db_pass
wird hier nicht genutzt, sondern das Passwort wird direkt in den Verbindungsaufbau eingetragen, da bei komplexen Passwörtern mit Sonderzeichenkombinationen sonst Probleme auftreten könnten!
Nachfolgender Befehl setzte die Datei- und Besitzrechte für das Skript wie folgt:
# chmod 755 /usr/local/bin/readdmarcfailure.pl
und
# chown root:root /usr/local/bin/readdmarcfailure.pl
/usr/local/bin/readdmarcfailure.py
Das Skript readdmarcfailure.py
liest eine eingehende e-Mail, welche einen Fehlerreport in einer ZIP-Datei enthält direkt vom Postfix dadurch ein, das Postfix die e-Mail direkt an das Skript übergibt und dieses dann alle Informationen extrahiert und in die Datenbank schreibt.
HINWIES - Anpassungen müssen beim Datenbankzugriff durchgeführt werden!
#!/usr/local/bin/python # $Header: /home/johnl/hack/dmarc/RCS/dmarcfail.py,v 1.2 2015/05/02 20:15:02 johnl Exp $ # parse DMARC failure reports, add it to the mysql database # optional arguments are names of files containing ARF messages, # otherwise it reads stdin # Copyright 2012, Taughannock Networks. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. #!/usr/local/bin/python # parse a DMARC failure report, add it to the mysql database import re import email import time import MySQLdb # Tachtler # default: db = MySQLdb.connect(user='dmarc',passwd='x',db='dmarc', use_unicode=True) db = MySQLdb.connect(host='mariadb.idmz.tachtler.net',port=3306,user='dmarc_user',passwd='geheim',db='dmarc', use_unicode=True) MySQLdb.paramstyle='format' def dmfail(h,f): e = email.message_from_file(h) if(e.get_content_type() != "multipart/report"): print f,"is not a report" return for p in e.get_payload(): if(p.get_content_type() == "message/feedback-report"): r = email.parser.Parser() fr = r.parsestr(p.get_payload()[0].as_string(), True) fx = re.search(r'<(.+?)@(.+?)>', fr['original-mail-from']) if fx: origbox,origdom = fx.group(1,2) else: origbox,origdom = (None,None) if 'arrival-date' in fr: arr = int(email.utils.mktime_tz(email.utils.parsedate_tz(fr['arrival-date']))) else: # fake it with message date arr = int(email.utils.mktime_tz(email.utils.parsedate_tz(e['date']))) authres = fr['authentication-results'] if 'authentication-results' in fr else None elif(p.get_content_type() == "message/rfc822" or p.get_content_type() == "text/rfc822-headers"): if p.is_multipart(): # library thinks message/rfc822 is multipart m = email.message_from_string(p.get_payload(0).as_string()) else: m = email.message_from_string(p.get_payload()) frombox = fromdom = None if 'from' in m: fx = re.search(r'<(.+)@(.+)>', m['from']) if(fx): frombox,fromdom = fx.group(1,2) else: t = re.sub(m['from'],r'\s+|\([^)]*\)',"") fx = re.match(r'(.+)@(.+)', t) if(fx): frombox,fromdom = fx.group(1,2) # OK, parsed it, now add an entry to the database #print fr['reported-domain'],origdom,origbox,fromdom,frombox,arr,fr['source-ip'],"===" #print m.as_string() #print "===" c = db.cursor() c.execute("""INSERT INTO failure(serial,org,bouncedomain,bouncebox,fromdomain, frombox,arrival,sourceip,headers,authres) VALUES(NULL,%s,%s,%s,%s,%s,FROM_UNIXTIME(%s),INET_ATON(%s),%s,%s)""", (fr['reported-domain'],origdom,origbox,fromdom,frombox,arr,fr['source-ip'],m.as_string(),authres)) print "Inserted failure report %s" % c.lastrowid c.close() if __name__ == "__main__": import sys if(len(sys.argv) < 2): dmfail(sys.stdin,"stdin"); else: for f in sys.argv[1:]: h = open(f) dmfail(h, f) h.close()
Nachfolgende Änderungen wurden am Skript durchgeführt:
db = MySQLdb.connect(host='mariadb.idmz.tachtler.net',port=3306,user='dmarc_user',passwd='geheim',db='dmarc', use_unicode=True)
Anpassen des Datenbankzugriffs in Bezug auf den Datenbank-Server, Datenbank-Port, den Benutzernamen und das Passwort.
Nachfolgender Befehl setzte die Datei- und Besitzrechte für das Skript wie folgt:
# chmod 755 /usr/local/bin/readdmarcfailure.py
und
# chown root:root /usr/local/bin/readdmarcfailure.py
/etc/postfix/master.cf
Nachfolgende Definitionen sind erforderlich, damit Postfix das Skript bekannt ist und nutzen kann:
(Nur relevanter Ausschnitt:)
... # Tachtler readdmarc unix - n n - 1 pipe flags=DRhu user=nobody argv=/usr/local/bin/readdmarc.pl # Tachtler readdmarcfailure unix - n n - 1 pipe flags=DRhu user=nobody argv=/usr/local/bin/readdmarcfailure.pl
ODER
# Tachtler readdmarcfailure unix - n n - 1 pipe flags=DRhu user=nobody argv=/usr/local/bin/readdmarcfailure.py ...
/etc/postfix/main.cf
Nachfolgende Änderungen sind erforderlich, falls noch keine transport_maps
verwendet werden:
(Nur relevanter Ausschnitt):
... # TRANSPORT MAP # # See the discussion in the ADDRESS_REWRITING_README document. # Tachtler - new - transport_maps = btree:/etc/postfix/transport_maps, $relay_domains ...
/etc/postfix/transport_maps
Zur Weiterleitung von Postfix an die Skripte /usr/local/bin/readddmarc.pl
und /usr/local/bin/readddmarcfailure.pl
, ist es erforderlich diese in der /etc/postfix/transport_maps
entsprechend zu definieren, wie nachfolgend gezeigt:
(Komplette Konfigurationsdatei:)
dmarc-aggregate@tachtler.net readdmarc: dmarc-incorrect@tachtler.net readdmarcfailure:
HINWEIS - Als Trennzeichen zwischen den beiden Einträgen, sollte am besten die [TAB]-Taste verwendet werden, es ist aber auch die Trennung durch [Leerzeichen] möglich!
WICHTIG - Zum Abschluss muss die Konfigurationsdatei in ein Datenbank-Format, z.B. das Format btree
mithilfe des Postfix eigenen Befehls postmap
umgewandelt werden, wie nachfolgender Befehl zeigt:
# postmap btree:/etc/postfix/transport_maps
Anschließend sollte eine neue Datei mit dem Namen /etc/postfix/transport_maps.db
entstanden sein, was mit nachfolgendem Befehl überprüft werden:
# ls -l /etc/postfix/transport_maps* -rw-r--r-- 1 root root 24 Aug 19 09:51 /etc/postfix/transport_maps -rw-r--r-- 1 root root 8192 Aug 19 09:51 /etc/postfix/transport_maps.db
**ODER** /etc/aliases
HINWEIS - Als alternative Weiterleitung von Postfix an die Skripte /usr/local/bin/eadddmarc.pl
und /usr/local/bin/readddmarcfailure.pl
, ist es ebenfalls möglich anstelle von /etc/postfix/transport_maps
, /etc/postfix/main.cf
und /etc/postfix/master.cf
diese in der /etc/aliases
entsprechend zu definieren, wie nachfolgend gezeigt:
(Nur relevanter Ausschnitt:)
... dmarc-aggregate: "|/usr/local/bin/readdmarc.pl" dmarc-incorrect: "|/usr/local/bin/readdmarcfailure.pl"
ODER </code> … dmarc-aggregate: „|/usr/local/bin/readdmarc.pl“ dmarc-incorrect: „|/usr/local/bin/readdmarcfailure.py“ </code>
Anschließend ist sind die Änderungen noch in ein für Postfix besser lesbares Datenbankformat umzuwandeln, was mit nachfolgendem Befehl durchgeführt werden kann:
# newaliases
Konfiguration: DMARC Reports Web GUI
Unter nachfolgenden externen Link
kann eine Web GUI heruntergeladen werden, welche zur Anzeige der Daten, die innerhalb der Datenbank gespeichert sind, genutzt werden kann.
Nachfolgend soll die Konfiguration eines Apache HTTP Servers durchgeführt werden, um die in PHP Net geschrieben DMARC Reports Web GUI zur anzeige zu bringen.
Voraussetzungen
Als Voraussetzung für die Installation von DMARC Reports sind folgende Komponenten erforderlich:
- Lauffähiger Datenbank-Server z.B. MariaDB
- Siehe auch den internen Link: MariaDB CentOS 7
- Lauffähiger Web-Server z.B. Apache HTTP Server
- Siehe auch den internen Link: Apache HTTP Server CentOS 7
- PHP PHP Net
Herunterladen
Bevor die in PHP Net geschriebene DMARC Reports Web GUI DMARC Reports heruntergeladen werden soll, muss zuerst auf dem Server auf dem der Apache HTTP Server läuft ein neues Verzeichnis angelegt werden, was mit nachfolgendem Befehl durchgeführt werden kann:
# mkdir /var/www/dmarcreports
Anschließend kann mit nachfolgendem Befehl die DMARC Reports Web GUI DMARC Reports direkt in das neu erstellte Verzeichnis heruntergeladen und umbenannt werden:
# wget -O /var/www/dmarcreports/index.php http://www.techsneeze.com/files/dmarcts-index.txt --2015-10-24 06:20:40-- http://www.techsneeze.com/files/dmarcts-index.txt Resolving www.techsneeze.com (www.techsneeze.com)... 74.208.243.183, 2607:f1c0:830:6800::4d:9c7e Connecting to www.techsneeze.com (www.techsneeze.com)|74.208.243.183|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 4253 (4.2K) [text/plain] Saving to: ‘/var/www/dmarcreports/index.php’ 100%[======================================>] 4,253 --.-K/s in 0.002s 2015-10-24 06:20:41 (2.37 MB/s) - ‘/var/www/dmarcreports/index.php’ saved [4253/4253]
/var/www/dmarcreports/index.php
Bevor die in PHP Net geschriebene DMARC Reports Web GUI DMARC Reports genutzt werden kann, ist es noch erforderlich die am Anfang des Skripts befindlichen Variablen anzupassen, damit ein Zugriff auf die Datenbank durch das Skript durchgeführt werden kann.
(Komplettes Skript)
<?php /////////////////////////////////////////////////////////////////////////// $dbhost="mariadb.idmz.tachtler.net"; $dbname="dmarc"; $dbuser="dmarc_user"; $dbpass="geheim"; /////////////// NO CHANGES BELOW ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Make a MySQL Connection mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error()); mysql_select_db($dbname) or die(mysql_error()); $query_report = "SELECT * FROM report ORDER BY mindate"; $result_report = mysql_query($query_report) or die(mysql_error()); echo "<title>DMARC Report</title>"; echo "<head>\n"; echo "</head>\n"; echo "<html>\n"; echo "<body>\n"; echo "<center><h1>DMARC Reports</h1></center>\n"; echo "<hr align=center width=90% noshade>\n"; function format_date($date, $format){ $answer = date($format, strtotime($date)); return $answer; }; echo "<table align=center border=0 cellpadding=3>\n"; echo "<thead><tr><th>Start Date</th><th>End Date</th><th>Domain</th><th>Reporting Organization</th><th>Report ID</th><th>Messages</th></tr></thead><tbody>\n"; $result_report = mysql_query($query_report) or die(mysql_error()); while($row = mysql_fetch_array($result_report)){ $array_report[] = $row; $message_query = "SELECT *, SUM(rcount) FROM rptrecord WHERE serial = {$row['serial']}"; $message_process = mysql_query($message_query) or die(mysql_error()); $message_result = mysql_fetch_array($message_process); $date_output_format = "r"; echo "<tr align=center>"; echo "<td align=right>". format_date($row['mindate'], $date_output_format). "</td><td align=right>". format_date($row['maxdate'], $date_output_format). "</td><td>". $row['domain']. "</td><td>". $row['org']. "</td><td><a href=?report=". $row['serial']. "#rpt". $row['serial']. ">". $row['reportid']. "</a></td><td>". $message_result['SUM(rcount)']. "</td>"; echo "</tr>"; echo "\n"; } echo "</tbody>"; echo "</table>"; echo " <br />"; echo "\n"; //echo "-------------------------------------------------------------------------------------"; echo "<hr align=center width=90% noshade>"; echo " <br />"; echo "\n"; /////////Start Lower Section // Get value (if it exists) from URL $displayreport = 0; if ($_GET) { $displayreport = $_GET["report"]; } if($displayreport !== 0){ $current = 0; $query_date = "SELECT * FROM report where serial = $displayreport"; $query_rptrecord = "SELECT * FROM rptrecord where serial = $displayreport"; $result_date = mysql_query($query_date) or die(mysql_error()); $showdate = mysql_fetch_array($result_date); echo "<br/><center><strong>". format_date($showdate['mindate'], r ). "</strong></center><br />\n"; $result_rptrecord = mysql_query($query_rptrecord) or die(mysql_error()); echo "<table align=center border=0 cellpadding=2>"; echo "<th>IP Address</th><th>Host Name</th><th>Message Count</th><th>Disposition</th><th>Reason</th><th>DKIM Domain</th><th>DKIM Result</th><th>SPF Domain</th><th>SPF Result</th>\n"; while($row = mysql_fetch_array($result_rptrecord)){ $rowcolor="FFFFFF"; if (($row['dkimresult'] == "fail") && ($row['spfresult'] == "fail")){ $rowcolor="FF0000"; //red } elseif (($row['dkimresult'] == "fail") || ($row['spfresult'] == "fail")){ $rowcolor="FFA500"; //orange } elseif (($row['dkimresult'] == "pass") && ($row['spfresult'] == "pass")){ $rowcolor="00FF00"; //lime } else { $rowcolor="FFFF00"; //yellow }; echo "<tr align=center bgcolor=". $rowcolor. ">"; echo "<td><a name=rpt". $row['serial'].">". long2ip($row['ip']). "</td><td>". gethostbyaddr(long2ip($row['ip'])). "</td><td>". $row['rcount']. "</td><td>". $row['disposition']. "</td><td>". $row['reason']. "</td><td>". $row['dkimdomain']. "</td><td>". $row['dkimresult']. "</td><td>". $row['spfdomain']. "</td><td>". $row['spfresult']. "</td>"; echo "</tr>"; echo "\n"; } echo "</table>"; echo "<hr align=center width=90% noshade>"; echo "<center><h5>Brought to you by <a href=http://www.techsneeze.com>TechSneeze.com</a> - <a href=mailto:dave@techsneeze.com>dave@techsneeze.com</a></h5></center><br />\n"; } echo "</body>"; echo "</html>"; //var_dump($array_report); //var_dump($message_result); //print_r(array_keys($array_report[5])); ?>
Nachfolgende Änderungen wurden am Skript durchgeführt:
$dbhost="mariadb.idmz.tachtler.net"; $dbname="dmarc"; $dbuser="dmarc_user"; $dbpass="geheim";
Anpassung der Zugriffsdaten für die Datenbank, wie Datenbank-Server, Benutzername und das Passwort.
/etc/httpd/conf.d/vhost.conf
Es soll ein virtueller Host im Apache HTTP Server eingerichtet werden.
Siehe dazu auch nachfolgende interne Links:
Dazu kann der Inhalt einer möglichen Konfigurationsdatei
/etc/httpd/conf.d/dmarcreports.conf
wie nachfolgend dargestellt entsprechend erstellt werden:
(Komplette Konfigurationsdatei)
# # dmarcreports.tachtler.net (DMARC-Reports for OpenDMARC) # <VirtualHost *:80> ServerAdmin webmaster@tachtler.net ServerName dmarcreports.tachtler.net ServerAlias www.dmarcreports.tachtler.net ServerPath / DocumentRoot "/var/www/dmarcreports" <Directory "/var/www/dmarcreports"> Options -Indexes +FollowSymLinks # Tachtler (enable for .htaccess file support) # AllowOverride AuthConfig AllowOverride None # Tachtler (enable for unlimited access) Require all granted </Directory> DirectoryIndex index.php ErrorLog logs/dmarcreports_error.log SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded CustomLog logs/dmarcreports_access.log combined env=!forwarded CustomLog logs/dmarcreports_access.log combined_proxypass env=forwarded </VirtualHost>
HINWEIS - Zu beachten sind hier, die Zeilen mit dem Inhalten
DirectoryIndex index.php
- da hier die erste Seite der Anwendung ein PHP-Script ist!
Nach Durchführung der vorhergehenden Konfigurationsschritte, sollte einem Neustart nichts im Wege stehen und die Apache VHOST-Konfiguration angezogen werden:
# systemctl restart httpd.service
HINWEIS - Es erfolgen keine weiteren Ausgaben, wenn der Start erfolgreich war !
Aufruf DMARC Reports Web GUI
Anschließend kann die DMARC Reports Web GUI DMARC Reports über den Browser aufgerufen werden. Falls bereits Daten in der Datenbank stehen, könnte die Bildschirmausgabe wie folgt aussehen:
Performace Verbesserung DMARC Reports Web GUI
Um den Seitenaufbau der DMARC Reports Web GUI zu beschleunigen, kann auf die DNS-Auflösung der IP-Adresse verzichtet werden. Dazu sind nachfolgende Änderungen in der HTML-Seite unter:
/var/www/dmarcreports/index.php
erforderlich:
Alle Anpassungen sind mit dem Kommentar
# Tachtler
versehen.
(Nur relevanter Ausschnitt)
Funktion: function tmpl_reportData($reportnumber, $allowed_reports)
- (ab Zeile 80)
function tmpl_reportData($reportnumber, $allowed_reports) { if (!$reportnumber) { return ""; } $reportdata[] = ""; $reportdata[] = "<!-- Start of report rata -->"; if (isset($allowed_reports[BySerial][$reportnumber])) { $row = $allowed_reports[BySerial][$reportnumber]; $reportdata[] = "<div class='center reportdesc'><p> Report from ".$row['org']." for ".$row['domain']."<br>(". format_date($row['mindate'], "r" ). " - ".format_date($row['maxdate'], "r" ).")</p></div>"; } else { return "Unknown report number!"; } $reportdata[] = "<a id='rpt".$reportnumber."'></a>"; $reportdata[] = "<table class='reportdata'>"; $reportdata[] = " <thead>"; $reportdata[] = " <tr>"; $reportdata[] = " <th>IP Address</th>"; # Tachtler - DISABLED - # default: $reportdata[] = " <th>Host Name</th>"; # $reportdata[] = " <th>Host Name</th>"; $reportdata[] = " <th>Message Count</th>"; $reportdata[] = " <th>Disposition</th>"; $reportdata[] = " <th>Reason</th>"; $reportdata[] = " <th>DKIM Domain</th>"; $reportdata[] = " <th>Raw DKIM Result</th>"; $reportdata[] = " <th>SPF Domain</th>"; $reportdata[] = " <th>Raw SPF Result</th>"; $reportdata[] = " </tr>"; $reportdata[] = " </thead>"; $reportdata[] = " <tbody>"; global $mysqli; $sql = "SELECT * FROM rptrecord where serial = $reportnumber"; $query = $mysqli->query($sql) or die("Query failed: ".$mysqli->error." (Error #" .$mysqli->errno.")"); while($row = $query->fetch_assoc()) { $status=""; if (($row['dkimresult'] == "fail") && ($row['spfresult'] == "fail")) { $status="red"; } elseif (($row['dkimresult'] == "fail") || ($row['spfresult'] == "fail")) { $status="orange"; } elseif (($row['dkimresult'] == "pass") && ($row['spfresult'] == "pass")) { $status="lime"; } else { $status="yellow"; }; if ( $row['ip'] ) { $ip = long2ip($row['ip']); } if ( $row['ip6'] ) { $ip = inet_ntop($row['ip6']); } $reportdata[] = " <tr class='".$status."'>"; $reportdata[] = " <td>". $ip. "</td>"; # Tachtler - DISABLED - # default: $reportdata[] = " <td>". gethostbyaddr($ip). "</td>"; # $reportdata[] = " <td>". gethostbyaddr($ip). "</td>"; $reportdata[] = " <td>". $row['rcount']. "</td>"; $reportdata[] = " <td>". $row['disposition']. "</td>"; $reportdata[] = " <td>". $row['reason']. "</td>"; $reportdata[] = " <td>". $row['dkimdomain']. "</td>"; $reportdata[] = " <td>". $row['dkimresult']. "</td>"; $reportdata[] = " <td>". $row['spfdomain']. "</td>"; $reportdata[] = " <td>". $row['spfresult']. "</td>"; $reportdata[] = " </tr>"; } $reportdata[] = " </tbody>"; $reportdata[] = "</table>"; $reportdata[] = "<!-- End of report rata -->"; $reportdata[] = ""; #indent generated html by 2 extra spaces return implode("\n ",$reportdata); }
Die Sortierreihenfolge der DMARC Reports Web GUI von Absteigend auf Aufsteigen - neuster Eintrag als erster Eintrag - kann mit nachfolgender Anpassung durchgeführt werden:
(Nur relevanter Ausschnitt)
Funktion: main
- (ab Zeile 179)
//#################################################################### //### main ########################################################### //#################################################################### // The file is expected to be in the same folder as this script, and it // must exist. include "dmarcts-report-viewer-config.php"; // Make a MySQL Connection using mysqli $mysqli = new mysqli($dbhost, $dbuser, $dbpass, $dbname); if ($mysqli->connect_errno) { echo "Error: Failed to make a MySQL connection, here is why: \n"; echo "Errno: " . $mysqli->connect_errno . "\n"; echo "Error: " . $mysqli->connect_error . "\n"; exit; } define("BySerial", 1); define("ByDomain", 2); define("ByOrganisation", 3); // Get allowed reports and cache them - using serial as key $allowed_reports = array(); # Include the rcount via left join, so we do not have to make an sql query for every single report. # Tachtler - CHANGE ORDER to DESCending - # default: $sql = "SELECT report.* , sum(rptrecord.rcount) as rcount FROM `report` LEFT Join rptrecord on report.serial = rptrecord.serial group by serial order by mindate"; $sql = "SELECT report.* , sum(rptrecord.rcount) as rcount FROM `report` LEFT Join rptrecord on report.serial = rptrecord.serial group by serial order by mindate DESC"; $query = $mysqli->query($sql) or die("Query failed: ".$mysqli->error." (Error #" .$mysqli->errno.")"); while($row = $query->fetch_assoc()) { //todo: check ACL if this row is allowed if (true) { //add data by serial $allowed_reports[BySerial][$row['serial']] = $row; //make a list of serials by domain and by organisation $allowed_reports[ByDomain][$row['domain']][] = $row['serial']; $allowed_reports[ByOrganisation][$row['org']][] = $row['serial']; } } if(isset($_GET['report']) && is_numeric($_GET['report'])){ $reportid=$_GET['report']; }elseif(!isset($_GET['report'])){ $reportid=false; }else{ die('Invalid Report ID'); } // Generate Page with report list and report data (if a report is selected). echo tmpl_page( "" .tmpl_reportList($allowed_reports) .tmpl_reportData($reportid, $allowed_reports ) ); ?>
Test Werkzeuge
Nachfolgende externe Links führen zu verschiedenen Test Werkzeugen: