Mailserver - Logfileauswertungen unter CentOS 7.x
Oft werden vom Management hübsche bunte Auswertungsgraphiken gewünscht, um sich so mehr oder weniger ein Bild davon zu machen, ob und wie der/die Mailserver mit der anfallenden Menge an elektronischer Post umgehen können. Auch als Postmaster und Admin können wir uns so einen kurzen Überblick verschaffen, ob alles im grünen Bereich ist, ohne dazu extra langwierig im Logfile unseres Mailservers herum zu suchen. Hierzu stehen uns mehrere Hilfsprogramme zur Verfügung.
Im Detail wollen wir uns nun drei der Varianten genauer ansehen.
pflogsumm
Mit Hilfe dieses kleinen Perlscriptes können wir uns einen täglichen Statusbericht unseres Mailservers erstellen lassen und wissen so das was unserem MX widerfahren ist. Das Perlscript pflogsumm wertet hierzu das Logfile /var/log/maillog unseres Mailservers aus.
Installation
Das Perlscript pflogsumm befindet sich unter CentOS 7.x im Paket postfix-perl-scripts. Die Installation dieses Paketes gestaltet sich im gewohnten Maße sehr einfach mit Hilfe von YUM. Falls das Paket noch nicht installiert wurde, holen wir einfach diesen Schritt kurzer Hand nach.
# yum install postfix-perl-scripts -y
Was uns bei der Installation dieses Paketes alles mitgebracht wurde, zeigt uns folgender Aufruf.
# rpm -qil postfix-perl-scripts
Name : postfix-perl-scripts Epoch : 2 Version : 2.11.3 Release : 1.el7.centos Architecture: x86_64 Install Date: Thu 30 Oct 2014 01:35:54 PM CET Group : Applications/System Size : 111466 License : IBM and GPLv2+ Signature : RSA/SHA1, Thu 30 Oct 2014 01:23:00 PM CET, Key ID 60ecfb9e8195aea0 Source RPM : postfix-2.11.3-1.el7.centos.src.rpm Build Date : Thu 30 Oct 2014 12:57:01 PM CET Build Host : vml000200.dmz.nausch.org Relocations : (not relocatable) Packager : Django <django@nausch.org> Vendor : Django URL : http://www.postfix.org Summary : Postfix utilities written in perl Description : This package contains perl scripts pflogsumm and qshape. Pflogsumm is a log analyzer/summarizer for the Postfix MTA. It is designed to provide an over-view of Postfix activity. Pflogsumm generates summaries and, in some cases, detailed reports of mail server traffic volumes, rejected and bounced email, and server warnings, errors and panics. qshape prints Postfix queue domain and age distribution. /usr/sbin/pflogsumm /usr/sbin/qshape /usr/share/doc/postfix-2.11.3/pflogsumm-faq.txt /usr/share/man/man1/pflogsumm.1.gz /usr/share/man/man1/qshape.1.gz
Optionen beim Programmaufruf
Hinweise zur Konfiguration oder besser gesagt über die Optionen beim Aufruf des Programms zeigt ein Blick in die Manpage von pflogsumm.
# man pflogsumm
PFLOGSUMM(1) User Contributed Perl Documentation PFLOGSUMM(1) NAME pflogsumm.pl - Produce Postfix MTA logfile summary Copyright (C) 1998-2010 by James S. Seymour, Release 1.1.3. SYNOPSIS pflogsumm.pl -[eq] [-d <today|yesterday>] [--detail <cnt>] [--bounce_detail <cnt>] [--deferral_detail <cnt>] [-h <cnt>] [-i|--ignore_case] [--iso_date_time] [--mailq] [-m|--uucp_mung] [--no_bounce_detail] [--no_deferral_detail] [--no_no_msg_size] [--no_reject_detail] [--no_smtpd_warnings] [--problems_first] [--rej_add_from] [--reject_detail <cnt>] [--smtp_detail <cnt>] [--smtpd_stats] [--smtpd_warning_detail <cnt>] [--syslog_name=string] [-u <cnt>] [--verbose_msg_detail] [--verp_mung[=<n>]] [--zero_fill] [file1 [filen]] pflogsumm.pl -[help|version] If no file(s) specified, reads from stdin. Output is to stdout. DESCRIPTION Pflogsumm is a log analyzer/summarizer for the Postfix MTA. It is designed to provide an over-view of Postfix activity, with just enough detail to give the administrator a "heads up" for potential trouble spots. Pflogsumm generates summaries and, in some cases, detailed reports of mail server traffic volumes, rejected and bounced email, and server warnings, errors and panics. OPTIONS --bounce_detail <cnt> Limit detailed bounce reports to the top <cnt>. 0 to suppress entirely. -d today generate report for just today -d yesterday generate report for just "yesterday" --deferral_detail <cnt> Limit detailed deferral reports to the top <cnt>. 0 to suppress entirely. --detail <cnt> Sets all --*_detail, -h and -u to <cnt>. Is over-ridden by individual settings. --detail 0 suppresses *all* detail. -e extended (extreme? excessive?) detail Emit detailed reports. At present, this includes only a per-message report, sorted by sender domain, then user-in-domain, then by queue i.d. WARNING: the data built to generate this report can quickly consume very large amounts of memory if a lot of log entries are processed! -h <cnt> top <cnt> to display in host/domain reports. 0 = none. See also: "-u" and "--*_detail" options for further report-limiting options. --help Emit short usage message and bail out. (By happy coincidence, "-h" alone does much the same, being as it requires a numeric argument :-). Yeah, I know: lame.) -i --ignore_case Handle complete email address in a case-insensitive manner. Normally pflogsumm lower-cases only the host and domain parts, leaving the user part alone. This option causes the entire email address to be lower- cased. --iso_date_time For summaries that contain date or time information, use ISO 8601 standard formats (CCYY-MM-DD and HH:MM), rather than "Mon DD CCYY" and "HHMM". -m modify (mung?) UUCP-style bang-paths --uucp_mung This is for use when you have a mix of Internet-style domain addresses and UUCP-style bang-paths in the log. Upstream UUCP feeds sometimes mung Internet domain style address into bang-paths. This option can sometimes undo the "damage". For example: "somehost.dom!username@foo" (where "foo" is the next host upstream and "somehost.dom" was whence the email originated) will get converted to "foo!username@somehost.dom". This also affects the extended detail report (-e), to help ensure that by- domain-by-name sorting is more accurate. --mailq Run "mailq" command at end of report. Merely a convenience feature. (Assumes that "mailq" is in $PATH. See "$mailqCmd" variable to path thisi if desired.) --no_bounce_detail --no_deferral_detail --no_reject_detail These switches are depreciated in favour of --bounce_detail, --deferral_detail and --reject_detail, respectively. Suppresses the printing of the following detailed reports, respectively: message bounce detail (by relay) message deferral detail message reject detail See also: "-u" and "-h" for further report-limiting options. --no_no_msg_size Do not emit report on "Messages with no size data". Message size is reported only by the queue manager. The message may be delivered long-enough after the (last) qmgr log entry that the information is not in the log(s) processed by a particular run of pflogsumm.pl. This throws off "Recipients by message size" and the total for "bytes delivered." These are normally reported by pflogsumm as "Messages with no size data." --no_smtpd_warnings This switch is depreciated in favour of smtpd_warning_detail On a busy mail server, say at an ISP, SMTPD warnings can result in a rather sizeable report. This option turns reporting them off. --problems_first Emit "problems" reports (bounces, defers, warnings, etc.) before "normal" stats. --rej_add_from For those reject reports that list IP addresses or host/domain names: append the email from address to each listing. (Does not apply to "Improper use of SMTP command pipelining" report.) -q quiet - don't print headings for empty reports note: headings for warning, fatal, and "master" messages will always be printed. --reject_detail <cnt> Limit detailed smtpd reject, warn, hold and discard reports to the top <cnt>. 0 to suppress entirely. --smtp_detail <cnt> Limit detailed smtp delivery reports to the top <cnt>. 0 to suppress entirely. --smtpd_stats Generate smtpd connection statistics. The "per-day" report is not generated for single-day reports. For multiple-day reports: "per-hour" numbers are daily averages (reflected in the report heading). --smtpd_warning_detail <cnt> Limit detailed smtpd warnings reports to the top <cnt>. 0 to suppress entirely. --syslog_name=name Set syslog_name to look for for Postfix log entries. By default, pflogsumm looks for entries in logfiles with a syslog name of "postfix," the default. If you've set a non-default "syslog_name" parameter in your Postfix configuration, use this option to tell pflogsumm what that is. See the discussion about the use of this option under "NOTES," below. -u <cnt> top <cnt> to display in user reports. 0 == none. See also: "-h" and "--*_detail" options for further report-limiting options. --verbose_msg_detail For the message deferral, bounce and reject summaries: display the full "reason", rather than a truncated one. Note: this can result in quite long lines in the report. --verp_mung do "VERP" generated address (?) munging. Convert --verp_mung=2 sender addresses of the form "list-return-NN-someuser=some.dom@host.sender.dom" to "list-return-ID-someuser=some.dom@host.sender.dom" In other words: replace the numeric value with "ID". By specifying the optional "=2" (second form), the munging is more "aggressive", converting the address to something like: "list-return@host.sender.dom" Actually: specifying anything less than 2 does the "simple" munging and anything greater than 1 results in the more "aggressive" hack being applied. See "NOTES" regarding this option. --version Print program name and version and bail out. --zero_fill "Zero-fill" certain arrays so reports come out with data in columns that that might otherwise be blank. RETURN VALUE Pflogsumm doesn't return anything of interest to the shell. ERRORS Error messages are emitted to stderr. EXAMPLES Produce a report of previous day's activities: pflogsumm.pl -d yesterday /var/log/maillog A report of prior week's activities (after logs rotated): pflogsumm.pl /var/log/maillog.0 What's happened so far today: pflogsumm.pl -d today /var/log/maillog Crontab entry to generate a report of the previous day's activity at 10 minutes after midnight. 10 0 * * * /usr/local/sbin/pflogsumm -d yesterday /var/log/maillog 2>&1 |/usr/bin/mailx -s "`uname -n` daily mail stats" postmaster Crontab entry to generate a report for the prior week's activity. (This example assumes one rotates ones mail logs weekly, some time before 4:10 a.m. on Sunday.) 10 4 * * 0 /usr/local/sbin/pflogsumm /var/log/maillog.0 2>&1 |/usr/bin/mailx -s "`uname -n` weekly mail stats" postmaster The two crontab examples, above, must actually be a single line each. They're broken-up into two-or-more lines due to page formatting issues. SEE ALSO The pflogsumm FAQ: pflogsumm-faq.txt. NOTES Pflogsumm makes no attempt to catch/parse non-Postfix log entries. Unless it has "postfix/" in the log entry, it will be ignored. It's important that the logs are presented to pflogsumm in chronological order so that message sizes are available when needed. For display purposes: integer values are munged into "kilo" and "mega" notation as they exceed certain values. I chose the admittedly arbitrary boundaries of 512k and 512m as the points at which to do this--my thinking being 512x was the largest number (of digits) that most folks can comfortably grok at-a-glance. These are "computer" "k" and "m", not 1000 and 1,000,000. You can easily change all of this with some constants near the beginning of the program. "Items-per-day" reports are not generated for single-day reports. For multiple-day reports: "Items-per-hour" numbers are daily averages (reflected in the report headings). Message rejects, reject warnings, holds and discards are all reported under the "rejects" column for the Per-Hour and Per-Day traffic summaries. Verp munging may not always result in correct address and address-count reduction. Verp munging is always in a state of experimentation. The use of this option may result in inaccurate statistics with regards to the "senders" count. UUCP-style bang-path handling needs more work. Particularly if Postfix is not being run with "swap_bangpath = yes" and/or *is* being run with "append_dot_mydomain = yes", the detailed by-message report may not be sorted correctly by-domain-by-user. (Also depends on upstream MTA, I suspect.) The "percent rejected" and "percent discarded" figures are only approximations. They are calculated as follows (example is for "percent rejected"): percent rejected = (rejected / (delivered + rejected + discarded)) * 100 There are some issues with the use of --syslog_name. The problem is that, even with $syslog_name set, Postfix will sometimes still log things with "postfix" as the syslog_name. This is noted in /etc/postfix/sample-misc.cf: # Beware: a non-default syslog_name setting takes effect only # after process initialization. Some initialization errors will be # logged with the default name, especially errors while parsing # the command line and errors while accessing the Postfix main.cf # configuration file. As a consequence, pflogsumm must always look for "postfix," in logs, as well as whatever is supplied for syslog_name. Where this becomes an issue is where people are running two or more instances of Postfix, logging to the same file. In such a case: . Neither instance may use the default "postfix" syslog name and... . Log entries that fall victim to what's described in sample-misc.cf will be reported under "postfix", so that if you're running pflogsumm twice, once for each syslog_name, such log entries will show up in each report. The Pflogsumm Home Page is at: http://jimsun.LinxNet.com/postfix_contrib.html REQUIREMENTS For certain options (e.g.: --smtpd_stats), Pflogsumm requires the Date::Calc module, which can be obtained from CPAN at http://www.perl.com. Pflogsumm is currently written and tested under Perl 5.8.3. As of version 19990413-02, pflogsumm worked with Perl 5.003, but future compatibility is not guaranteed. LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You may have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. An on-line copy of the GNU General Public License can be found http://www.fsf.org/copyleft/gpl.html. 1.1.3 2010-03-20 PFLOGSUMM(1)
Mit der Option -help werden die entsprechenden Optionen ebenfalls in Kurzform angezeigt.
# pflogsumm -help
usage: pflogsumm.pl -[eq] [-d <today|yesterday>] [--detail <cnt>] [--bounce_detail <cnt>] [--deferral_detail <cnt>] [-h <cnt>] [-i|--ignore_case] [--iso_date_time] [--mailq] [-m|--uucp_mung] [--no_bounce_detail] [--no_deferral_detail] [--no_no_msg_size] [--no_reject_detail] [--no_smtpd_warnings] [--problems_first] [--rej_add_from] [--reject_detail <cnt>] [--smtp_detail <cnt>] [--smtpd_stats] [--smtpd_warning_detail <cnt>] [--syslog_name=string] [-u <cnt>] [--verbose_msg_detail] [--verp_mung[=<n>]] [--zero_fill] [file1 [filen]] pflogsumm.pl --[version|help]
manueller Programmaufruf
Wollen wir uns einen Bericht des heutigen Tages ansehen, so generieren wir diesen on-the-fly mit:
# /usr/sbin/pflogsumm -d today /var/log/maillog
Interessiert uns was gestern los war, so lautet der Aufgruf ganz einfach:
# /usr/sbin/pflogsumm -d yesterday /var/log/maillog
automatischer Programmaufruf
Für die tägliche Erstellung unserer Mailserverstatistik bemühen wir nun ganz einfach unseres cron-deamon.
Hierzu legen wir mit dem Editor unserer Wahl eine betreffende Konfigurationsdatei an, bzw. ergänzen die bereits vorhandene Datei.
# vim /etc/crontab
# Django : 2015-01-30 #täglicher Statusbericht unseres Mailservers postfix 10 0 * * * root /usr/sbin/pflogsumm -d yesterday /var/log/maillog 2>&1 | /bin/mailx -s "mx-test.dmz.nausch.org daily mail stats" postmaster@nausch.org
Täglich um 00:10 Uhr wird die Statistik des letzten Tages erstellt und mittels mailx als eMail an den postmaster verschickt. Das Paket mailx muss dazu natürlich installiert sein, falls (noch) nicht, holen wir dies kurz noch nach.
# yum install mailx -y
Date: Mon, 13 Oct 2008 00:10:03 +0200 (CEST)
From: root <root@nausch.org>
To: postmaster@nausch.org
Subject: mx-test.dmz.nausch.org daily mail stats
Postfix log summaries for Feb 01
Grand Totals
------------
messages
31 received
27 delivered
0 forwarded
0 deferred
0 bounced
339 rejected (92%)
0 reject warnings
0 held
0 discarded (0%)
353252 bytes received
353252 bytes delivered
16 senders
16 sending hosts/domains
4 recipients
1 recipient hosts/domains
Per-Hour Traffic Summary
time received delivered deferred bounced rejected
--------------------------------------------------------------------
0000-0100 0 0 0 0 0
0100-0200 0 0 0 0 0
0200-0300 0 0 0 0 0
0300-0400 0 0 0 0 0
0400-0500 1 0 0 0 3
0500-0600 1 1 0 0 24
0600-0700 0 0 0 0 10
0700-0800 0 0 0 0 2
0800-0900 4 3 0 0 7
0900-1000 3 3 0 0 18
1000-1100 3 2 0 0 18
1100-1200 2 2 0 0 29
1200-1300 2 2 0 0 25
1300-1400 2 1 0 0 20
1400-1500 0 0 0 0 37
1500-1600 0 0 0 0 22
1600-1700 0 0 0 0 24
1700-1800 2 2 0 0 31
1800-1900 3 3 0 0 12
1900-2000 3 3 0 0 10
2000-2100 4 4 0 0 19
2100-2200 1 1 0 0 2
2200-2300 0 0 0 0 19
2300-2400 0 0 0 0 7
Host/Domain Summary: Message Delivery
sent cnt bytes defers avg dly max dly host/domain
-------- ------- ------- ------- ------- -----------
872 6405k 0 2.1 s 2.4 m nausch.org
...
...
Fatal Errors: none
Panics: none
Master daemon messages
----------------------
1 reload configuration /etc/postfix
mailgraph
Eine ansprechende graphische Übersicht kann mittels Mailgraph erstellt werden. Das passende Paket mailgraph installieren wir aus dem Repository nausch.org.
Mailgraph besteht im wesentlichen aus zwei Teilen. Das eine perl-Script ist zuständig für das Durchsuchen und Analysieren des Mailserver-Logdatei. Die gewonnenen Daten werden in rrd-Datendateien geschrieben. Ein zweites Perl cgi-Script generiert dann beim Aufrufen der zugehörigen Webseite Graphiken mit den Daten der rrd-Dateien.
Installation
Mit Hilfe von yum holen wir uns als erstes das benötigte Paket auf unser System.
# yum install mailgraph -y
Den Inhalt des Paketes inspizieren wir bei Bedarf mit folgendem Aufruf.
# rpm -qil mailgraph
Name : mailgraph
Version : 1.14
Release : 1.el7.centos
Architecture: noarch
Install Date: Fri 30 Jan 2015 03:21:39 PM CET
Group : System Environment/Daemons
Size : 66890
License : GPL+
Signature : RSA/SHA1, Fri 30 Jan 2015 09:57:54 AM CET, Key ID 60ecfb9e8195aea0
Source RPM : mailgraph-1.14-1.el7.centos.src.rpm
Build Date : Fri 30 Jan 2015 09:57:42 AM CET
Build Host : vml000200.dmz.nausch.org
Relocations : (not relocatable)
Packager : Django <django@nausch.org>
URL : http://mailgraph.schweikert.ch/
Summary : A RRDtool frontend for Mail statistics
Description :
Mailgraph is a very simple mail statistics RRDtool frontend for Postfix and
Sendmail that produces daily, weekly, monthly and yearly graphs of
received/sent and bounced/rejected mail.
/etc/httpd/conf.d/mailgraph.conf
/etc/sysconfig/mailgraph
/usr/lib/systemd/system/mailgraph.service
/usr/sbin/mailgraph
/usr/share/doc/mailgraph-1.14
/usr/share/doc/mailgraph-1.14/CHANGES
/usr/share/doc/mailgraph-1.14/COPYING
/usr/share/doc/mailgraph-1.14/README
/usr/share/mailgraph
/usr/share/mailgraph/mailgraph.cgi
/usr/share/mailgraph/mailgraph.css
/var/cache/mailgraph
/var/lib/mailgraph
Konfiguration
Die Konfiguration von mailgraph selbst gestaltet sich sehr einfach. Über die Konfigurationsdatei /etc/sysconfig/mailgraph können wir angeben, wo das Logfile unseres Mailservers zu finden ist. Unter CentOS ist dies /var/log/maillog. Mit der Option –ignore-localhost können wir mailgraph anweisen, Nachrichten von localhost nicht in die Statistik aufzunehmen, die Mails also nicht „doppelt“ zu zählen, wenn z.B. Postfix _und_ AMaViS auf dem gleichen Host laufen.
# vim /etc/sysconfig/mailgraph
- /etc/sysconfig/mailgraph
MAILLOG=/var/log/maillog PRIORITY=-19 OPTIONS=--ignore-localhost
Will man die Sprache bei der Webseite, oder die Farben der Grafiken anpassen, schreibt man einfach seine Änderungen direkt in das übersichtliche CGI-Script.
# cat /usr/share/mailgraph/mailgraph.cgi
- /usr/share/mailgraph/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.14"; my $host = (POSIX::uname())[1]; my $scriptname = 'mailgraph.cgi'; my $xpoints = 540; my $points_per_sample = 3; my $ypoints = 160; my $ypoints_err = 96; my $rrd = '/var/lib/mailgraph/mailgraph.rrd'; # path to where the RRD database is my $rrd_virus = '/var/lib/mailgraph/mailgraph_virus.rrd'; # path to where the Virus RRD database is my $tmp_dir = '/var/cache/mailgraph'; # temporary directory where to store the images my @graphs = ( { title => 'Last Day', seconds => 3600*24, }, { title => 'Last Week', seconds => 3600*24*7, }, { title => 'Last Month', seconds => 3600*24*31, }, { title => 'Last Year', seconds => 3600*24*365, }, ); my %color = ( sent => '000099', # rrggbb in hex received => '009900', rejected => 'AA0000', bounced => '000000', virus => 'DDBB00', spam => '999999', ); sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; # choose carefully the end otherwise rrd will maybe pick the wrong RRA: my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %8.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %4.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received", 'GPRINT:srecv:MAX:total\: %8.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %4.0lf msgs/min\l', ); } sub graph_err($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints_err, "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "AREA:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %8.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %4.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "STACK:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %8.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %4.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %8.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %4.0lf msgs/min\l', "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "LINE2:rrejected#$color{rejected}:Rejected", 'GPRINT:srejected:MAX:total\: %8.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %5.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %4.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mail statistics for $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mail statistics for $host</h1>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph\"/><br/>\n"; print "<img src=\"$scriptname?${n}-e\" alt=\"mailgraph\"/></p>\n"; } print <<FOOTER; <hr/> <table><tr><td> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a> $VERSION by <a href="http://david.schweikert.ch/">David Schweikert</a></td> <td align="right"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="http://oss.oetiker.ch/rrdtool/.pics/rrdtool.gif" alt="" width="120" height="34"/></a> </td></tr></table> </body></html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_err($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
Apache VHost anlegen
Damit wir bequem von unserem Browser aus, die aktuellen Graphiken abfragen können, bearbeiten wir entweder die aus dem RPM stammende Konfigurationsdatei oder legen wir nun einen passenden VHost an.
# vim /etc/httpd/conf.d/vhosts.conf
- /etc/httpd/conf.d/vhosts.conf
# # mailgraph.nausch.org # <VirtualHost *:80> ServerAdmin webmaster@nausch.org ServerName mailgraph.nausch.org ServerAlias www.mailgraph.nausch.org ServerPath / DocumentRoot "/usr/share/mailgraph" AddHandler cgi-script .cgi <Directory "/usr/share/mailgraph"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi Order deny,allow require IP 10.0. </Directory> ErrorLog logs/mailgraph_error.log CustomLog logs/mailgraph_access.log combined </VirtualHost>
Bevor wir bei unserem Webserver eine Reload der Konfiguration vornehmen, testen wir unsere neue Konfigurationsdatei auf syntaktische Fehler.
# apachectl -t
Syntax OK
Da keine Fehler aufgetreten sind, aktivieren wir die neue Konfiguration durch einen Reload des Webserver-Daemon.
# systemctl reload postfix
NGiNX VHost anlegen
Nutzen wir als Webserver NGiNX können wir auch hier schnell und einfach einen passenden vHOST anlegen.
# vim /etc/nginx/conf.d/vhosts.conf
- /etc/nginx/conf.d/vhosts.conf
server { listen 80; server_name mailgraph.nausch.org; access_log /var/log/nginx/mailgraph_access.log; error_log /var/log/nginx/mailgraph_errors.log; root /usr/share/mailgraph/; index mailgraph.cgi; location ~ \.php { fastcgi_split_path_info ^(.+\.cgi)(/.+)$; fastcgi_index mailgraph.cgi; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
Den Parameter fastcgi_pass setzen wir im übrigen auf den Wert aus der Konfigurationsdatei /etc/php-fpm.d/www.conf des PHP FastCGI Process Manager-Daemon php-fpm.
Haben wir die Konfigurationsdatei vervollständigt, prüfen wir diese noch auf syntaktische Fehler.
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Somit können wir unsere Konfiguration nun noch aktivieren.
# systemctl reload nginx
Programmaufruf
erster manueller Start des Dämon
Damit das Mail-Logfile forlaufend ausgelesen wird, starten wir nun noch den Dämon mit Hilfe des mitgelieferten systemd-Start-Scriptes /usr/lib/systemd/system/mailgraph.service.
# systemctl start mailgraph
Im syslog wurde der Start des Daemon entsprechend dokumentiert.
# tail -n2 /var/log/messages
Feb 2 21:17:36 vml000097 systemd: Starting mailgraph mail log file analyzer... Feb 2 21:17:37 vml000097 systemd: Started mailgraph mail log file analyzer.
Ebenso kann man den Status des Webservers mit Hilfe des Befehls systemctl abfragen.
# systemctl status mailgraph
mailgraph.service - mailgraph mail log file analyzer Loaded: loaded (/usr/lib/systemd/system/mailgraph.service; disabled) Active: active (running) since Mon 2015-02-02 21:17:37 CET; 2min 48s ago Process: 2362 ExecStart=/usr/sbin/mailgraph -d -l $MAILLOG --daemon-rrd=/var/lib/mailgraph $OPTIONS (code=exited, status=0/SUCCESS) Main PID: 2367 (mailgraph) CGroup: /system.slice/mailgraph.service └─2367 /usr/bin/perl -w /usr/sbin/mailgraph -d -l /var/log/maillog --daemon-rrd=/var/lib/mail... Feb 02 21:17:37 vml000097.dmz.nausch.org systemd[1]: Started mailgraph mail log file analyzer.
automatischer Start beim Systemstart
Wollen wir den Daemon beim Hochfahren des Systems automatisch starten, greifen wir auf den Befehl systemctl zurück.
# systemctl enable mailgraph.service
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
Möchten wir uns vergewissern, ob der Daemon beim Systemstart gestartet wird oder nicht, erfahren wir ebenfalls mit dem Befehl systemctl.
# systemctl is-enabled mailgraph.service
enabled
Startet der Server nicht automatisch, wird uns ein „disabled“ zurückgemeldet.
Webaufruf
Über unseren vHOST erhalten wir nun optisch schön ansprechende Übersichten über den Mailverkehr unseres MX.
queuegraph
Möchte man einen graphischen Überblick über die Queues haben, so liefert uns das gerade vorgestellte und installierte Mailgraph leider keine grafischen werte. Hierzu greifen wir auf das Programm Queuegraph von Ralf Hildebrandt zurück.
Das passende Programmpaket queuegraph installieren am einfachsten aus dem Repository nausch.org.
Installation
Mit Hilfe von yum holen wir uns als erstes das benötigte Paket auf unser System.
# yum install queuegraph -y
Den Inhalt des Paketes inspizieren wir bei Bedarf mit folgendem Aufruf.
# rpm -qil queuegraph
Name : queuegraph
Version : 1.1
Release : 1.el7.centos
Architecture: noarch
Install Date: Mon 02 Feb 2015 09:29:25 PM CET
Group : System Environment/Daemons
Size : 6271
License : GPL+
Signature : RSA/SHA1, Fri 30 Jan 2015 09:21:58 PM CET, Key ID 60ecfb9e8195aea0
Source RPM : queuegraph-1.1-1.el7.centos.src.rpm
Build Date : Fri 30 Jan 2015 09:21:46 PM CET
Build Host : vml000200.dmz.nausch.org
Relocations : (not relocatable)
Packager : Django <django@nausch.org>
URL : http://www.arschkrebs.de/postfix/queuegraph/
Summary : A RRDtool frontend for Mail statistics
Description :
Queuegraph is a very simple mail statistics RRDtool frontend for Postfix that
produces daily, weekly, monthly and yearly graphs of Postfix's active,
deferred, incoming and bounce queues.
/etc/cron.d/queuegraph
/etc/httpd/conf.d/queuegraph.conf
/usr/sbin/queuegraph-rrd.sh
/usr/share/doc/queuegraph-1.1
/usr/share/doc/queuegraph-1.1/README
/usr/share/queuegraph
/usr/share/queuegraph/queuegraph.cgi
/var/cache/queuegraph
/var/lib/queuegraph
Konfiguration
Die Konfiguration von queuegraph selbst gestaltet sich sehr einfach, da es gar nichts großartrig zu konfigurieren gibt!
Will man die Sprache bei der Webseite, oder die Farben anpassen, so nimmt man die Änderungen direkt im CGI-Script vor.
# vim /usr/share/queuegraph/queuegraph.cgi
- /usr/share/queuegraph/queuegraph.cgi
#!/usr/bin/perl -w # queuegraph -- a postfix queue statistics rrdtool frontend # based on mailgraph, which is # copyright (c) 2000-2002 David Schweikert <dws@ee.ethz.ch> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.1"; my $host = (POSIX::uname())[1]; my $scriptname = 'queuegraph.cgi'; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $ypoints_err = 80; my $rrd = '/var/lib/queuegraph/mailqueues.rrd'; # path to where the RRD database is my $tmp_dir = '/var/cache/queuegraph'; # temporary directory where to store the images my $rrdtool_1_0 = ($RRDs::VERSION < 1.199908); my @graphs = ( { title => 'Day Graph', seconds => 3600*24, }, { title => 'Week Graph', seconds => 3600*24*7, }, { title => 'Month Graph', seconds => 3600*24*31, }, { title => 'Year Graph', seconds => 3600*24*365, }, ); my %color = ( sent => '000099', # rrggbb in hex received => '00FF00', rejected => '999999', bounced => '993399', virus => 'FFFF00', spam => 'FF0000', ); sub graph($$$) { my $range = shift; my $file = shift; my $title = shift; my $step = $range*$points_per_sample/$xpoints; my $date = localtime(time); $date =~ s|:|\\:|g unless $rrdtool_1_0; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', "-".int($range*0.01), '--vertical-label', 'queuefiles', '--title', $title, '--lazy', $rrdtool_1_0 ? () : ( '--slope-mode' ), "DEF:active=$rrd:active:AVERAGE", "DEF:deferred=$rrd:deferred:AVERAGE", 'LINE2:active#00ff00:Active+Incoming+Maildrop\:', 'GPRINT:active:MAX:Maximum\: %0.0lf ', 'GPRINT:active:AVERAGE:Average\: %0.0lf/min\n', 'LINE1:deferred#0000ff:Deferred\:', 'GPRINT:deferred:MAX:Maximum\: %0.0lf ', 'GPRINT:deferred:AVERAGE:Average\: %0.0lf/min\l', 'HRULE:0#000000', 'COMMENT:\n', 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <HTML> <HEAD> <TITLE>Queue Statistics for $host</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> HEADER print "<H1>Postfix Queue Statistics for $host</H1>\n"; for my $n (0..$#graphs) { print "<H2>$graphs[$n]{title}</H2>\n"; print "<P><IMG BORDER=\"0\" SRC=\"$scriptname/queuegraph_${n}.png\" ALT=\"queuegraph\">\n"; } print <<FOOTER; <table border="0" width="400"><tr><td align="left"> <A href="http://www.arschkrebs.de/postfix/queuegraph">queuegraph</A> $VERSION by <A href="http://www.arschkrebs.de/">Ralf Hildebrandt</A>, based on <A href="http://mailgraph.schweikert.ch/">mailgraph</A> by <A href="http://david.schweikert.ch/">David Schweikert</A></td> <td ALIGN="right"> <a HREF="http://oss.oetiker.ch/rrdtool/"><img border="0" src="http://tobi.oetiker.ch/webtools/rrdtool/.pics/rrdtool.gif" alt="rrdtool" width="120" height="34"></a> </td></tr></table> </BODY> FOOTER } sub send_image($) { my $file = shift; -r $file or do { print "Content-Type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-Type: image/png\n"; print "Content-Length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read IMG, $data, 1; } sub main() { if($ENV{PATH_INFO}) { my $uri = $ENV{REQUEST_URI}; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/\~/tilde,/g; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $file = "$tmp_dir/$uri$ENV{PATH_INFO}"; if($ENV{PATH_INFO} =~ /^\/queuegraph_(\d+)\.png$/) { graph($graphs[$1]{seconds}, $file, $graphs[$1]{title}); } else { print "Content-Type: text/plain\n\nERROR: unknown image $ENV{PATH_INFO}\n"; exit 1; } send_image($file); } else { print_html; } } main;
Optionen beim Programmaufruf
Hinweise zur Konfiguration oder besser gesagt über die Optionen beim Aufruf des Programms zeigt ein Blick in die Datei /usr/share/doc/queuegraph-1.1/README.
# cat /usr/share/doc/queuegraph-1.1/README
- /usr/share/doc/queuegraph-1.1/README
README for queuegraph Dependencies: rrdtools, librrds-perl To install, adjust the path to your rrdtools binaries in queuegraph-rrd.sh Create a cronjob that runs queuegraph-rrd.sh every minute -- this populates the *.rrd database: * * * * * /usr/local/bin/queuegraph-rrd.sh Now put queuegraph.cgi into the cgi-bin directory of your webserver. chmod 755 queuegraph.cgi
Apache VHost anlegen
Damit wir bequem von unserem Browser aus, die aktuellen Graphiken abfragen können, bearbeiten wir entweder die aus dem RPM stammende Konfigurationsdatei oder legen wir nun einen passenden VHost an.
# vim /etc/httpd/conf.d/vhosts.conf
- /etc/httpd/conf.d/vhosts.conf
# # queuegraph.nausch.org # <VirtualHost *:80> ServerAdmin webmaster@nausch.org ServerName queue.nausch.org ServerAlias www.queue.nausch.org ServerPath / DocumentRoot "/usr/share/queuegraph" AddHandler cgi-script .cgi <Directory "/usr/share/queuegraph"> AllowOverride None Options +ExecCGI DirectoryIndex queuegraph.cgi Order deny,allow require IP 10.0. </Directory> ErrorLog logs/queuegraph_error.log CustomLog logs/queuegraph_access.log combined </VirtualHost>
Bevor wir bei unserem Webserver eine Reload der Konfiguration vornehmen, testen wir unsere neue Konfigurationsdatei auf syntaktische Fehler.
# apachectl -t
Syntax OK
Da keine Fehler aufgetreten sind, aktivieren wir die neue Konfiguration durch einen Reload des Webserver-Daemon.
# systemctl reload postfix
NGiNX VHost anlegen
Nutzen wir als Webserver NGiNX können wir auch hier schnell und einfach einen passenden vHOST anlegen.
# vim /etc/nginx/conf.d/vhosts.conf
- /etc/nginx/conf.d/vhosts.conf
server { listen 80; server_name queuegraph.nausch.org; access_log /var/log/nginx/queuegraph_access.log; error_log /var/log/nginx/queuegraph_errors.log; root /usr/share/queuegraph/; index queuegraph.cgi; location ~ \.php { fastcgi_split_path_info ^(.+\.cgi)(/.+)$; fastcgi_index queuegraph.cgi; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
Den Parameter fastcgi_pass setzen wir im übrigen auf den Wert aus der Konfigurationsdatei /etc/php-fpm.d/www.conf des PHP FastCGI Process Manager-Daemon php-fpm.
Haben wir die Konfigurationsdatei vervollständigt, prüfen wir diese noch auf syntaktische Fehler.
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Somit können wir unsere Konfiguration nun noch aktivieren.
# systemctl reload nginx
Programmaufruf
Die automatische Befüllung der rrd-files übernimmt ein cronjob, der jede Minute das Script queuegraph-rrd.sh startet.
# cat /etc/cron.d/queuegraph
- /etc/cron.d/queuegraph
# Runs the queuegraph update program # # This will run every one minute * * * * * root /usr/sbin/queuegraph-rrd.sh &> /dev/null
# less /usr/sbin/queuegraph-rrd.sh
- /usr/sbin/queuegraph-rrd.sh
#!/bin/sh # output the number of messages in the incoming, active, and deferred # queues of postfix one per line suitable for use with snmpd/cricket/rrdtool # # 2003/01/24 Mike Saunders <method at method DOT cx> # mailqsize was originally written by Vivek Khera. All I did was # make it update an rrd. # 2003/04/14 Ralf Hildebrandt <ralf.hildebrandt at charite DOT de> # I bundled this with a modified mailgraph # 2007/07/28 Ralf Hildebrandt <ralf.hildebrandt at charite DOT de> # find rrdtool using "which" # change this to the location of rrdtool RRDTOOL=`which rrdtool` # change this to the location you want to store the rrd RRDFILE=/var/lib/queuegraph/mailqueues.rrd if test ! -x $RRDTOOL ; then echo "ERROR: $RRDTOOL does not exist or is not executable" exit fi if test ! -f $RRDFILE ; then echo "Creating RRD file $RRDFILE" $RRDTOOL create $RRDFILE --step 60 \ DS:active:GAUGE:900:0:U \ DS:deferred:GAUGE:900:0:U \ RRA:AVERAGE:0.5:1:1440 \ RRA:AVERAGE:0.5:30:2016 \ RRA:AVERAGE:0.5:60:105120 \ RRA:MAX:0.5:1:1440 \ RRA:MAX:0.5:30:2016 \ RRA:MAX:0.5:60:105120 fi #set -x qdir=`/usr/sbin/postconf -h queue_directory` active=`find $qdir/incoming $qdir/active $qdir/maildrop -type f -print | wc -l | awk '{print $1}'` deferred=`find $qdir/deferred -type f -print | wc -l | awk '{print $1}'` #printf "active: %d\ndeferred: %d\n" $active $deferred $RRDTOOL update $RRDFILE "N:$active:$deferred"
Webaufruf
Über unseren Vhost erhalten wir nun optisch schön ansprechende Übersichten über die Mail-Queues unseres MX.
Mailgraph NextGeneration
Wem all die vorgenannten graphischen Aufbereitungen noch nicht ausführlich genug sind, dem bietet der Fork von David Schweikert's Mailgraph zusätzliche Übersichten:
- Mail Ein und -Ausgang
- geblockte Nachrichten
- Greylisting Übersicht
- Greylisting Detailansicht
- Postscreen Übersicht
- Postscreen Detailansicht
- Übersicht Mail-Queues
- DANE / TLSA Verbindungen
- Sender policy Framework - SPF-Prüfungen
- DomainKeys Identified Mail - DKIM-Prüfungen
- Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen
Das Ganze ist aber zugegebener Maßen keine großartige Neuprogrammierung, sondern setzt auf bekannte Programm(pakete) auf.
Besonderer Dank geht daher an:
- David Schweikert für das Programm Mailgraph
- Ralf Hildebrandt für das Programm Queuegraph
- Markus Neubauer für das Programm Greygraph und zu guter Letzt an
- Sebastian van de Meer für die beiden Mailgraph-Patche mailgraph Graphen um SPF, DMARC und DKIM erweitern und mailgraph Graphen um DANE erweitern
Doch genug der Vorrede, einen Blick auf die Ausgabe des Webfrontends von Mailgraph-ng sagt mehr als 1.000 Worte. Die Ausgabe zeigt exemplarisch alle möglichen Graphen an, die auf einem Testsystem generiert wurden.
Im Realbetrieb wird man entweder Greylisting oder Postscreen einsetzen, somit werden sicher zwei der gezeigten Graphen wegfallen. Wie das geht, wird hier gezeigt.
Installation
Bei der Installation der NextGeneration Version von Mailgraph gibt es mehrere Möglichkeiten.
- RPM-basiert
- Update mit Hilfe eines tar.gz-Archives
- manuelle Installation/Update
Auf die einzelnen Installationsarten gehen wir nun entsprechend ein.
RPM-Installation
Die wohl einfachste Art ist die der RPM-basierten Installation. Das passende Paket mailgraph installieren wir aus dem Repository nausch.org. Aktuell wird vom Paketmaintainer die Version 1.15 bereitgestellt. Haben wir das Repository nausch.org installiert, reicht der folgende Aufruf.
# yum install mailgraph
Das abhängige Paket Queuegraph von Ralf Hildebrandt, welches zum Erstellen und Aktualisieren des Queuegraph rrd-Datenbankdatei verwendet wird, wird automatisch mit installiert.
Will man die beiden Pakete „nur lokal“ installieren verwendet man folgenden Aufruf.
# yum localinstall http://repo.nausch.org/7/x86_64/mailgraph-1.15.2-1.el7.centos.noarch.rpm \ http://repo.nausch.org/7/x86_64/queuegraph-1.1-1.el7.centos.noarch.rpm
manuelle Installation
Hat man bereits Mailgraph in einer früheren Version (manuell) installiert und möchte diese Installation erweitern, so kann man natürlich Mailgraph auch manuell installieren.
Hierzu laden wir uns erst einmal das betreffende Paket auf unseren Server. Zuerst wechseln wir in unseren lokalen Paketspeicher.
# cd /usr/local/src/packages
# wget http://repository.nausch.org/public/var/mailgraph-1.15.2.tar.gz
Anschließend entpacken wir das Archiv.
# tar xzfv mailgraph-1.15.2.tar.gz -C /tmp/
Dann erstellen wir, falls noch nicht im System vorhanden, die einzelnen Zielordner.
- RRD-Speicherverzeichnis
# mkdir -p /var/lib/mailgraph
- Cachingverzeichnis für die Graphen
mkdir -p /var/cache/mailgraph
- WEB Root Verzeichnis für die Webseite
mkdir -p /usr/share/mailgraph
Anschließend passen wir noch die Gruppenrechte an dem Ordner an.
# chgrp apache /usr/share/mailgraph
# chmod g+w /usr/share/mailgraph
Nun kopieren wir die Dateien aus unserem temporären Verzeichnis an die richtige Stelle im System.
# cp /tmp/mailgraph-1.15.2/mailgraph.c* /usr/share/mailgraph/ -y
# cp /tmp/mailgraph-1.15.2/rrdtool-3dlogo.png /usr/share/mailgraph/ -y
Das Script zur Datengenerierung kopieren wir dann noch in das Verzeichnis /usr/sbin/. Vorher halten wir ggf. einen bereits laufenden mailgraph-Daemon an!
# systemctl stop mailgraph
# cp /tmp/mailgraph-1.15.2/mailgraph /usr/sbin/ -y
Konfiguration
/etc/sysconfig/mailgraph
Die Konfiguration von mailgraph selbst gestaltet sich sehr einfach. Über die Konfigurationsdatei /etc/sysconfig/mailgraph können wir angeben, wo das Logfile unseres Mailservers zu finden ist. Unter CentOS ist dies /var/log/maillog. Mit der Option –ignore-localhost können wir mailgraph anweisen, Nachrichten von localhost nicht in die Statistik aufzunehmen, die Mails also nicht „doppelt“ zu zählen, wenn z.B. Postfix _und_ AMaViS auf dem gleichen Host laufen.
# vim /etc/sysconfig/mailgraph
- /etc/sysconfig/mailgraph
MAILLOG=/var/log/maillog PRIORITY=-19 OPTIONS=--ignore-localhost
/usr/share/mailgraph/mailgraph.cgi
Will man die Sprache bei der Webseite, oder die Farben der Grafiken anpassen, schreibt man einfach seine Änderungen direkt in das übersichtliche CGI-Script.
# vim /usr/share/mailgraph/mailgraph.cgi
- /usr/share/mailgraph/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd = '/var/lib/mailgraph/mailgraph.rrd'; # path to where the Mailgraph RRD database is my $rrd_virus = '/var/lib/mailgraph/mailgraph_virus.rrd'; # path to where the Virus RRD database is my $rrd_grey = '/var/lib/mailgraph/mailgraph_grey.rrd'; # path to where the Greygraph RRD database is my $rrd_dane = '/var/lib/mailgraph/mailgraph_dane.rrd'; # path to where the DANE RRD database is my $rrd_dmarc = '/var/lib/mailgraph/mailgraph_dmarc.rrd'; # path to where the DMARC RRD database is my $rrd_queue = '/var/lib/mailgraph/mailqueues.rrd'; # path to where the Mailqueue RRD database is my $rrd_post = '/var/lib/mailgraph/mailgraph_post.rrd'; # path to where the Postscreen RRD database is my $tmp_dir = '/var/cache/mailgraph'; # temporary directory where to store the images my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'C33333', # psbl => 'EBBAD5', passold => 'C39233', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => 'C3D533', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', anonymoustls => '000099', trustedtls => '009900', untrustedtls => 'AA0000', verifiedtls => '000000', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "AREA:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "STACK:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "LINE2:rrejected#$color{rejected}:Rejected ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new=$rrd_grey:new:AVERAGE", "DEF:mnew=$rrd_grey:new:MAX", "CDEF:rnew=new,60,*", "CDEF:rmnew=mnew,60,*", "CDEF:dnew=new,UN,0,new,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok=$rrd_grey:reconnectok:AVERAGE", "DEF:mreconnectok=$rrd_grey:reconnectok:MAX", "CDEF:rreconnectok=reconnectok,60,*", "CDEF:dreconnectok=reconnectok,UN,0,reconnectok,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted=$rrd_grey:greylisted:AVERAGE", "DEF:mgreylisted=$rrd_grey:greylisted:MAX", "CDEF:rgreylisted=greylisted,60,*", "CDEF:rmgreylisted=mgreylisted,60,*", "CDEF:dgreylisted=greylisted,UN,0,greylisted,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed=$rrd_grey:delayed:AVERAGE", "DEF:mdelayed=$rrd_grey:delayed:MAX", "CDEF:rdelayed=delayed,60,*", "CDEF:rmdelayed=mdelayed,60,*", "CDEF:ddelayed=delayed,UN,0,delayed,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist=$rrd_grey:whitelist:AVERAGE", "DEF:mwhitelist=$rrd_grey:whitelist:MAX", "CDEF:rwhitelist=whitelist,60,*", "CDEF:rmwhitelist=mwhitelist,60,*", "CDEF:dwhitelist=whitelist,UN,0,whitelist,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl=$rrd_grey:awl:AVERAGE", "DEF:mawl=$rrd_grey:awl:MAX", "CDEF:rawl=awl,60,*", "CDEF:dawl=awl,UN,0,awl,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early=$rrd_grey:early:AVERAGE", "DEF:mearly=$rrd_grey:early:MAX", "CDEF:rearly=early,60,*", "CDEF:rmearly=mearly,60,*", "CDEF:dearly=early,UN,0,early,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl=$rrd_post:pswl:AVERAGE", "DEF:mpswl=$rrd_post:pswl:MAX", "CDEF:rpswl=pswl,60,*", "CDEF:rmpswl=mpswl,60,*", "CDEF:dpswl=pswl,UN,0,pswl,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold=$rrd_post:passold:AVERAGE", "DEF:mpassold=$rrd_post:passold:MAX", "CDEF:rpassold=passold,60,*", "CDEF:dpassold=passold,UN,0,passold,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold,60,*", "LINE2:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew=$rrd_post:passnew:AVERAGE", "DEF:mpassnew=$rrd_post:passnew:MAX", "CDEF:rpassnew=passnew,60,*", "CDEF:dpassnew=passnew,UN,0,passnew,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl=$rrd_post:psbl:AVERAGE", "DEF:mpsbl=$rrd_post:psbl:MAX", "CDEF:rpsbl=psbl,60,*", "CDEF:dpsbl=psbl,UN,0,psbl,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto=$rrd_post:veto:AVERAGE", "DEF:mveto=$rrd_post:veto:MAX", "CDEF:rveto=veto,60,*", "CDEF:dveto=veto,UN,0,veto,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet=$rrd_post:pregreet:AVERAGE", "DEF:mpregreet=$rrd_post:pregreet:MAX", "CDEF:rpregreet=pregreet,60,*", "CDEF:dpregreet=pregreet,UN,0,pregreet,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl=$rrd_post:dnsbl:AVERAGE", "DEF:mdnsbl=$rrd_post:dnsbl:MAX", "CDEF:rdnsbl=dnsbl,60,*", "CDEF:ddnsbl=dnsbl,UN,0,dnsbl,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining=$rrd_post:pipelining:AVERAGE", "DEF:mpipelining=$rrd_post:pipelining:MAX", "CDEF:rpipelining=pipelining,60,*", "CDEF:dpipelining=pipelining,UN,0,pipelining,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp=$rrd_post:nonsmtp:AVERAGE", "DEF:mnonsmtp=$rrd_post:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp,60,*", "CDEF:dnonsmtp=nonsmtp,UN,0,nonsmtp,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline=$rrd_post:barenewline:AVERAGE", "DEF:mbarenewline=$rrd_post:barenewline:MAX", "CDEF:rbarenewline=barenewline,60,*", "CDEF:dbarenewline=barenewline,UN,0,barenewline,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command=$rrd_post:command:AVERAGE", "DEF:mcommand=$rrd_post:command:MAX", "CDEF:rcommand=command,60,*", "CDEF:dcommand=command,UN,0,command,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup=$rrd_post:hangup:AVERAGE", "DEF:mhangup=$rrd_post:hangup:MAX", "CDEF:rhangup=hangup,60,*", "CDEF:dhangup=hangup,UN,0,hangup,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:anonymoustls=$rrd_dane:anonymoustls:AVERAGE", "DEF:manonymoustls=$rrd_dane:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls,60,*", "CDEF:danonymoustls=anonymoustls,UN,0,anonymoustls,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls,60,*", "AREA:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls=$rrd_dane:trustedtls:AVERAGE", "DEF:mtrustedtls=$rrd_dane:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls,60,*", "CDEF:dtrustedtls=trustedtls,UN,0,trustedtls,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:untrustedtls=$rrd_dane:untrustedtls:AVERAGE", "DEF:muntrustedtls=$rrd_dane:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls,60,*", "CDEF:duntrustedtls=untrustedtls,UN,0,untrustedtls,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls,60,*", "STACK:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls=$rrd_dane:verifiedtls:AVERAGE", "DEF:mverifiedtls=$rrd_dane:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls,60,*", "CDEF:dverifiedtls=verifiedtls,UN,0,verifiedtls,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass=$rrd_dmarc:spfpass:AVERAGE", "DEF:mspfpass=$rrd_dmarc:spfpass:MAX", "CDEF:rspfpass=spfpass,60,*", "CDEF:dspfpass=spfpass,UN,0,spfpass,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone=$rrd_dmarc:spfnone:AVERAGE", "DEF:mspfnone=$rrd_dmarc:spfnone:MAX", "CDEF:rspfnone=spfnone,60,*", "CDEF:dspfnone=spfnone,UN,0,spfnone,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail=$rrd_dmarc:spffail:AVERAGE", "DEF:mspffail=$rrd_dmarc:spffail:MAX", "CDEF:rspffail=spffail,60,*", "CDEF:dspffail=spffail,UN,0,spffail,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass=$rrd_dmarc:dkimpass:AVERAGE", "DEF:mdkimpass=$rrd_dmarc:dkimpass:MAX", "CDEF:rdkimpass=dkimpass,60,*", "CDEF:ddkimpass=dkimpass,UN,0,dkimpass,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone=$rrd_dmarc:dkimnone:AVERAGE", "DEF:mdkimnone=$rrd_dmarc:dkimnone:MAX", "CDEF:rdkimnone=dkimnone,60,*", "CDEF:ddkimnone=dkimnone,UN,0,dkimnone,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail=$rrd_dmarc:dkimfail:AVERAGE", "DEF:mdkimfail=$rrd_dmarc:dkimfail:MAX", "CDEF:rdkimfail=dkimfail,60,*", "CDEF:ddkimfail=dkimfail,UN,0,dkimfail,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass=$rrd_dmarc:dmarcpass:AVERAGE", "DEF:mdmarcpass=$rrd_dmarc:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass,60,*", "CDEF:ddmarcpass=dmarcpass,UN,0,dmarcpass,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone=$rrd_dmarc:dmarcnone:AVERAGE", "DEF:mdmarcnone=$rrd_dmarc:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone,60,*", "CDEF:ddmarcnone=dmarcnone,UN,0,dmarcnone,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail=$rrd_dmarc:dmarcfail:AVERAGE", "DEF:mdmarcfail=$rrd_dmarc:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail,60,*", "CDEF:ddmarcfail=dmarcfail,UN,0,dmarcfail,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred=$rrd_queue:deferred:AVERAGE", "AREA:deferred#$color{deferred}:Deferred ", 'GPRINT:deferred:MAX:total\: %15.0lf msgs', 'GPRINT:deferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:deferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active=$rrd_queue:active:AVERAGE", "LINE2:active#$color{active}:Active+Incoming+Maildrop", 'GPRINT:active:MAX:total\: %15.0lf msgs', 'GPRINT:active:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:active:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mail statistics for $host</h1>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen Übersicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>DANE / TLSA Verbindungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="97" height="43" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
/usr/share/mailgraph/mailgraph.css
Änderungen am Aussehen im Punkto Schriften und Farben der Webseite können auch mit Hilfe der CSS-Datei vorgenommen werden.
# vim /usr/share/mailgraph/mailgraph.css
- /usr/share/mailgraph/mailgraph.css
* { margin: 0; padding: 0 } body { width: 900px; background-color: white; font-family: sans-serif; font-size: 12pt; margin: 5px } h1 { margin-top: 20px; margin-bottom: 30px; text-align: center } h2 { background-color: #ddd; padding: 2px 0 2px 4px } hr { height: 1px; border: 0; border-top: 1px solid #aaa } table { border: 0px; width: 100% } img { border: 0 } a { text-decoration: none; color: #00e } a:hover { text-decoration: underline; } #jump { margin: 0 0 10px 4px } #jump li { list-style: none; display: inline; font-size: 90%; } #jump li:after { content: "|"; } #jump li:last-child:after { content: ""; }
/usr/sbin/mailgraph
Der Vollständigkeit halber ist nachfolgend das Perl-Script zum erstellen und Befüllen der RRD-Datenbankfiles aufgeführt. Eine Konfiguration des scriptes ist aber nicht vorgesehen und notwendig!
# less /usr/sbin/mailgraph
- /usr/sbin/mailgraph
#!/usr/bin/perl -w # mailgraph -- an rrdtool frontend for mail statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # copyright (c) 2011 Markus Neubauer <neubauer@std-service.com> # copyright (c) 2014-2015 Django <django@nausch.org> # released under the GNU General Public License # with spf-, dkim-, dmarc, and dane-patch Sebastian van de Meer <kernel-error@kernel-error.de> ######## Parse::Syslog 1.09 (automatically embedded) ######## package Parse::Syslog; use Carp; use Symbol; use Time::Local; use IO::File; use strict; use vars qw($VERSION); my %months_map = ( 'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' =>10, 'Dec' =>11, 'jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3, 'may' => 4, 'jun' => 5, 'jul' => 6, 'aug' => 7, 'sep' => 8, 'oct' => 9, 'nov' =>10, 'dec' =>11, ); sub is_dst_switch($$$) { my ($self, $t, $time) = @_; # calculate the time in one hour and see if the difference is 3600 seconds. # if not, we are in a dst-switch hour # note that right now we only support 1-hour dst offsets # cache the result if(defined $self->{is_dst_switch_last_hour} and $self->{is_dst_switch_last_hour} == $t->[3]<<5+$t->[2]) { return @{$self->{is_dst_switch_result}}; } # calculate a number out of the day and hour to identify the hour $self->{is_dst_switch_last_hour} = $t->[3]<<5+$t->[2]; # calculating hour+1 (below) is a problem if the hour is 23. as far as I # know, nobody does the DST switch at this time, so just assume it isn't # DST switch if the hour is 23. if($t->[2]==23) { @{$self->{is_dst_switch_result}} = (0, undef); return @{$self->{is_dst_switch_result}}; } # let's see the timestamp in one hour # 0: sec, 1: min, 2: h, 3: day, 4: month, 5: year my $time_plus_1h = timelocal($t->[0], $t->[1], $t->[2]+1, $t->[3], $t->[4], $t->[5]); if($time_plus_1h - $time > 4000) { @{$self->{is_dst_switch_result}} = (3600, $time-$time%3600+3600); } else { @{$self->{is_dst_switch_result}} = (0, undef); } return @{$self->{is_dst_switch_result}}; } # fast timelocal, cache minute's timestamp # don't cache more than minute because of daylight saving time switch # 0: sec, 1: min, 2: h, 3: day, 4: month, 5: year sub str2time($$$$$$$$) { my $self = shift @_; my $GMT = pop @_; my $lastmin = $self->{str2time_lastmin}; if(defined $lastmin and $lastmin->[0] == $_[1] and $lastmin->[1] == $_[2] and $lastmin->[2] == $_[3] and $lastmin->[3] == $_[4] and $lastmin->[4] == $_[5]) { $self->{last_time} = $self->{str2time_lastmin_time} + $_[0]; return $self->{last_time} + ($self->{dst_comp}||0); } my $time; if($GMT) { $time = timegm(@_); } else { $time = timelocal(@_); } # compensate for DST-switch # - if a timewarp is detected (1:00 -> 1:30 -> 1:00): # - test if we are in a DST-switch-hour # - compensate if yes # note that we assume that the DST-switch goes like this: # time 1:00 1:30 2:00 2:30 2:00 2:30 3:00 3:30 # stamp 1 2 3 4 3 3 7 8 # comp. 0 0 0 0 2 2 0 0 # result 1 2 3 4 5 6 7 8 # old Time::Local versions behave differently (1 2 5 6 5 6 7 8) if(!$GMT and !defined $self->{dst_comp} and defined $self->{last_time} and $self->{last_time}-$time > 1200 and $self->{last_time}-$time < 3600) { my ($off, $until) = $self->is_dst_switch(\@_, $time); if($off) { $self->{dst_comp} = $off; $self->{dst_comp_until} = $until; } } if(defined $self->{dst_comp_until} and $time > $self->{dst_comp_until}) { delete $self->{dst_comp}; delete $self->{dst_comp_until}; } $self->{str2time_lastmin} = [ @_[1..5] ]; $self->{str2time_lastmin_time} = $time-$_[0]; $self->{last_time} = $time; return $time+($self->{dst_comp}||0); } sub _use_locale($) { use POSIX qw(locale_h strftime); my $old_locale = setlocale(LC_TIME); for my $locale (@_) { croak "new(): wrong 'locale' value: '$locale'" unless setlocale(LC_TIME, $locale); for my $month (0..11) { $months_map{strftime("%b", 0, 0, 0, 1, $month, 96)} = $month; } } setlocale(LC_TIME, $old_locale); } sub new($$;%) { my ($class, $file, %data) = @_; croak "new() requires one argument: file" unless defined $file; %data = () unless %data; if(not defined $data{year}) { $data{year} = (localtime(time))[5]+1900; } $data{type} = 'syslog' unless defined $data{type}; $data{_repeat}=0; if(UNIVERSAL::isa($file, 'IO::Handle')) { $data{file} = $file; } elsif(UNIVERSAL::isa($file, 'File::Tail')) { $data{file} = $file; $data{filetail}=1; } elsif(! ref $file) { if($file eq '-') { my $io = new IO::Handle; $data{file} = $io->fdopen(fileno(STDIN),"r"); } else { $data{file} = new IO::File($file, "<"); defined $data{file} or croak "can't open $file: $!"; } } else { croak "argument must be either a file-name or an IO::Handle object."; } if(defined $data{locale}) { if(ref $data{locale} eq 'ARRAY') { _use_locale @{$data{locale}}; } elsif(ref $data{locale} eq '') { _use_locale $data{locale}; } else { croak "'locale' parameter must be scalar or array of scalars"; } } return bless \%data, $class; } sub _year_increment($$) { my ($self, $mon) = @_; # year change if($mon==0) { $self->{year}++ if defined $self->{_last_mon} and $self->{_last_mon} == 11; $self->{enable_year_decrement} = 1; } elsif($mon == 11) { if($self->{enable_year_decrement}) { $self->{year}-- if defined $self->{_last_mon} and $self->{_last_mon} != 11; } } else { $self->{enable_year_decrement} = 0; } $self->{_last_mon} = $mon; } sub _next_line($) { my $self = shift; my $f = $self->{file}; if(defined $self->{filetail}) { return $f->read; } else { return $f->getline; } } sub _next_syslog($) { my ($self) = @_; while($self->{_repeat}>0) { $self->{_repeat}--; return $self->{_repeat_data}; } my $file = $self->{file}; line: while(defined (my $str = $self->_next_line)) { # date, time and host $str =~ /^ (\S{3})\s+(\d+) # date -- 1, 2 \s (\d+):(\d+):(\d+) # time -- 3, 4, 5 (?:\s<\w+\.\w+>)? # FreeBSD's verbose-mode \s ([-\w\.\@:]+) # host -- 6 \s+ (?:\[LOG_[A-Z]+\]\s+)? # FreeBSD (.*) # text -- 7 $/x or do { warn "WARNING: line not in syslog format: $str"; next line; }; my $mon = $months_map{$1}; defined $mon or croak "unknown month $1\n"; $self->_year_increment($mon); # convert to unix time my $time = $self->str2time($5,$4,$3,$2,$mon,$self->{year}-1900,$self->{GMT}); if(not $self->{allow_future}) { # accept maximum one day in the present future if($time - time > 86400) { warn "WARNING: ignoring future date in syslog line: $str"; next line; } } my ($host, $text) = ($6, $7); # last message repeated ... times if($text =~ /^(?:last message repeated|above message repeats) (\d+) time/) { next line if defined $self->{repeat} and not $self->{repeat}; next line if not defined $self->{_last_data}{$host}; $1 > 0 or do { warn "WARNING: last message repeated 0 or less times??\n"; next line; }; $self->{_repeat}=$1-1; $self->{_repeat_data}=$self->{_last_data}{$host}; return $self->{_last_data}{$host}; } # marks next if $text eq '-- MARK --'; # some systems send over the network their # hostname prefixed to the text. strip that. $text =~ s/^$host\s+//; # discard ':' in HP-UX 'su' entries like this: # Apr 24 19:09:40 remedy : su : + tty?? root-oracle $text =~ s/^:\s+//; $text =~ /^ ([^:]+?) # program -- 1 (?:\[(\d+)\])? # PID -- 2 :\s+ (?:\[ID\ (\d+)\ ([a-z0-9]+)\.([a-z]+)\]\ )? # Solaris 8 "message id" -- 3, 4, 5 (.*) # text -- 6 $/x or do { warn "WARNING: line not in syslog format: $str"; next line; }; if($self->{arrayref}) { $self->{_last_data}{$host} = [ $time, # 0: timestamp $host, # 1: host $1, # 2: program $2, # 3: pid $6, # 4: text ]; } else { $self->{_last_data}{$host} = { timestamp => $time, host => $host, program => $1, pid => $2, msgid => $3, facility => $4, level => $5, text => $6, }; } return $self->{_last_data}{$host}; } return undef; } sub _next_metalog($) { my ($self) = @_; my $file = $self->{file}; line: while(my $str = $self->_next_line) { # date, time and host $str =~ /^ (\S{3})\s+(\d+) # date -- 1, 2 \s (\d+):(\d+):(\d+) # time -- 3, 4, 5 # host is not logged \s+ (.*) # text -- 6 $/x or do { warn "WARNING: line not in metalog format: $str"; next line; }; my $mon = $months_map{$1}; defined $mon or croak "unknown month $1\n"; $self->_year_increment($mon); # convert to unix time my $time = $self->str2time($5,$4,$3,$2,$mon,$self->{year}-1900,$self->{GMT}); my $text = $6; $text =~ /^ \[(.*?)\] # program -- 1 # no PID \s+ (.*) # text -- 2 $/x or do { warn "WARNING: text line not in metalog format: $text ($str)"; next line; }; if($self->{arrayref}) { return [ $time, # 0: timestamp 'localhost', # 1: host $1, # 2: program undef, # 3: (no) pid $2, # 4: text ]; } else { return { timestamp => $time, host => 'localhost', program => $1, text => $2, }; } } return undef; } sub next($) { my ($self) = @_; if($self->{type} eq 'syslog') { return $self->_next_syslog(); } elsif($self->{type} eq 'metalog') { return $self->_next_metalog(); } croak "Internal error: unknown type: $self->{type}"; } ##################################################################### ##################################################################### ##################################################################### use RRDs; use strict; use File::Tail; use Getopt::Long; use POSIX 'setsid'; my $VERSION = "1.15"; # config my $rrdstep = 60; my $xpoints = 540; my $points_per_sample = 3; my $daemon_logfile = '/var/log/mailgraph.log'; my $daemon_pidfile = '/var/run/mailgraph.pid'; my $daemon_rrd_dir = '/var/log'; # global variables my $logfile; my $rrd = "mailgraph.rrd"; my $rrd_virus = "mailgraph_virus.rrd"; my $rrd_grey = "mailgraph_grey.rrd"; my $rrd_dane = "mailgraph_dane.rrd"; my $rrd_dmarc = "mailgraph_dmarc.rrd"; my $rrd_post = "mailgraph_post.rrd"; my $year; my $this_minute; my %sum = ( sent => 0, received => 0, bounced => 0, rejected => 0, virus => 0, spam => 0, greylisted => 0, delayed => 0,whitelist => 0, new => 0, awl => 0, early => 0, reconnectok => 0, anonymoustls => 0, trustedtls => 0, untrustedtls => 0, verifiedtls => 0, spfnone => 0, spffail => 0, spfpass => 0, dmarcnone => 0, dmarcfail => 0, dmarcpass => 0, dkimnone => 0, dkimfail => 0, dkimpass => 0, pswl => 0, psbl => 0, passold => 0, veto => 0, pregreet => 0, dnsbl => 0, pipelining =>, nonsmtp => 0, barenewline => 0, command => 0, hangup =>, passnew => 0); my $rrd_inited=0; my %opt = (); # prototypes sub daemonize(); sub process_line($); #4 sub event_sent($); sub event_received($); sub event_bounced($); sub event_rejected($); #2 sub event_virus($); sub event_spam($); #7 sub event_greylisted($); sub event_delayed($); sub event_whitelist($); sub event_new($); sub event_awl($); sub event_early($); sub event_reconnectok($); #4 sub event_anonymoustls($); sub event_trustedtls($); sub event_untrustedtls($); sub event_verifiedtls($); #9 sub event_spfnone($); sub event_spffail($); sub event_spfpass($); sub event_dmarcnone($); sub event_dmarcfail($); sub event_dmarcpass($); sub event_dkimnone($); sub event_dkimfail($); sub event_dkimpass($); #12 sub event_pswl($); sub event_psbl($); sub event_passold($); sub event_veto($); sub event_pregreet($); sub event_dnsbl($); sub event_pipelining($); sub event_nonsmtp($); sub event_barenewline($); sub event_command($); sub event_hangup($); sub event_passnew($); # sub init_rrd($); sub update($); sub usage { print "usage: mailgraph [*options*]\n\n"; print " -h, --help display this help and exit\n"; print " -v, --verbose be verbose about what you do\n"; print " -V, --version output version information and exit\n"; print " -c, --cat causes the logfile to be only read and not monitored\n"; print " -l, --logfile f monitor logfile f instead of /var/log/syslog\n"; print " -t, --logtype t set logfile's type (default: syslog)\n"; print " -y, --year starting year of the log file (default: current year)\n"; print " --host=HOST use only entries for HOST (regexp) in syslog\n"; print " -d, --daemon start in the background\n"; print " --daemon-pid=FILE write PID to FILE instead of /var/run/mailgraph.pid\n"; print " --daemon-rrd=DIR write RRDs to DIR instead of /var/log\n"; print " --daemon-log=FILE write verbose-log to FILE instead of /var/log/mailgraph.log\n"; print " --ignore-localhost ignore mail to/from localhost (used for virus scanner)\n"; print " --ignore-host=HOST ignore mail to/from HOST regexp (used for virus scanner)\n"; print " --no-mail-rrd no update mail rrd\n"; print " --no-virus-rrd no update virus rrd\n"; print " --no-grey-rrd no update grey rrd\n"; print " --no-dmarc-rrd no update dmarc rrd\n"; print " --no-dane-rrd no update dane rrd\n"; print " --no-post-rrd no update post rrd\n"; print " --only-mail-rrd update only the mail rrd\n"; print " --only-virus-rrd update only the virus rrd\n"; print " --rrd-name=NAME use NAME.rrd and NAME_virus.rrd for the rrd files\n"; print " --rbl-is-spam count rbl rejects as spam\n"; print " --virbl-is-virus count virbl rejects as viruses\n"; exit; } sub main { Getopt::Long::Configure('no_ignore_case'); GetOptions(\%opt, 'help|h', 'cat|c', 'logfile|l=s', 'logtype|t=s', 'version|V', 'year|y=i', 'host=s', 'verbose|v', 'daemon|d!', 'daemon_pid|daemon-pid=s', 'daemon_rrd|daemon-rrd=s', 'daemon_log|daemon-log=s', 'ignore-localhost!', 'ignore-host=s@', 'no-mail-rrd', 'no-virus-rrd', 'no-grey-rrd', 'no-dane-rrd', 'no-post', 'no-dmarc-rrd', 'only-mail-rrd', 'only-virus-rrd', 'rrd_name|rrd-name=s', 'rbl-is-spam', 'virbl-is-virus' ) or exit(1); usage if $opt{help}; if($opt{version}) { print "mailgraph $VERSION by david\@schweikert.ch and django\@nausch.org\n"; exit; } $daemon_pidfile = $opt{daemon_pid} if defined $opt{daemon_pid}; $daemon_logfile = $opt{daemon_log} if defined $opt{daemon_log}; $daemon_rrd_dir = $opt{daemon_rrd} if defined $opt{daemon_rrd}; $rrd = $opt{rrd_name}.".rrd" if defined $opt{rrd_name}; $rrd_virus = $opt{rrd_name}."_virus.rrd" if defined $opt{rrd_name}; $rrd_grey = $opt{rrd_name}."_grey.rrd" if defined $opt{rrd_name}; $rrd_dane = $opt{rrd_name}."_dane.rrd" if defined $opt{rrd_name}; $rrd_dmarc = $opt{rrd_name}."_dmarc.rrd" if defined $opt{rrd_name}; $rrd_post = $opt{rrd_name}."_post.rrd" if defined $opt{rrd_name}; # compile --ignore-host regexps if(defined $opt{'ignore-host'}) { for my $ih (@{$opt{'ignore-host'}}) { push @{$opt{'ignore-host-re'}}, qr{\brelay=[^\s,]*$ih}i; } } if($opt{daemon} or $opt{daemon_rrd}) { chdir $daemon_rrd_dir or die "mailgraph: can't chdir to $daemon_rrd_dir: $!"; -w $daemon_rrd_dir or die "mailgraph: can't write to $daemon_rrd_dir\n"; } daemonize if $opt{daemon}; my $logfile = defined $opt{logfile} ? $opt{logfile} : '/var/log/messages'; my $file; if($opt{cat}) { $file = $logfile; } else { $file = File::Tail->new(name=>$logfile, tail=>-1); } my $parser = new Parse::Syslog($file, year => $opt{year}, arrayref => 1, type => defined $opt{logtype} ? $opt{logtype} : 'syslog'); if(not defined $opt{host}) { while(my $sl = $parser->next) { process_line($sl); } } else { my $host = qr/^$opt{host}$/i; while(my $sl = $parser->next) { process_line($sl) if $sl->[1] =~ $host; } } } sub daemonize() { open STDIN, '/dev/null' or die "mailgraph: can't read /dev/null: $!"; if($opt{verbose}) { open STDOUT, ">>$daemon_logfile" or die "mailgraph: can't write to $daemon_logfile: $!"; } else { open STDOUT, '>/dev/null' or die "mailgraph: can't write to /dev/null: $!"; } defined(my $pid = fork) or die "mailgraph: can't fork: $!"; if($pid) { # parent open PIDFILE, ">$daemon_pidfile" or die "mailgraph: can't write to $daemon_pidfile: $!\n"; print PIDFILE "$pid\n"; close(PIDFILE); exit; } # child setsid or die "mailgraph: can't start a new session: $!"; open STDERR, '>&STDOUT' or die "mailgraph: can't dup stdout: $!"; } sub init_rrd($) { my $m = shift; my $rows = $xpoints/$points_per_sample; my $realrows = int($rows*1.1); # ensure that the full range is covered my $day_steps = int(3600*24 / ($rrdstep*$rows)); # use multiples, otherwise rrdtool could choose the wrong RRA my $week_steps = $day_steps*7; my $month_steps = $week_steps*5; my $year_steps = $month_steps*12; # mail rrd if(! -f $rrd and ! $opt{'only-virus-rrd'}) { RRDs::create($rrd, '--start', $m, '--step', $rrdstep, 'DS:sent:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:recv:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:bounced:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:rejected:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); $this_minute = $m; } elsif(-f $rrd) { $this_minute = RRDs::last($rrd) + $rrdstep; } # virus rrd if(! -f $rrd_virus and ! $opt{'only-mail-rrd'}) { RRDs::create($rrd_virus, '--start', $m, '--step', $rrdstep, 'DS:virus:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:spam:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); } elsif(-f $rrd_virus and ! defined $rrd_virus) { $this_minute = RRDs::last($rrd_virus) + $rrdstep; } # grey rrd if(! -f $rrd_grey and ! $opt{'no-grey-rrd'}) { RRDs::create($rrd_grey, '--start', $m, '--step', $rrdstep, 'DS:greylisted:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:delayed:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:whitelist:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:new:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:awl:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:early:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:reconnectok:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); $this_minute = $m; } elsif(-f $rrd_grey and ! defined $rrd_grey) { $this_minute = RRDs::last($rrd_grey) + $rrdstep; } # dane rrd if(! -f $rrd_dane and ! $opt{'no-dane-rrd'}) { RRDs::create($rrd_dane, '--start', $m, '--step', $rrdstep, 'DS:anonymoustls:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:trustedtls:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:untrustedtls:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:verifiedtls:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); $this_minute = $m; } elsif(-f $rrd_dane and ! defined $rrd_dane) { $this_minute = RRDs::last($rrd_dane) + $rrdstep; } # dmarc rrd if(! -f $rrd_dmarc and ! $opt{'no-dmarc-rrd'}) { RRDs::create($rrd_dmarc, '--start', $m, '--step', $rrdstep, 'DS:spfnone:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:spffail:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:spfpass:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dmarcnone:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dmarcfail:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dmarcpass:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dkimnone:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dkimfail:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dkimpass:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); $this_minute = $m; } elsif(-f $rrd_dmarc and ! defined $rrd_dmarc) { $this_minute = RRDs::last($rrd_dmarc) + $rrdstep; } # post rrd if(! -f $rrd_post and ! $opt{'no-post-rrd'}) { RRDs::create($rrd_post, '--start', $m, '--step', $rrdstep, 'DS:pswl:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:psbl:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:passold:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:veto:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:pregreet:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:dnsbl:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:pipelining:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:nonsmtp:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:barenewline:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:command:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:hangup:ABSOLUTE:'.($rrdstep*2).':0:U', 'DS:passnew:ABSOLUTE:'.($rrdstep*2).':0:U', "RRA:AVERAGE:0.5:$day_steps:$realrows", # day "RRA:AVERAGE:0.5:$week_steps:$realrows", # week "RRA:AVERAGE:0.5:$month_steps:$realrows", # month "RRA:AVERAGE:0.5:$year_steps:$realrows", # year "RRA:MAX:0.5:$day_steps:$realrows", # day "RRA:MAX:0.5:$week_steps:$realrows", # week "RRA:MAX:0.5:$month_steps:$realrows", # month "RRA:MAX:0.5:$year_steps:$realrows", # year ); $this_minute = $m; } elsif(-f $rrd_post and ! defined $rrd_post) { $this_minute = RRDs::last($rrd_post) + $rrdstep; } $rrd_inited=1; } sub process_line($) { my $sl = shift; my $time = $sl->[0]; my $prog = $sl->[2]; my $text = $sl->[4]; if($prog =~ /^postfix\/(.*)/) { my $prog = $1; if($prog eq 'smtp') { if($text =~ /\bstatus=sent\b/) { return if $opt{'ignore-localhost'} and $text =~ /\brelay=[^\s\[]*\[127\.0\.0\.1\]/; if(defined $opt{'ignore-host-re'}) { for my $ih (@{$opt{'ignore-host-re'}}) { warn "MATCH! $text\n" if $text =~ $ih; return if $text =~ $ih; } } event($time, 'sent'); } elsif($text =~ /\bstatus=bounced\b/) { event($time, 'bounced'); } elsif($text =~ /Anonymous TLS connection established to/) { event($time, 'anonymoustls'); } elsif($text =~ /Trusted TLS connection established to/) { event($time, 'trustedtls'); } elsif($text =~ /Untrusted TLS connection established to/) { event($time, 'untrustedtls'); } elsif($text =~ /Verified TLS connection established to/) { event($time, 'verifiedtls'); } } elsif($prog eq 'local') { if($text =~ /\bstatus=bounced\b/) { event($time, 'bounced'); } } elsif($prog eq 'smtpd') { if($text =~ /^[0-9A-Z]+: client=(\S+)/) { my $client = $1; return if $opt{'ignore-localhost'} and $client =~ /\[127\.0\.0\.1\]$/; return if $opt{'ignore-host'} and $client =~ /$opt{'ignore-host'}/oi; event($time, 'received'); } elsif($opt{'virbl-is-virus'} and $text =~ /^(?:[0-9A-Z]+: |NOQUEUE: )?reject: .*: 554.* blocked using virbl.dnsbl.bit.nl/) { event($time, 'virus'); } elsif($opt{'rbl-is-spam'} and $text =~ /^(?:[0-9A-Z]+: |NOQUEUE: )?reject: .*: 554.* blocked using/) { event($time, 'spam'); } #elsif($text =~ /Greylisted/) { # event($time, 'greylisted'); #} elsif($text =~ /^(?:[0-9A-Z]+: |NOQUEUE: )?reject: /) { event($time, 'rejected'); } elsif($text =~ /^(?:[0-9A-Z]+: |NOQUEUE: )?milter-reject: /) { if($text =~ /Blocked by SpamAssassin/) { event($time, 'spam'); } else { event($time, 'rejected'); } } } elsif($prog eq 'error') { if($text =~ /\bstatus=bounced\b/) { event($time, 'bounced'); } } elsif($prog eq 'cleanup') { if($text =~ /^[0-9A-Z]+: (?:reject|discard): /) { event($time, 'rejected'); } } elsif($prog eq 'postscreen') { if($text =~ /WHITELISTED/) { event($time, 'pswl'); } if($text =~ /BLACKLISTED/) { event($time, 'psbl'); } if($text =~ /BLACKLISTED/) { event($time, 'rejected'); } if($text =~ /PASS OLD/) { event($time, 'passold'); } if($text =~ /WHITELIST VETO/) { event($time, 'veto'); } if($text =~ /WHITELIST VETO/) { event($time, 'rejected'); } if($text =~ /PREGREET/) { event($time, 'pregreet'); } if($text =~ /PREGREET/) { event($time, 'rejected'); } if($text =~ /DNSBL/) { event($time, 'dnsbl'); } if($text =~ /DNSBL/) { event($time, 'rejected'); } if($text =~ /COMMAND PIPELINING/) { event($time, 'pipelining'); } if($text =~ /COMMAND PIPELINING/) { event($time, 'rejected'); } if($text =~ /NON-SMTP COMMAND/) { event($time, 'nonsmtp'); } if($text =~ /NON-SMTP COMMAND/) { event($time, 'rejected'); } if($text =~ /BARE NEWLINE/) { event($time, 'barenewline'); } if($text =~ /BARE NEWLINE/) { event($time, 'rejected'); } if($text =~ /COMMAND TIME LIMIT/) { event($time, 'command'); } if($text =~ /COMMAND TIME LIMIT/) { event($time, 'rejected'); } if($text =~ /COMMAND COUNT LIMIT/) { event($time, 'command'); } if($text =~ /COMMAND COUNT LIMIT/) { event($time, 'rejected'); } if($text =~ /COMMAND LENGTH LIMIT/) { event($time, 'command'); } if($text =~ /COMMAND LENGTH LIMIT/) { event($time, 'rejected'); } if($text =~ /HANGUP/) { event($time, 'hangup'); } if($text =~ /HANGUP/) { event($time, 'rejected'); } if($text =~ /PASS NEW/) { event($time, 'passnew'); } } } elsif($prog eq 'sendmail' or $prog eq 'sm-mta') { if($text =~ /\bmailer=local\b/ ) { event($time, 'received'); } elsif($text =~ /\bmailer=relay\b/) { event($time, 'received'); } elsif($text =~ /\bstat=Sent\b/ ) { event($time, 'sent'); } elsif($text =~ /\bmailer=esmtp\b/ ) { event($time, 'sent'); } elsif($text =~ /\bruleset=check_XS4ALL\b/ ) { event($time, 'rejected'); } elsif($text =~ /\blost input channel\b/ ) { event($time, 'rejected'); } elsif($text =~ /\bruleset=check_rcpt\b/ ) { event($time, 'rejected'); } elsif($text =~ /\bstat=virus\b/ ) { event($time, 'virus'); } elsif($text =~ /\bruleset=check_relay\b/ ) { if (($opt{'virbl-is-virus'}) and ($text =~ /\bivirbl\b/ )) { event($time, 'virus'); } elsif ($opt{'rbl-is-spam'}) { event($time, 'spam'); } else { event($time, 'rejected'); } } elsif($text =~ /\bsender blocked\b/ ) { event($time, 'rejected'); } elsif($text =~ /\bsender denied\b/ ) { event($time, 'rejected'); } elsif($text =~ /\brecipient denied\b/ ) { event($time, 'rejected'); } elsif($text =~ /\brecipient unknown\b/ ) { event($time, 'rejected'); } elsif($text =~ /\bUser unknown$/i ) { event($time, 'bounced'); } elsif($text =~ /\bMilter:.*\breject=55/ ) { event($time, 'rejected'); } } elsif($prog eq 'exim') { if($text =~ /^[0-9a-zA-Z]{6}-[0-9a-zA-Z]{6}-[0-9a-zA-Z]{2} <= \S+/) { event($time, 'received'); } elsif($text =~ /^[0-9a-zA-Z]{6}-[0-9a-zA-Z]{6}-[0-9a-zA-Z]{2} => \S+/) { event($time, 'sent'); } elsif($text =~ / rejected because \S+ is in a black list at \S+/) { if($opt{'rbl-is-spam'}) { event($time, 'spam'); } else { event($time, 'rejected'); } } elsif($text =~ / rejected RCPT \S+: (Sender verify failed|Unknown user)/) { event($time, 'rejected'); } } elsif($prog eq 'amavis' || $prog eq 'amavisd') { if( $text =~ /^\([\w-]+\) (Passed|Blocked) SPAM(?:MY)?\b/) { if($text !~ /\btag2=/) { # ignore new per-recipient log entry (2.2.0) event($time, 'spam'); # since amavisd-new-2004xxxx } } elsif($text =~ /^\([\w-]+\) (Passed|Not-Delivered)\b.*\bquarantine spam/) { event($time, 'spam'); # amavisd-new-20030616 and earlier } elsif($text =~ /^\([\w-]+\) (Passed |Blocked )?INFECTED\b/) { if($text !~ /\btag2=/) { event($time, 'virus');# Passed|Blocked inserted since 2004xxxx } } elsif($text =~ /^\([\w-]+\) (Passed |Blocked )?BANNED\b/) { if($text !~ /\btag2=/) { event($time, 'virus'); } } elsif($text =~ /^Virus found\b/) { event($time, 'virus');# AMaViS 0.3.12 and amavisd-0.1 } # elsif($text =~ /^\([\w-]+\) Passed|Blocked BAD-HEADER\b/) { # event($time, 'badh'); # } } elsif($prog eq 'vagatefwd') { # Vexira antivirus (old) if($text =~ /^VIRUS/) { event($time, 'virus'); } } elsif($prog eq 'hook') { # Vexira antivirus if($text =~ /^\*+ Virus\b/) { event($time, 'virus'); } # Vexira antispam elsif($text =~ /\bcontains spam\b/) { event($time, 'spam'); } } elsif($prog eq 'avgatefwd' or $prog eq 'avmailgate.bin') { # AntiVir MailGate if($text =~ /^Alert!/) { event($time, 'virus'); } elsif($text =~ /blocked\.$/) { event($time, 'virus'); } } elsif($prog eq 'avcheck') { # avcheck if($text =~ /^infected/) { event($time, 'virus'); } } elsif($prog eq 'spamd') { if($text =~ /^(?:spamd: )?identified spam/) { event($time, 'spam'); } # ClamAV SpamAssassin-plugin elsif($text =~ /(?:result: )?CLAMAV/) { event($time, 'virus'); } } elsif($prog eq 'dspam') { if($text =~ /spam detected from/) { event($time, 'spam'); } } elsif($prog eq 'spamproxyd' or $prog eq 'spampd') { if($text =~ /^\s*SPAM/ or $text =~ /^identified spam/) { event($time, 'spam'); } } elsif($prog eq 'drweb-postfix') { # DrWeb if($text =~ /infected/) { event($time, 'virus'); } } elsif($prog eq 'BlackHole') { if($text =~ /Virus/) { event($time, 'virus'); } if($text =~ /(?:RBL|Razor|Spam)/) { event($time, 'spam'); } } elsif($prog eq 'MailScanner') { if($text =~ /(Virus Scanning: Found)/ ) { event($time, 'virus'); } elsif($text =~ /Bounce to/ ) { event($time, 'bounced'); } elsif($text =~ /^Spam Checks: Found ([0-9]+) spam messages/) { my $cnt = $1; for (my $i=0; $i<$cnt; $i++) { event($time, 'spam'); } } } elsif($prog eq 'clamsmtpd') { if($text =~ /status=VIRUS/) { event($time, 'virus'); } } elsif($prog eq 'clamav-milter') { if($text =~ /Intercepted/) { event($time, 'virus'); } } # uncommment for clamassassin: #elsif($prog eq 'clamd') { # if($text =~ /^stream: .* FOUND$/) { # event($time, 'virus'); # } #} elsif ($prog eq 'smtp-vilter') { if ($text =~ /clamd: found/) { event($time, 'virus'); } } elsif($prog eq 'avmilter') { # AntiVir Milter if($text =~ /^Alert!/) { event($time, 'virus'); } elsif($text =~ /blocked\.$/) { event($time, 'virus'); } } elsif($prog eq 'bogofilter') { if($text =~ /Spam/) { event($time, 'spam'); } } elsif($prog eq 'filter-module') { if($text =~ /\bspam_status\=(?:yes|spam)/) { event($time, 'spam'); } } elsif($prog eq 'sta_scanner') { if($text =~ /^[0-9A-F]+: virus/) { event($time, 'virus'); } } elsif($prog eq 'postgrey') { if($text =~ /action=greylist/) { event($time, 'greylisted'); } ## Old versions (up to 1.27) #if($text =~ /delayed [0-9]+ seconds: client/) { # event($time, 'delayed'); #} # New versions (from 1.28) elsif($text =~ /delay=[0-9]+/) { event($time, 'delayed'); } elsif($text =~ /action=pass, reason=client whitelist/) { event($time, 'whitelist'); } elsif($text =~ /action=greylist, reason=new/) { event($time, 'new'); } elsif($text =~ /action=pass, reason=client AWL/) { event($time, 'awl'); } elsif($text =~ /action=greylist, reason=early-retry/) { event($time, 'early'); } elsif($text =~ /triplet found/) { event($time, 'reconnectok'); } } elsif ($prog eq 'smf-spf') { if ($text =~ /SPF pass:/) { event($time, 'spfpass'); } elsif($text =~ /SPF none:/) { event($time, 'spfnone'); } elsif($text =~ /fail:/) { event($time, 'spffail'); } # elsif($text =~ /SPF fail:\b/) { # event($time, 'spffail'); # } # elsif($text =~ /SPF softfail:\b/) { # event($time, 'spffail'); # } } elsif ($prog eq 'opendkim') { if ($text =~ /DKIM verification successful/) { event($time, 'dkimpass'); } elsif($text =~ /no signature data/) { event($time, 'dkimnone'); } elsif($text =~ /bad signature data/) { event($time, 'dkimfail'); } } elsif ($prog eq 'opendmarc') { if ($text =~ /pass/) { event($time, 'dmarcpass'); } elsif($text =~ /none/) { event($time, 'dmarcnone'); } elsif($text =~ /fail/) { event($time, 'dmarcfail'); } } } sub event($$) { my ($t, $type) = @_; update($t) and $sum{$type}++; } # returns 1 if $sum should be updated sub update($) { my $t = shift; my $m = $t - $t%$rrdstep; init_rrd($m) unless $rrd_inited; return 1 if $m == $this_minute; return 0 if $m < $this_minute; print "update $this_minute:$sum{sent}:$sum{received}:$sum{bounced}:$sum{rejected}:$sum{virus}:$sum{spam}:$sum{greylisted}:$sum{delayed}:$sum{whitelist}:$sum{new}:$sum{awl}:$sum{early}:$sum{reconnectok}:$sum{anonymoustls}:$sum{trustedtls}:$sum{untrustedtls}:$sum{verifiedtls}:$sum{spfnone}:$sum{spffail}:$sum{spfpass}:$sum{dmarcnone}:$sum{dmarcfail}:$sum{dmarcpass}:$sum{dkimnone}:$sum{dkimfail}:$sum{dkimpass}:$sum{pswl}:$sum{psbl}:$sum{passold}:$sum{veto}:$sum{pregreet}:$sum{dnsbl}:$sum{pipelining}:$sum{nonsmtp}:$sum{barenewline}:$sum{command}:$sum{hangup}:$sum{passnew}\n" if $opt{verbose}; #4 RRDs::update $rrd, "$this_minute:$sum{sent}:$sum{received}:$sum{bounced}:$sum{rejected}" unless $opt{'only-virus-rrd'}; #2 RRDs::update $rrd_virus, "$this_minute:$sum{virus}:$sum{spam}" unless $opt{'only-mail-rrd'}; #7 RRDs::update $rrd_grey, "$this_minute:$sum{greylisted}:$sum{delayed}:$sum{whitelist}:$sum{new}:$sum{awl}:$sum{early}:$sum{reconnectok}" unless $opt{'no-greylist-rrd'}; #4 RRDs::update $rrd_dane, "$this_minute:$sum{anonymoustls}:$sum{trustedtls}:$sum{untrustedtls}:$sum{verifiedtls}" unless $opt{'no-dane-rrd'}; #9 RRDs::update $rrd_dmarc, "$this_minute:$sum{spfnone}:$sum{spffail}:$sum{spfpass}:$sum{dmarcnone}:$sum{dmarcfail}:$sum{dmarcpass}:$sum{dkimnone}:$sum{dkimfail}:$sum{dkimpass}" unless $opt{'no-dane-rrd'}; #12 RRDs::update $rrd_post, "$this_minute:$sum{pswl}:$sum{psbl}:$sum{passold}:$sum{veto}:$sum{pregreet}:$sum{dnsbl}:$sum{pipelining}:$sum{nonsmtp}:$sum{barenewline}:$sum{command}:$sum{hangup}:$sum{passnew}" unless $opt{'no-post-rrd'}; if($m > $this_minute+$rrdstep) { for(my $sm=$this_minute+$rrdstep;$sm<$m;$sm+=$rrdstep) { print "update $sm:0:0:0:0:0:0 (SKIP)\n" if $opt{verbose}; #4 RRDs::update $rrd, "$sm:0:0:0:0" unless $opt{'only-virus-rrd'}; #2 RRDs::update $rrd_virus, "$sm:0:0" unless $opt{'only-mail-rrd'}; #7 RRDs::update $rrd_grey, "$sm:0:0:0:0:0:0:0" unless $opt{'no-grey-rrd'}; #4 RRDs::update $rrd_dane, "$sm:0:0:0:0" unless $opt{'no-dane-rrd'}; #9 RRDs::update $rrd_dmarc, "$sm:0:0:0:0:0:0:0:0:0" unless $opt{'no-dane-rrd'}; #12 RRDs::update $rrd_post, "$sm:0:0:0:0:0:0:0:0:0:0:0:0" unless $opt{'no-post-rrd'}; } } $this_minute = $m; #4 $sum{sent}=0; $sum{received}=0; $sum{bounced}=0; $sum{rejected}=0; #2 $sum{virus}=0; $sum{spam}=0; #7 $sum{greylisted}=0; $sum{delayed}=0; $sum{whitelist}=0; $sum{new}=0; $sum{awl}=0; $sum{early}=0; $sum{reconnectok}=0; #4 $sum{anonymoustls}=0; $sum{trustedtls}=0; $sum{untrustedtls}=0; $sum{verifiedtls}=0; #9 $sum{spfnone}=0; $sum{spffail}=0; $sum{spfpass}=0; $sum{dmarcnone}=0; $sum{dmarcfail}=0; $sum{dmarcpass}=0; $sum{dkimnone}=0; $sum{dkimfail}=0; $sum{dkimpass}=0; # 12 $sum{pswl}=0; $sum{psbl}=0; $sum{passold}=0; $sum{veto}=0; $sum{pregreet}=0; $sum{dnsbl}=0; $sum{pipelining}=0; $sum{nonsmtp}=0; $sum{barenewline}=0; $sum{command}=0; $sum{hangup}=0; $sum{passnew}=0; return 1; } main; __END__ =head1 NAME mailgraph.pl - rrdtool frontend for mail statistics =head1 SYNOPSIS B<mailgraph> [I<options>...] --man show man-page and exit -h, --help display this help and exit --version output version information and exit -h, --help display this help and exit -v, --verbose be verbose about what you do -V, --version output version information and exit -c, --cat causes the logfile to be only read and not monitored -l, --logfile f monitor logfile f instead of /var/log/messages -t, --logtype t set logfile's type (default: syslog) -y, --year starting year of the log file (default: current year) --host=HOST use only entries for HOST (regexp) in syslog -d, --daemon start in the background --daemon-pid=FILE write PID to FILE instead of /var/run/mailgraph.pid --daemon-rrd=DIR write RRDs to DIR instead of /var/log --daemon-log=FILE write verbose-log to FILE instead of /var/log/mailgraph.log --ignore-localhost ignore mail to/from localhost (used for virus scanner) --ignore-host=HOST ignore mail to/from HOST regexp (used for virus scanner) --no-mail-rrd do not update mail rrd --no-virus-rrd do not update virus rrd --no-grey-rrd do not update grey rrd --no-dane-rrd do not update dane rrd --only-mail-rrd update only the mail rrd --only-virus-rrd update only the virus rrd --rrd-name=NAME use NAME.rrd and NAME_virus.rrd for the rrd files --rbl-is-spam count rbl rejects as spam --virbl-is-virus count virbl rejects as viruses =head1 DESCRIPTION This script does parse syslog and updates the RRD database (mailgraph.rrd) in the current directory. =head2 Log-Types The following types can be given to --logtype: =over 10 =item syslog Traditional "syslog" (default) =item metalog Metalog (see http://metalog.sourceforge.net/) =back =head1 COPYRIGHT Copyright (c) 2000-2007 by ETH Zurich Copyright (c) 2000-2007 by David Schweikert Copyright (c) 2011 Markus Neubauer Copyright (c) 2014-2015 by Django =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. =head1 AUTHOR S<David Schweikert E<lt>david@schweikert.chE<gt>> =cut # vi: sw=8
Apache VHost anlegen
Damit wir bequem von unserem Browser aus, die aktuellen Graphiken abfragen können, bearbeiten wir entweder die aus dem RPM stammende Konfigurationsdatei oder legen wir nun einen passenden VHost an.
# vim /etc/httpd/conf.d/vhosts.conf
- /etc/httpd/conf.d/vhosts.conf
# # mailgraph.nausch.org # <VirtualHost *:80> ServerAdmin webmaster@nausch.org ServerName mailgraph.nausch.org ServerAlias www.mailgraph.nausch.org ServerPath / DocumentRoot "/usr/share/mailgraph" AddHandler cgi-script .cgi <Directory "/usr/share/mailgraph"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi Order deny,allow require IP 10.0. </Directory> ErrorLog logs/mailgraph_error.log CustomLog logs/mailgraph_access.log combined </VirtualHost>
Bevor wir bei unserem Webserver eine Reload der Konfiguration vornehmen, testen wir unsere neue Konfigurationsdatei auf syntaktische Fehler.
# apachectl -t
Syntax OK
Da keine Fehler aufgetreten sind, aktivieren wir die neue Konfiguration durch einen Reload des Webserver-Daemon.
# systemctl reload postfix
NGiNX VHost anlegen
Nutzen wir als Webserver NGiNX können wir auch hier schnell und einfach einen passenden vHOST anlegen.
# vim /etc/nginx/conf.d/vhosts.conf
- /etc/nginx/conf.d/vhosts.conf
server { listen 80; server_name queuegraph.nausch.org; access_log /var/log/nginx/queuegraph_access.log; error_log /var/log/nginx/queuegraph_errors.log; root /usr/share/queuegraph/; index queuegraph.cgi; location ~ \.php { fastcgi_split_path_info ^(.+\.cgi)(/.+)$; fastcgi_index queuegraph.cgi; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
Den Parameter fastcgi_pass setzen wir im übrigen auf den Wert aus der Konfigurationsdatei /etc/php-fpm.d/www.conf des PHP FastCGI Process Manager-Daemon php-fpm.
Haben wir die Konfigurationsdatei vervollständigt, prüfen wir diese noch auf syntaktische Fehler.
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Somit können wir unsere Konfiguration nun noch aktivieren.
# systemctl reload nginx
Programmaufruf
erster manueller Start des Dämon
Damit das Mail-Logfile forlaufend ausgelesen wird, starten wir nun noch den Dämon mit Hilfe des mitgelieferten systemd-Start-Scriptes /usr/lib/systemd/system/mailgraph.service.
# systemctl start mailgraph
Im syslog wurde der Start des Daemon entsprechend dokumentiert.
# tail -n2 /var/log/messages
Feb 2 21:17:36 vml000097 systemd: Starting mailgraph mail log file analyzer... Feb 2 21:17:37 vml000097 systemd: Started mailgraph mail log file analyzer.
Ebenso kann man den Status des Webservers mit Hilfe des Befehls systemctl abfragen.
# systemctl status mailgraph
mailgraph.service - mailgraph mail log file analyzer Loaded: loaded (/usr/lib/systemd/system/mailgraph.service; disabled) Active: active (running) since Mon 2015-02-02 21:17:37 CET; 2min 48s ago Process: 2362 ExecStart=/usr/sbin/mailgraph -d -l $MAILLOG --daemon-rrd=/var/lib/mailgraph $OPTIONS (code=exited, status=0/SUCCESS) Main PID: 2367 (mailgraph) CGroup: /system.slice/mailgraph.service └─2367 /usr/bin/perl -w /usr/sbin/mailgraph -d -l /var/log/maillog --daemon-rrd=/var/lib/mail... Feb 02 21:17:37 vml000097.dmz.nausch.org systemd[1]: Started mailgraph mail log file analyzer.
automatischer Start beim Systemstart
Wollen wir den Daemon beim Hochfahren des Systems automatisch starten, greifen wir auf den Befehl systemctl zurück.
# systemctl enable mailgraph.service
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
Möchten wir uns vergewissern, ob der Daemon beim Systemstart gestartet wird oder nicht, erfahren wir ebenfalls mit dem Befehl systemctl.
# systemctl is-enabled mailgraph.service
enabled
Startet der Server nicht automatisch, wird uns ein „disabled“ zurückgemeldet.
Webaufruf
Über unseren vHOST erhalten wir nun optisch schön ansprechende Übersichten über den Mailverkehr unseres MX.
Ausgabe individualisieren
Der zeitgleiche Einsatz von Postgrey und Postscreen macht i.d.R. keinen besonderen Sinn und ist auch nicht zu empfehlen. In der CGI-Datei, die das Generieren der Graphiken und der Webseite vornimmt, sind aber alle aktuell möglichen Szenarien abgebildet.
Einzelne Graphen lassen sich sehr leicht und einfach ausblenden, in dem man die zugehörigen Zeilen einfach auskommentiert, also mit einem # am Zeilenanfang versieht. Diese Zeilen findet man im letzten ¼ der Datei.
Im folgendem Beispiel wollen wir die Greylisting-Graphen nicht mehr ausgeben lassen. Mit dem Editor unserer Wahl bearbeiten wir das cgi-script.
# vim /usr/share/mailgraph/mailgraph.cgi
... for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; #print "<h4><center>Greylisting Übersicht</center></h3>\n"; #print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; #print "<h4><center>Greylisting Detailansicht</center></h3>\n"; #print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen Übersicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>DANE / TLSA Verbindungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h3>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } ...
So lassen sich z.B. auch die Reports zu SPF, DKIM und DMARC ausblenden, wenn man diese (aktuell noch) nicht haben möchte. Will man später die Graphen wieder sehen, reicht das Entfernen des Kommentarzeichens # am Anfang der betreffenden Zeilen.
Mailgraph-NG für mehrere Mailserver
Betreibt man mehrere Mailserver will man neben den Einzelstatistiken der einzelnen Mailserver oft auch einen Überblick über den gesamten Mail-Server-Verkehr haben. Auch das lässt sich mit einem überschaubaren Aufwand realisieren. Hierzu gehen wir wie folgt vor.
- Von den einzelnen Mailservern holen wir die RRD-Dateien ab.
- Diese kopieren wir dann auf ein zentrales Verzeichnis auf unserem WEB-Server.
- Wir passen unser CGI-Script, welches für die dynamische Generierung der WEB-Seite benutzt wird, soweit an, dass beim Erstellen eines Graphen nicht nur eine Datenquelle herangezogen wird, sondern die Summe unserer einzelnen Mailserver verwendet werden soll.
Auf diese drei Schritte gehen wir nun im Detail noch ein.
RRD-Dateien einsammeln
Die einzelnen RRD-Dateien unserer Mailserver kopieren wir mit scp in ein temporäres Arbeitsverzeichnis unseres WEB-Servers. Hierzu legen wir uns erst einmal einen eigenen User an.
# groupadd -g 1100 mx-transfer && useradd -c "MX Transfer User" -d /home/mx-transfer -g 1100 -s /bin/bash -u 1100 mx-transfer
Das Gleiche machen wir auf jedem der MX-Hosts, von denen wir später die Dateien einsammeln möchten.
Anschließend generieren wir uns einen ssh-key für diesen transfer-User. Hierzu schlüpfen wir erst in die Rolle dieses speziellen Users.
# su - mx-transfer
Anschließend legen wir das Unterverzeichnis für den SSH-Schlüssel an und passen dessen Verzeichnisrechte an.
$ mkdir ~/.ssh $ chmod 700 ~/.ssh
Auch hier legen wir auf jedem der MX-Hosts, von denen wir später die Dateien einsammeln möchten, das Verzeichnis mit den nötigen Berechtigungen an.
Nun legen wir uns einen ssh-key ohne Passphrase an, in dem wir bei der Frage nach der Passphrase einfach keine angeben.
$ ssh-keygen -b 4096 -t rsa -C "MX Transfer User"
Generating public/private rsa key pair. Enter file in which to save the key (/home/mx-transfer/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/mx-transfer/.ssh/id_rsa. Your public key has been saved in /home/mx-transfer/.ssh/id_rsa.pub. The key fingerprint is: 30:9d:ab:8e:62:66:85:da:49:9e:29:75:60:23:a7:e2 MX Transfer User The key's randomart image is: +--[ RSA 4096]----+ | | | . . | | o o | |. = o . | | = + S | |o + o . | |o* * . | |oE@ o | | = .. . | +-----------------+
Den public-key kopieren wir nun anschließend auf jeden unserer MX-Hosts.
$ ssh-copy-id -i ~/.ssh/id_rsa.pub mx11.dmz.nausch.org
$ ssh-copy-id -i ~/.ssh/id_rsa.pub mx12.dmz.nausch.org
$ ssh-copy-id -i ~/.ssh/id_rsa.pub mx13.dmz.nausch.org
$ ssh-copy-id -i ~/.ssh/id_rsa.pub mx14.dmz.nausch.org
Als nächstes legen wir uns die temporären Zielverzeichnisse auf unserem Webserver an, in die wir später via scp die RRD-Dateien kopieren.
$ mkdir -p ~/mailgraph/{mx11,mx12,mx13,mx14}
Somit haben wir folgende Verzeichnisstruktur auf unserem Server.
/home/mx-transfer/ └── mailgraph ├── mx11 ├── mx12 ├── mx13 └── mx14
Für das Einsammeln der RRD-Dateien legen wir uns ein passendes Script im Verzeichnis ~/bin an. Fehlt dieses Verzeichnis, so legen wir zuvor dieses noch an.
$ mkdir ~/bin
$ vim ~/bin/mx-collect
- ~/bin/mx-collect
#!/bin/bash # Django : 2015-02-26 # Einsammeln der RRD-Files von den einzelnen Mailservern cd /home/mx-transfer/mailgraph/mx11 scp 10.100.0.87:/var/lib/queuegraph/mailqueues.rrd . scp 10.100.0.87:/var/lib/mailgraph/*.rrd . cd /home/mx-transfer/mailgraph/mx12 scp 10.200.0.87:/var/lib/queuegraph/mailqueues.rrd . scp 10.200.0.87:/var/lib/mailgraph/*.rrd . cd /home/mx-transfer/mailgraph/mx13 scp 10.300.0.87:/var/lib/queuegraph/mailqueues.rrd . scp 10.300.0.87:/var/lib/mailgraph/*.rrd . cd /home/mx-transfer/mailgraph/mx14 scp 10.400.0.87:/var/lib/queuegraph/mailqueues.rrd . scp 10.400.0.87:/var/lib/mailgraph/*.rrd .
Damit das Script auch ausgeführt werden kann, geben wir ihm noch das entsprechende x-Recht.
$ chmod +x ~/bin/mx-collect
Im 5-minütigen Abstand holen wird dann die Daten von den Mailservern ab. Hierzu nutzen wir die benutzerindividuelle crontab unseres Transfer-Users.
$ crontab -e
# Django : 2015-02-26 # alles 5 Minuten die RRD-Files von den einzelnen Mailservern einsammeln 2,7,12,17,22,27,32,37,42,47,52,57 * * * * /home/mx-transfer/bin/mx-collect &> /dev/null
Nach kurzer Zeit haben wir nunmehr von den einzelnen Mailservern die Statistikdaten mit einer Aktualität von 5 Minuten vorliegen.
/home/mx-transfer/mailgraph/ ├── mx11 │ ├── mailgraph_dane.rrd │ ├── mailgraph_dmarc.rrd │ ├── mailgraph_grey.rrd │ ├── mailgraph_post.rrd │ ├── mailgraph.rrd │ ├── mailgraph_smtpd.rrd │ ├── mailgraph_virus.rrd │ └── mailqueues.rrd ├── mx12 │ ├── mailgraph_dane.rrd │ ├── mailgraph_dmarc.rrd │ ├── mailgraph_grey.rrd │ ├── mailgraph_post.rrd │ ├── mailgraph.rrd │ ├── mailgraph_smtpd.rrd │ ├── mailgraph_virus.rrd │ └── mailqueues.rrd ├── mx13 │ ├── mailgraph_dane.rrd │ ├── mailgraph_dmarc.rrd │ ├── mailgraph_grey.rrd │ ├── mailgraph_post.rrd │ ├── mailgraph.rrd │ ├── mailgraph_smtpd.rrd │ ├── mailgraph_virus.rrd │ └── mailqueues.rrd └── mx14 ├── mailgraph_dane.rrd ├── mailgraph_dmarc.rrd ├── mailgraph_grey.rrd ├── mailgraph_post.rrd ├── mailgraph.rrd ├── mailgraph_smtpd.rrd ├── mailgraph_virus.rrd └── mailqueues.rrd
Datenbereitstellung
Das CGI-Script, welches wir zum dynamischen Generieren der Statistikgraphen verwenden, bekommt im Verzeichnis /var/lib/mailgraph/ zur Verfügung gestellt. Für jeden unserer Mailserver legen wir dort ein Unterverzeichnis an.
# mkdir -p /var/lib/mailgraph/{mx11,mx12,mx13,mx14}
Die Befüllung dieser Zeilverzeichnisse nehmen wir jeweils mit Hilfe eines rsync-Aufrufs vor, den wir via cronjob ausführen lassen.
# vim /etc/crontab
- /etc/crontab
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # 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 # Django : 2015-02-26 # alles 5 Minuten die RRD-Files vom Transfer-Verzeichnis abholen und in den Zielverzeichnissen ablegen */5 * * * * root /usr/bin/rsync /home/mx-transfer/mailgraph/mx11/* /var/lib/mailgraph/mx11/ &> /dev/null */5 * * * * root /usr/bin/rsync /home/mx-transfer/mailgraph/mx12/* /var/lib/mailgraph/mx12/ &> /dev/null */5 * * * * root /usr/bin/rsync /home/mx-transfer/mailgraph/mx13/* /var/lib/mailgraph/mx13/ &> /dev/null */5 * * * * root /usr/bin/rsync /home/mx-transfer/mailgraph/mx14/* /var/lib/mailgraph/mx14/ &> /dev/null
Nach wenigen Minuten stehen die Statistikdaten, jeweils mit einer Aktualität von 5 Minuten, in den Zielverzeichnissen zur Verfügen, wo diese nun von dem CGI-Script eingelesen und aus den Daten die Graphen produziert werden können.
Generierung Statistikgraphen und Webseite(n)
Zur Präsentation unserer Statistikdaten benötigen wir nun noch einen Webserver, der die aufbereiteten Daten bereitstellt. Hierzu legen wir uns folgende Verzeichnisstruktur an.
/usr/share/mailgraph-ng/ ├── mx ├── mx11 ├── mx12 ├── mx13 └── mx14
# mkdir -p /usr/share/mailgraph-ng/{mx,mx11,mx12,mx13,mx14}
Bei der RPM-Installation von mailgraph wurden das Logo /usr/share/mailgraph/rrdtool-3dlogo.png wie auch die CSS-Datei /usr/share/mailgraph/mailgraph.css bereits auf unseren Server kopiert. Fehlen diese beiden Dateien, kann man diese hier aus Djangos WIKI herunterladen.
- /usr/share/mailgraph/mailgraph.css
* { margin: 0; padding: 0 } body { width: 900px; background-color: white; font-family: sans-serif; font-size: 12pt; margin: 5px } h1 { margin-top: 20px; margin-bottom: 5px; text-align: center } h2 { background-color: #ddd; padding: 2px 0 2px 4px } h3 { margin-top: 10px; margin-bottom: 20px; text-align: center } hr { height: 1px; border: 0; border-top: 1px solid #aaa } table { border: 0px; width: 100% } img { border: 0 } a { text-decoration: none; color: #00e } a:hover { text-decoration: underline; } #jump { margin: 0 0 10px 4px } #jump li { list-style: none; display: inline; font-size: 90%; } #jump li:after { content: "|"; } #jump li:last-child:after { content: ""; }
Das Logo und die CSS-Date kopieren wir nun die die einzelnen Unterverzeichnisse.
# cp /usr/share/mailgraph/rrdtool-3dlogo.png /usr/share/mailgraph-ng/mx # cp /usr/share/mailgraph/rrdtool-3dlogo.png /usr/share/mailgraph-ng/mx11 # cp /usr/share/mailgraph/rrdtool-3dlogo.png /usr/share/mailgraph-ng/mx12 # cp /usr/share/mailgraph/rrdtool-3dlogo.png /usr/share/mailgraph-ng/mx13 # cp /usr/share/mailgraph/rrdtool-3dlogo.png /usr/share/mailgraph-ng/mx14
# cp /usr/share/mailgraph/mailgraph.css /usr/share/mailgraph-ng/mx # cp /usr/share/mailgraph/mailgraph.css /usr/share/mailgraph-ng/mx11 # cp /usr/share/mailgraph/mailgraph.css /usr/share/mailgraph-ng/mx12 # cp /usr/share/mailgraph/mailgraph.css /usr/share/mailgraph-ng/mx13 # cp /usr/share/mailgraph/mailgraph.css /usr/share/mailgraph-ng/mx14
In den Unterverzeichnissen mx11 bis mx14 legen wir jeweils ein CGI-Script ab. Damit wir später zwischen den Einzelanzeigen der Mailserver und der kumulierten Übersicht jeweils wechseln können, versehen wir das Originalscript aus dem RPM mailgraph mit entsprechenden angepassten Verweisen.
Die wesentlichen Änderungen/Erweiterung zum Originalscript aus dem RPM sind:
- Änderungen des Verzeichnisses der Datenquellen (RRD-Dateien).
Bsp. MX11:... my $rrd = '/var/lib/mailgraph/mx11/mailgraph.rrd'; my $rrd_virus = '/var/lib/mailgraph/mx11/mailgraph_virus.rrd'; my $rrd_grey = '/var/lib/mailgraph/mx11/mailgraph_grey.rrd'; my $rrd_dane = '/var/lib/mailgraph/mx11/mailgraph_dane.rrd'; my $rrd_dmarc = '/var/lib/mailgraph/mx11/mailgraph_dmarc.rrd'; my $rrd_smtpd = '/var/lib/mailgraph/mx11/mailgraph_smtpd.rrd'; my $rrd_queue = '/var/lib/mailgraph/mx11/mailqueues.rrd'; my $rrd_post = '/var/lib/mailgraph/mx11/mailgraph_post.rrd'; ...
- Einfügen der Variablen url für die Verweise zu den jeweils anderen Statistikseiten.
Bsp. MX11:... my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; ...
- Erweiterung des Unterprogramms print_html um die links zu den verweisenden Statistikseiten.
... sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für mx11.dmz.nausch.org</h1>\n"; print "<h3>(<a href='$url'>Summen-</a> und Einzelaufstellungen "; #print "<a href='$url'>MX11</a>, "; print "<a href='$url2'>MX12</a>, "; print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h4>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } ...
CGI-Scripte für die Einzelsysteme
Für die vier Mailserver ergeben sich folgende CGI-Scripte.
/usr/share/mailgraph-ng/mx11/mailgraph.cgi
# vim /usr/share/mailgraph-ng/mx11/mailgraph.cgi
- /usr/share/mailgraph-ng/mx11/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd = '/var/lib/mailgraph/mx11/mailgraph.rrd'; my $rrd_virus = '/var/lib/mailgraph/mx11/mailgraph_virus.rrd'; my $rrd_grey = '/var/lib/mailgraph/mx11/mailgraph_grey.rrd'; my $rrd_dane = '/var/lib/mailgraph/mx11/mailgraph_dane.rrd'; my $rrd_dmarc = '/var/lib/mailgraph/mx11/mailgraph_dmarc.rrd'; my $rrd_smtpd = '/var/lib/mailgraph/mx11/mailgraph_smtpd.rrd'; my $rrd_queue = '/var/lib/mailgraph/mx11/mailqueues.rrd'; my $rrd_post = '/var/lib/mailgraph/mx11/mailgraph_post.rrd'; my $tmp_dir = '/var/cache/mailgraph'; my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'E1FFC1', # psbl => 'EBBAD5', passold => 'BAFF70', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => '468700', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', untrustedtls => 'ffebd1', anonymoustls => 'ffcf90', trustedtls => 'ffb24f', verifiedtls => 'ff5800', untrustedtlsin => 'ddd1ff', anonymoustlsin => 'a8a8ff', trustedtlsin => '6767ff', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "AREA:rrejected#$color{rejected}:Rejected ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "AREA:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "LINE2:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new=$rrd_grey:new:AVERAGE", "DEF:mnew=$rrd_grey:new:MAX", "CDEF:rnew=new,60,*", "CDEF:rmnew=mnew,60,*", "CDEF:dnew=new,UN,0,new,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok=$rrd_grey:reconnectok:AVERAGE", "DEF:mreconnectok=$rrd_grey:reconnectok:MAX", "CDEF:rreconnectok=reconnectok,60,*", "CDEF:dreconnectok=reconnectok,UN,0,reconnectok,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted=$rrd_grey:greylisted:AVERAGE", "DEF:mgreylisted=$rrd_grey:greylisted:MAX", "CDEF:rgreylisted=greylisted,60,*", "CDEF:rmgreylisted=mgreylisted,60,*", "CDEF:dgreylisted=greylisted,UN,0,greylisted,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed=$rrd_grey:delayed:AVERAGE", "DEF:mdelayed=$rrd_grey:delayed:MAX", "CDEF:rdelayed=delayed,60,*", "CDEF:rmdelayed=mdelayed,60,*", "CDEF:ddelayed=delayed,UN,0,delayed,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist=$rrd_grey:whitelist:AVERAGE", "DEF:mwhitelist=$rrd_grey:whitelist:MAX", "CDEF:rwhitelist=whitelist,60,*", "CDEF:rmwhitelist=mwhitelist,60,*", "CDEF:dwhitelist=whitelist,UN,0,whitelist,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl=$rrd_grey:awl:AVERAGE", "DEF:mawl=$rrd_grey:awl:MAX", "CDEF:rawl=awl,60,*", "CDEF:dawl=awl,UN,0,awl,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early=$rrd_grey:early:AVERAGE", "DEF:mearly=$rrd_grey:early:MAX", "CDEF:rearly=early,60,*", "CDEF:rmearly=mearly,60,*", "CDEF:dearly=early,UN,0,early,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl=$rrd_post:pswl:AVERAGE", "DEF:mpswl=$rrd_post:pswl:MAX", "CDEF:rpswl=pswl,60,*", "CDEF:rmpswl=mpswl,60,*", "CDEF:dpswl=pswl,UN,0,pswl,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold=$rrd_post:passold:AVERAGE", "DEF:mpassold=$rrd_post:passold:MAX", "CDEF:rpassold=passold,60,*", "CDEF:dpassold=passold,UN,0,passold,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold,60,*", "STACK:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew=$rrd_post:passnew:AVERAGE", "DEF:mpassnew=$rrd_post:passnew:MAX", "CDEF:rpassnew=passnew,60,*", "CDEF:dpassnew=passnew,UN,0,passnew,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl=$rrd_post:psbl:AVERAGE", "DEF:mpsbl=$rrd_post:psbl:MAX", "CDEF:rpsbl=psbl,60,*", "CDEF:dpsbl=psbl,UN,0,psbl,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto=$rrd_post:veto:AVERAGE", "DEF:mveto=$rrd_post:veto:MAX", "CDEF:rveto=veto,60,*", "CDEF:dveto=veto,UN,0,veto,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet=$rrd_post:pregreet:AVERAGE", "DEF:mpregreet=$rrd_post:pregreet:MAX", "CDEF:rpregreet=pregreet,60,*", "CDEF:dpregreet=pregreet,UN,0,pregreet,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl=$rrd_post:dnsbl:AVERAGE", "DEF:mdnsbl=$rrd_post:dnsbl:MAX", "CDEF:rdnsbl=dnsbl,60,*", "CDEF:ddnsbl=dnsbl,UN,0,dnsbl,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining=$rrd_post:pipelining:AVERAGE", "DEF:mpipelining=$rrd_post:pipelining:MAX", "CDEF:rpipelining=pipelining,60,*", "CDEF:dpipelining=pipelining,UN,0,pipelining,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp=$rrd_post:nonsmtp:AVERAGE", "DEF:mnonsmtp=$rrd_post:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp,60,*", "CDEF:dnonsmtp=nonsmtp,UN,0,nonsmtp,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline=$rrd_post:barenewline:AVERAGE", "DEF:mbarenewline=$rrd_post:barenewline:MAX", "CDEF:rbarenewline=barenewline,60,*", "CDEF:dbarenewline=barenewline,UN,0,barenewline,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command=$rrd_post:command:AVERAGE", "DEF:mcommand=$rrd_post:command:MAX", "CDEF:rcommand=command,60,*", "CDEF:dcommand=command,UN,0,command,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup=$rrd_post:hangup:AVERAGE", "DEF:mhangup=$rrd_post:hangup:MAX", "CDEF:rhangup=hangup,60,*", "CDEF:dhangup=hangup,UN,0,hangup,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtls=$rrd_dane:untrustedtls:AVERAGE", "DEF:muntrustedtls=$rrd_dane:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls,60,*", "CDEF:duntrustedtls=untrustedtls,UN,0,untrustedtls,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls,60,*", "AREA:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustls=$rrd_dane:anonymoustls:AVERAGE", "DEF:manonymoustls=$rrd_dane:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls,60,*", "CDEF:danonymoustls=anonymoustls,UN,0,anonymoustls,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls,60,*", "STACK:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls=$rrd_dane:trustedtls:AVERAGE", "DEF:mtrustedtls=$rrd_dane:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls,60,*", "CDEF:dtrustedtls=trustedtls,UN,0,trustedtls,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls=$rrd_dane:verifiedtls:AVERAGE", "DEF:mverifiedtls=$rrd_dane:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls,60,*", "CDEF:dverifiedtls=verifiedtls,UN,0,verifiedtls,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_smtpd($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtlsin=$rrd_smtpd:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin=$rrd_smtpd:untrustedtlsin:MAX", "CDEF:runtrustedtlsin=untrustedtlsin,60,*", "CDEF:duntrustedtlsin=untrustedtlsin,UN,0,untrustedtlsin,IF,$step,*", "CDEF:suntrustedtlsin=PREV,UN,duntrustedtlsin,PREV,IF,duntrustedtlsin,+", "CDEF:rmuntrustedtlsin=muntrustedtlsin,60,*", "AREA:runtrustedtlsin#$color{untrustedtlsin}:IN Untrusted TLS ", 'GPRINT:suntrustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustlsin=$rrd_smtpd:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin=$rrd_smtpd:anonymoustlsin:MAX", "CDEF:ranonymoustlsin=anonymoustlsin,60,*", "CDEF:danonymoustlsin=anonymoustlsin,UN,0,anonymoustlsin,IF,$step,*", "CDEF:sanonymoustlsin=PREV,UN,danonymoustlsin,PREV,IF,danonymoustlsin,+", "CDEF:rmanonymoustlsin=manonymoustlsin,60,*", "STACK:ranonymoustlsin#$color{anonymoustlsin}:IN Anonymous TLS ", 'GPRINT:sanonymoustlsin:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtlsin=$rrd_smtpd:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin=$rrd_smtpd:trustedtlsin:MAX", "CDEF:rtrustedtlsin=trustedtlsin,60,*", "CDEF:dtrustedtlsin=trustedtlsin,UN,0,trustedtlsin,IF,$step,*", "CDEF:strustedtlsin=PREV,UN,dtrustedtlsin,PREV,IF,dtrustedtlsin,+", "CDEF:rmtrustedtlsin=mtrustedtlsin,60,*", "STACK:rtrustedtlsin#$color{trustedtlsin}:In Trusted TLS ", 'GPRINT:strustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtlsin:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass=$rrd_dmarc:spfpass:AVERAGE", "DEF:mspfpass=$rrd_dmarc:spfpass:MAX", "CDEF:rspfpass=spfpass,60,*", "CDEF:dspfpass=spfpass,UN,0,spfpass,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone=$rrd_dmarc:spfnone:AVERAGE", "DEF:mspfnone=$rrd_dmarc:spfnone:MAX", "CDEF:rspfnone=spfnone,60,*", "CDEF:dspfnone=spfnone,UN,0,spfnone,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail=$rrd_dmarc:spffail:AVERAGE", "DEF:mspffail=$rrd_dmarc:spffail:MAX", "CDEF:rspffail=spffail,60,*", "CDEF:dspffail=spffail,UN,0,spffail,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass=$rrd_dmarc:dkimpass:AVERAGE", "DEF:mdkimpass=$rrd_dmarc:dkimpass:MAX", "CDEF:rdkimpass=dkimpass,60,*", "CDEF:ddkimpass=dkimpass,UN,0,dkimpass,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone=$rrd_dmarc:dkimnone:AVERAGE", "DEF:mdkimnone=$rrd_dmarc:dkimnone:MAX", "CDEF:rdkimnone=dkimnone,60,*", "CDEF:ddkimnone=dkimnone,UN,0,dkimnone,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail=$rrd_dmarc:dkimfail:AVERAGE", "DEF:mdkimfail=$rrd_dmarc:dkimfail:MAX", "CDEF:rdkimfail=dkimfail,60,*", "CDEF:ddkimfail=dkimfail,UN,0,dkimfail,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass=$rrd_dmarc:dmarcpass:AVERAGE", "DEF:mdmarcpass=$rrd_dmarc:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass,60,*", "CDEF:ddmarcpass=dmarcpass,UN,0,dmarcpass,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone=$rrd_dmarc:dmarcnone:AVERAGE", "DEF:mdmarcnone=$rrd_dmarc:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone,60,*", "CDEF:ddmarcnone=dmarcnone,UN,0,dmarcnone,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail=$rrd_dmarc:dmarcfail:AVERAGE", "DEF:mdmarcfail=$rrd_dmarc:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail,60,*", "CDEF:ddmarcfail=dmarcfail,UN,0,dmarcfail,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred=$rrd_queue:deferred:AVERAGE", "AREA:deferred#$color{deferred}:Deferred ", 'GPRINT:deferred:MAX:total\: %15.0lf msgs', 'GPRINT:deferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:deferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active=$rrd_queue:active:AVERAGE", "LINE2:active#$color{active}:Active+Incoming+Maildrop", 'GPRINT:active:MAX:total\: %15.0lf msgs', 'GPRINT:active:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:active:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für mx11.dmz.nausch.org</h1>\n"; print "<h3>(<a href='$url'>Summen-</a> und Einzelaufstellungen "; #print "<a href='$url'>MX11</a>, "; print "<a href='$url2'>MX12</a>, "; print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h4>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; rint "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-i$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_smtpd.png"; graph_smtpd($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
/usr/share/mailgraph-ng/mx12/mailgraph.cgi
# vim /usr/share/mailgraph-ng/mx12/mailgraph.cgi
- /usr/share/mailgraph-ng/mx12/mailgraph.cgi
!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd = '/var/lib/mailgraph/mx12/mailgraph.rrd'; my $rrd_virus = '/var/lib/mailgraph/mx12/mailgraph_virus.rrd'; my $rrd_grey = '/var/lib/mailgraph/mx12/mailgraph_grey.rrd'; my $rrd_dane = '/var/lib/mailgraph/mx12/mailgraph_dane.rrd'; my $rrd_dmarc = '/var/lib/mailgraph/mx12/mailgraph_dmarc.rrd'; my $rrd_smtpd = '/var/lib/mailgraph/mx12/mailgraph_smtpd.rrd'; my $rrd_queue = '/var/lib/mailgraph/mx12/mailqueues.rrd'; my $rrd_post = '/var/lib/mailgraph/mx12/mailgraph_post.rrd'; my $tmp_dir = '/var/cache/mailgraph'; my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'E1FFC1', # psbl => 'EBBAD5', passold => 'BAFF70', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => '468700', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', untrustedtls => 'ffebd1', anonymoustls => 'ffcf90', trustedtls => 'ffb24f', verifiedtls => 'ff5800', untrustedtlsin => 'ddd1ff', anonymoustlsin => 'a8a8ff', trustedtlsin => '6767ff', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "AREA:rrejected#$color{rejected}:Rejected ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "AREA:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "LINE2:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new=$rrd_grey:new:AVERAGE", "DEF:mnew=$rrd_grey:new:MAX", "CDEF:rnew=new,60,*", "CDEF:rmnew=mnew,60,*", "CDEF:dnew=new,UN,0,new,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok=$rrd_grey:reconnectok:AVERAGE", "DEF:mreconnectok=$rrd_grey:reconnectok:MAX", "CDEF:rreconnectok=reconnectok,60,*", "CDEF:dreconnectok=reconnectok,UN,0,reconnectok,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted=$rrd_grey:greylisted:AVERAGE", "DEF:mgreylisted=$rrd_grey:greylisted:MAX", "CDEF:rgreylisted=greylisted,60,*", "CDEF:rmgreylisted=mgreylisted,60,*", "CDEF:dgreylisted=greylisted,UN,0,greylisted,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed=$rrd_grey:delayed:AVERAGE", "DEF:mdelayed=$rrd_grey:delayed:MAX", "CDEF:rdelayed=delayed,60,*", "CDEF:rmdelayed=mdelayed,60,*", "CDEF:ddelayed=delayed,UN,0,delayed,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist=$rrd_grey:whitelist:AVERAGE", "DEF:mwhitelist=$rrd_grey:whitelist:MAX", "CDEF:rwhitelist=whitelist,60,*", "CDEF:rmwhitelist=mwhitelist,60,*", "CDEF:dwhitelist=whitelist,UN,0,whitelist,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl=$rrd_grey:awl:AVERAGE", "DEF:mawl=$rrd_grey:awl:MAX", "CDEF:rawl=awl,60,*", "CDEF:dawl=awl,UN,0,awl,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early=$rrd_grey:early:AVERAGE", "DEF:mearly=$rrd_grey:early:MAX", "CDEF:rearly=early,60,*", "CDEF:rmearly=mearly,60,*", "CDEF:dearly=early,UN,0,early,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl=$rrd_post:pswl:AVERAGE", "DEF:mpswl=$rrd_post:pswl:MAX", "CDEF:rpswl=pswl,60,*", "CDEF:rmpswl=mpswl,60,*", "CDEF:dpswl=pswl,UN,0,pswl,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold=$rrd_post:passold:AVERAGE", "DEF:mpassold=$rrd_post:passold:MAX", "CDEF:rpassold=passold,60,*", "CDEF:dpassold=passold,UN,0,passold,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold,60,*", "STACK:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew=$rrd_post:passnew:AVERAGE", "DEF:mpassnew=$rrd_post:passnew:MAX", "CDEF:rpassnew=passnew,60,*", "CDEF:dpassnew=passnew,UN,0,passnew,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl=$rrd_post:psbl:AVERAGE", "DEF:mpsbl=$rrd_post:psbl:MAX", "CDEF:rpsbl=psbl,60,*", "CDEF:dpsbl=psbl,UN,0,psbl,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto=$rrd_post:veto:AVERAGE", "DEF:mveto=$rrd_post:veto:MAX", "CDEF:rveto=veto,60,*", "CDEF:dveto=veto,UN,0,veto,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet=$rrd_post:pregreet:AVERAGE", "DEF:mpregreet=$rrd_post:pregreet:MAX", "CDEF:rpregreet=pregreet,60,*", "CDEF:dpregreet=pregreet,UN,0,pregreet,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl=$rrd_post:dnsbl:AVERAGE", "DEF:mdnsbl=$rrd_post:dnsbl:MAX", "CDEF:rdnsbl=dnsbl,60,*", "CDEF:ddnsbl=dnsbl,UN,0,dnsbl,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining=$rrd_post:pipelining:AVERAGE", "DEF:mpipelining=$rrd_post:pipelining:MAX", "CDEF:rpipelining=pipelining,60,*", "CDEF:dpipelining=pipelining,UN,0,pipelining,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp=$rrd_post:nonsmtp:AVERAGE", "DEF:mnonsmtp=$rrd_post:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp,60,*", "CDEF:dnonsmtp=nonsmtp,UN,0,nonsmtp,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline=$rrd_post:barenewline:AVERAGE", "DEF:mbarenewline=$rrd_post:barenewline:MAX", "CDEF:rbarenewline=barenewline,60,*", "CDEF:dbarenewline=barenewline,UN,0,barenewline,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command=$rrd_post:command:AVERAGE", "DEF:mcommand=$rrd_post:command:MAX", "CDEF:rcommand=command,60,*", "CDEF:dcommand=command,UN,0,command,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup=$rrd_post:hangup:AVERAGE", "DEF:mhangup=$rrd_post:hangup:MAX", "CDEF:rhangup=hangup,60,*", "CDEF:dhangup=hangup,UN,0,hangup,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtls=$rrd_dane:untrustedtls:AVERAGE", "DEF:muntrustedtls=$rrd_dane:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls,60,*", "CDEF:duntrustedtls=untrustedtls,UN,0,untrustedtls,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls,60,*", "AREA:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustls=$rrd_dane:anonymoustls:AVERAGE", "DEF:manonymoustls=$rrd_dane:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls,60,*", "CDEF:danonymoustls=anonymoustls,UN,0,anonymoustls,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls,60,*", "STACK:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls=$rrd_dane:trustedtls:AVERAGE", "DEF:mtrustedtls=$rrd_dane:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls,60,*", "CDEF:dtrustedtls=trustedtls,UN,0,trustedtls,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls=$rrd_dane:verifiedtls:AVERAGE", "DEF:mverifiedtls=$rrd_dane:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls,60,*", "CDEF:dverifiedtls=verifiedtls,UN,0,verifiedtls,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_smtpd($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtlsin=$rrd_smtpd:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin=$rrd_smtpd:untrustedtlsin:MAX", "CDEF:runtrustedtlsin=untrustedtlsin,60,*", "CDEF:duntrustedtlsin=untrustedtlsin,UN,0,untrustedtlsin,IF,$step,*", "CDEF:suntrustedtlsin=PREV,UN,duntrustedtlsin,PREV,IF,duntrustedtlsin,+", "CDEF:rmuntrustedtlsin=muntrustedtlsin,60,*", "AREA:runtrustedtlsin#$color{untrustedtlsin}:IN Untrusted TLS ", 'GPRINT:suntrustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustlsin=$rrd_smtpd:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin=$rrd_smtpd:anonymoustlsin:MAX", "CDEF:ranonymoustlsin=anonymoustlsin,60,*", "CDEF:danonymoustlsin=anonymoustlsin,UN,0,anonymoustlsin,IF,$step,*", "CDEF:sanonymoustlsin=PREV,UN,danonymoustlsin,PREV,IF,danonymoustlsin,+", "CDEF:rmanonymoustlsin=manonymoustlsin,60,*", "STACK:ranonymoustlsin#$color{anonymoustlsin}:IN Anonymous TLS ", 'GPRINT:sanonymoustlsin:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtlsin=$rrd_smtpd:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin=$rrd_smtpd:trustedtlsin:MAX", "CDEF:rtrustedtlsin=trustedtlsin,60,*", "CDEF:dtrustedtlsin=trustedtlsin,UN,0,trustedtlsin,IF,$step,*", "CDEF:strustedtlsin=PREV,UN,dtrustedtlsin,PREV,IF,dtrustedtlsin,+", "CDEF:rmtrustedtlsin=mtrustedtlsin,60,*", "STACK:rtrustedtlsin#$color{trustedtlsin}:In Trusted TLS ", 'GPRINT:strustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtlsin:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass=$rrd_dmarc:spfpass:AVERAGE", "DEF:mspfpass=$rrd_dmarc:spfpass:MAX", "CDEF:rspfpass=spfpass,60,*", "CDEF:dspfpass=spfpass,UN,0,spfpass,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone=$rrd_dmarc:spfnone:AVERAGE", "DEF:mspfnone=$rrd_dmarc:spfnone:MAX", "CDEF:rspfnone=spfnone,60,*", "CDEF:dspfnone=spfnone,UN,0,spfnone,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail=$rrd_dmarc:spffail:AVERAGE", "DEF:mspffail=$rrd_dmarc:spffail:MAX", "CDEF:rspffail=spffail,60,*", "CDEF:dspffail=spffail,UN,0,spffail,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass=$rrd_dmarc:dkimpass:AVERAGE", "DEF:mdkimpass=$rrd_dmarc:dkimpass:MAX", "CDEF:rdkimpass=dkimpass,60,*", "CDEF:ddkimpass=dkimpass,UN,0,dkimpass,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone=$rrd_dmarc:dkimnone:AVERAGE", "DEF:mdkimnone=$rrd_dmarc:dkimnone:MAX", "CDEF:rdkimnone=dkimnone,60,*", "CDEF:ddkimnone=dkimnone,UN,0,dkimnone,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail=$rrd_dmarc:dkimfail:AVERAGE", "DEF:mdkimfail=$rrd_dmarc:dkimfail:MAX", "CDEF:rdkimfail=dkimfail,60,*", "CDEF:ddkimfail=dkimfail,UN,0,dkimfail,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass=$rrd_dmarc:dmarcpass:AVERAGE", "DEF:mdmarcpass=$rrd_dmarc:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass,60,*", "CDEF:ddmarcpass=dmarcpass,UN,0,dmarcpass,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone=$rrd_dmarc:dmarcnone:AVERAGE", "DEF:mdmarcnone=$rrd_dmarc:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone,60,*", "CDEF:ddmarcnone=dmarcnone,UN,0,dmarcnone,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail=$rrd_dmarc:dmarcfail:AVERAGE", "DEF:mdmarcfail=$rrd_dmarc:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail,60,*", "CDEF:ddmarcfail=dmarcfail,UN,0,dmarcfail,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred=$rrd_queue:deferred:AVERAGE", "AREA:deferred#$color{deferred}:Deferred ", 'GPRINT:deferred:MAX:total\: %15.0lf msgs', 'GPRINT:deferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:deferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active=$rrd_queue:active:AVERAGE", "LINE2:active#$color{active}:Active+Incoming+Maildrop", 'GPRINT:active:MAX:total\: %15.0lf msgs', 'GPRINT:active:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:active:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für mx12.dmz.nausch.org</h1>\n"; print "<h3>(<a href='$url'>Summen-</a> und Einzelaufstellungen "; print "<a href='$url1'>MX11</a>, "; #print "<a href='$url2'>MX12</a>, "; print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h4>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-i$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_smtpd.png"; graph_smtpd($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
/usr/share/mailgraph-ng/mx13/mailgraph.cgi
# vim /usr/share/mailgraph-ng/mx13/mailgraph.cgi
- /usr/share/mailgraph-ng/mx13/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd = '/var/lib/mailgraph/mx13/mailgraph.rrd'; my $rrd_virus = '/var/lib/mailgraph/mx13/mailgraph_virus.rrd'; my $rrd_grey = '/var/lib/mailgraph/mx13/mailgraph_grey.rrd'; my $rrd_dane = '/var/lib/mailgraph/mx13/mailgraph_dane.rrd'; my $rrd_dmarc = '/var/lib/mailgraph/mx13/mailgraph_dmarc.rrd'; my $rrd_smtpd = '/var/lib/mailgraph/mx13/mailgraph_smtpd.rrd'; my $rrd_queue = '/var/lib/mailgraph/mx13/mailqueues.rrd'; my $rrd_post = '/var/lib/mailgraph/mx13/mailgraph_post.rrd'; my $tmp_dir = '/var/cache/mailgraph'; my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'E1FFC1', # psbl => 'EBBAD5', passold => 'BAFF70', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => '468700', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', untrustedtls => 'ffebd1', anonymoustls => 'ffcf90', trustedtls => 'ffb24f', verifiedtls => 'ff5800', untrustedtlsin => 'ddd1ff', anonymoustlsin => 'a8a8ff', trustedtlsin => '6767ff', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "AREA:rrejected#$color{rejected}:Rejected ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "AREA:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "LINE2:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new=$rrd_grey:new:AVERAGE", "DEF:mnew=$rrd_grey:new:MAX", "CDEF:rnew=new,60,*", "CDEF:rmnew=mnew,60,*", "CDEF:dnew=new,UN,0,new,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok=$rrd_grey:reconnectok:AVERAGE", "DEF:mreconnectok=$rrd_grey:reconnectok:MAX", "CDEF:rreconnectok=reconnectok,60,*", "CDEF:dreconnectok=reconnectok,UN,0,reconnectok,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted=$rrd_grey:greylisted:AVERAGE", "DEF:mgreylisted=$rrd_grey:greylisted:MAX", "CDEF:rgreylisted=greylisted,60,*", "CDEF:rmgreylisted=mgreylisted,60,*", "CDEF:dgreylisted=greylisted,UN,0,greylisted,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed=$rrd_grey:delayed:AVERAGE", "DEF:mdelayed=$rrd_grey:delayed:MAX", "CDEF:rdelayed=delayed,60,*", "CDEF:rmdelayed=mdelayed,60,*", "CDEF:ddelayed=delayed,UN,0,delayed,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist=$rrd_grey:whitelist:AVERAGE", "DEF:mwhitelist=$rrd_grey:whitelist:MAX", "CDEF:rwhitelist=whitelist,60,*", "CDEF:rmwhitelist=mwhitelist,60,*", "CDEF:dwhitelist=whitelist,UN,0,whitelist,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl=$rrd_grey:awl:AVERAGE", "DEF:mawl=$rrd_grey:awl:MAX", "CDEF:rawl=awl,60,*", "CDEF:dawl=awl,UN,0,awl,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early=$rrd_grey:early:AVERAGE", "DEF:mearly=$rrd_grey:early:MAX", "CDEF:rearly=early,60,*", "CDEF:rmearly=mearly,60,*", "CDEF:dearly=early,UN,0,early,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl=$rrd_post:pswl:AVERAGE", "DEF:mpswl=$rrd_post:pswl:MAX", "CDEF:rpswl=pswl,60,*", "CDEF:rmpswl=mpswl,60,*", "CDEF:dpswl=pswl,UN,0,pswl,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold=$rrd_post:passold:AVERAGE", "DEF:mpassold=$rrd_post:passold:MAX", "CDEF:rpassold=passold,60,*", "CDEF:dpassold=passold,UN,0,passold,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold,60,*", "STACK:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew=$rrd_post:passnew:AVERAGE", "DEF:mpassnew=$rrd_post:passnew:MAX", "CDEF:rpassnew=passnew,60,*", "CDEF:dpassnew=passnew,UN,0,passnew,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl=$rrd_post:psbl:AVERAGE", "DEF:mpsbl=$rrd_post:psbl:MAX", "CDEF:rpsbl=psbl,60,*", "CDEF:dpsbl=psbl,UN,0,psbl,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto=$rrd_post:veto:AVERAGE", "DEF:mveto=$rrd_post:veto:MAX", "CDEF:rveto=veto,60,*", "CDEF:dveto=veto,UN,0,veto,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet=$rrd_post:pregreet:AVERAGE", "DEF:mpregreet=$rrd_post:pregreet:MAX", "CDEF:rpregreet=pregreet,60,*", "CDEF:dpregreet=pregreet,UN,0,pregreet,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl=$rrd_post:dnsbl:AVERAGE", "DEF:mdnsbl=$rrd_post:dnsbl:MAX", "CDEF:rdnsbl=dnsbl,60,*", "CDEF:ddnsbl=dnsbl,UN,0,dnsbl,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining=$rrd_post:pipelining:AVERAGE", "DEF:mpipelining=$rrd_post:pipelining:MAX", "CDEF:rpipelining=pipelining,60,*", "CDEF:dpipelining=pipelining,UN,0,pipelining,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp=$rrd_post:nonsmtp:AVERAGE", "DEF:mnonsmtp=$rrd_post:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp,60,*", "CDEF:dnonsmtp=nonsmtp,UN,0,nonsmtp,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline=$rrd_post:barenewline:AVERAGE", "DEF:mbarenewline=$rrd_post:barenewline:MAX", "CDEF:rbarenewline=barenewline,60,*", "CDEF:dbarenewline=barenewline,UN,0,barenewline,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command=$rrd_post:command:AVERAGE", "DEF:mcommand=$rrd_post:command:MAX", "CDEF:rcommand=command,60,*", "CDEF:dcommand=command,UN,0,command,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup=$rrd_post:hangup:AVERAGE", "DEF:mhangup=$rrd_post:hangup:MAX", "CDEF:rhangup=hangup,60,*", "CDEF:dhangup=hangup,UN,0,hangup,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtls=$rrd_dane:untrustedtls:AVERAGE", "DEF:muntrustedtls=$rrd_dane:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls,60,*", "CDEF:duntrustedtls=untrustedtls,UN,0,untrustedtls,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls,60,*", "AREA:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustls=$rrd_dane:anonymoustls:AVERAGE", "DEF:manonymoustls=$rrd_dane:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls,60,*", "CDEF:danonymoustls=anonymoustls,UN,0,anonymoustls,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls,60,*", "STACK:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls=$rrd_dane:trustedtls:AVERAGE", "DEF:mtrustedtls=$rrd_dane:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls,60,*", "CDEF:dtrustedtls=trustedtls,UN,0,trustedtls,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls=$rrd_dane:verifiedtls:AVERAGE", "DEF:mverifiedtls=$rrd_dane:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls,60,*", "CDEF:dverifiedtls=verifiedtls,UN,0,verifiedtls,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_smtpd($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtlsin=$rrd_smtpd:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin=$rrd_smtpd:untrustedtlsin:MAX", "CDEF:runtrustedtlsin=untrustedtlsin,60,*", "CDEF:duntrustedtlsin=untrustedtlsin,UN,0,untrustedtlsin,IF,$step,*", "CDEF:suntrustedtlsin=PREV,UN,duntrustedtlsin,PREV,IF,duntrustedtlsin,+", "CDEF:rmuntrustedtlsin=muntrustedtlsin,60,*", "AREA:runtrustedtlsin#$color{untrustedtlsin}:IN Untrusted TLS ", 'GPRINT:suntrustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustlsin=$rrd_smtpd:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin=$rrd_smtpd:anonymoustlsin:MAX", "CDEF:ranonymoustlsin=anonymoustlsin,60,*", "CDEF:danonymoustlsin=anonymoustlsin,UN,0,anonymoustlsin,IF,$step,*", "CDEF:sanonymoustlsin=PREV,UN,danonymoustlsin,PREV,IF,danonymoustlsin,+", "CDEF:rmanonymoustlsin=manonymoustlsin,60,*", "STACK:ranonymoustlsin#$color{anonymoustlsin}:IN Anonymous TLS ", 'GPRINT:sanonymoustlsin:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtlsin=$rrd_smtpd:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin=$rrd_smtpd:trustedtlsin:MAX", "CDEF:rtrustedtlsin=trustedtlsin,60,*", "CDEF:dtrustedtlsin=trustedtlsin,UN,0,trustedtlsin,IF,$step,*", "CDEF:strustedtlsin=PREV,UN,dtrustedtlsin,PREV,IF,dtrustedtlsin,+", "CDEF:rmtrustedtlsin=mtrustedtlsin,60,*", "STACK:rtrustedtlsin#$color{trustedtlsin}:In Trusted TLS ", 'GPRINT:strustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtlsin:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass=$rrd_dmarc:spfpass:AVERAGE", "DEF:mspfpass=$rrd_dmarc:spfpass:MAX", "CDEF:rspfpass=spfpass,60,*", "CDEF:dspfpass=spfpass,UN,0,spfpass,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone=$rrd_dmarc:spfnone:AVERAGE", "DEF:mspfnone=$rrd_dmarc:spfnone:MAX", "CDEF:rspfnone=spfnone,60,*", "CDEF:dspfnone=spfnone,UN,0,spfnone,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail=$rrd_dmarc:spffail:AVERAGE", "DEF:mspffail=$rrd_dmarc:spffail:MAX", "CDEF:rspffail=spffail,60,*", "CDEF:dspffail=spffail,UN,0,spffail,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass=$rrd_dmarc:dkimpass:AVERAGE", "DEF:mdkimpass=$rrd_dmarc:dkimpass:MAX", "CDEF:rdkimpass=dkimpass,60,*", "CDEF:ddkimpass=dkimpass,UN,0,dkimpass,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone=$rrd_dmarc:dkimnone:AVERAGE", "DEF:mdkimnone=$rrd_dmarc:dkimnone:MAX", "CDEF:rdkimnone=dkimnone,60,*", "CDEF:ddkimnone=dkimnone,UN,0,dkimnone,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail=$rrd_dmarc:dkimfail:AVERAGE", "DEF:mdkimfail=$rrd_dmarc:dkimfail:MAX", "CDEF:rdkimfail=dkimfail,60,*", "CDEF:ddkimfail=dkimfail,UN,0,dkimfail,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass=$rrd_dmarc:dmarcpass:AVERAGE", "DEF:mdmarcpass=$rrd_dmarc:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass,60,*", "CDEF:ddmarcpass=dmarcpass,UN,0,dmarcpass,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone=$rrd_dmarc:dmarcnone:AVERAGE", "DEF:mdmarcnone=$rrd_dmarc:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone,60,*", "CDEF:ddmarcnone=dmarcnone,UN,0,dmarcnone,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail=$rrd_dmarc:dmarcfail:AVERAGE", "DEF:mdmarcfail=$rrd_dmarc:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail,60,*", "CDEF:ddmarcfail=dmarcfail,UN,0,dmarcfail,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred=$rrd_queue:deferred:AVERAGE", "AREA:deferred#$color{deferred}:Deferred ", 'GPRINT:deferred:MAX:total\: %15.0lf msgs', 'GPRINT:deferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:deferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active=$rrd_queue:active:AVERAGE", "LINE2:active#$color{active}:Active+Incoming+Maildrop", 'GPRINT:active:MAX:total\: %15.0lf msgs', 'GPRINT:active:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:active:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für mx13.dmz.nausch.org</h1>\n"; print "<h3>(<a href='$url'>Summen-</a> und Einzelaufstellungen "; print "<a href='$url1'>MX11</a>, "; print "<a href='$url2'>MX12</a> und "; #print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h4>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-i$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_smtpd.png"; graph_smtpd($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
/usr/share/mailgraph-ng/mx14/mailgraph.cgi
# vim /usr/share/mailgraph-ng/mx14/mailgraph.cgi
- /usr/share/mailgraph-ng/mx14/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd = '/var/lib/mailgraph/mx14/mailgraph.rrd'; my $rrd_virus = '/var/lib/mailgraph/mx14/mailgraph_virus.rrd'; my $rrd_grey = '/var/lib/mailgraph/mx14/mailgraph_grey.rrd'; my $rrd_dane = '/var/lib/mailgraph/mx14/mailgraph_dane.rrd'; my $rrd_dmarc = '/var/lib/mailgraph/mx14/mailgraph_dmarc.rrd'; my $rrd_smtpd = '/var/lib/mailgraph/mx14/mailgraph_smtpd.rrd'; my $rrd_queue = '/var/lib/mailgraph/mx14/mailqueues.rrd'; my $rrd_post = '/var/lib/mailgraph/mx14/mailgraph_post.rrd'; my $tmp_dir = '/var/cache/mailgraph'; my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'E1FFC1', # psbl => 'EBBAD5', passold => 'BAFF70', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => '468700', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', untrustedtls => 'ffebd1', anonymoustls => 'ffcf90', trustedtls => 'ffb24f', verifiedtls => 'ff5800', untrustedtlsin => 'ddd1ff', anonymoustlsin => 'a8a8ff', trustedtlsin => '6767ff', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent=$rrd:sent:AVERAGE", "DEF:msent=$rrd:sent:MAX", "CDEF:rsent=sent,60,*", "CDEF:rmsent=msent,60,*", "CDEF:dsent=sent,UN,0,sent,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv=$rrd:recv:AVERAGE", "DEF:mrecv=$rrd:recv:MAX", "CDEF:rrecv=recv,60,*", "CDEF:rmrecv=mrecv,60,*", "CDEF:drecv=recv,UN,0,recv,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX", "CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected,60,*", "AREA:rrejected#$color{rejected}:Rejected ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', "DEF:virus=$rrd_virus:virus:AVERAGE", "DEF:mvirus=$rrd_virus:virus:MAX", "CDEF:rvirus=virus,60,*", "CDEF:dvirus=virus,UN,0,virus,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus,60,*", "AREA:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam=$rrd_virus:spam:AVERAGE", "DEF:mspam=$rrd_virus:spam:MAX", "CDEF:rspam=spam,60,*", "CDEF:dspam=spam,UN,0,spam,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "CDEF:rbounced=bounced,60,*", "CDEF:dbounced=bounced,UN,0,bounced,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced,60,*", "LINE2:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new=$rrd_grey:new:AVERAGE", "DEF:mnew=$rrd_grey:new:MAX", "CDEF:rnew=new,60,*", "CDEF:rmnew=mnew,60,*", "CDEF:dnew=new,UN,0,new,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok=$rrd_grey:reconnectok:AVERAGE", "DEF:mreconnectok=$rrd_grey:reconnectok:MAX", "CDEF:rreconnectok=reconnectok,60,*", "CDEF:dreconnectok=reconnectok,UN,0,reconnectok,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted=$rrd_grey:greylisted:AVERAGE", "DEF:mgreylisted=$rrd_grey:greylisted:MAX", "CDEF:rgreylisted=greylisted,60,*", "CDEF:rmgreylisted=mgreylisted,60,*", "CDEF:dgreylisted=greylisted,UN,0,greylisted,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed=$rrd_grey:delayed:AVERAGE", "DEF:mdelayed=$rrd_grey:delayed:MAX", "CDEF:rdelayed=delayed,60,*", "CDEF:rmdelayed=mdelayed,60,*", "CDEF:ddelayed=delayed,UN,0,delayed,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist=$rrd_grey:whitelist:AVERAGE", "DEF:mwhitelist=$rrd_grey:whitelist:MAX", "CDEF:rwhitelist=whitelist,60,*", "CDEF:rmwhitelist=mwhitelist,60,*", "CDEF:dwhitelist=whitelist,UN,0,whitelist,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl=$rrd_grey:awl:AVERAGE", "DEF:mawl=$rrd_grey:awl:MAX", "CDEF:rawl=awl,60,*", "CDEF:dawl=awl,UN,0,awl,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early=$rrd_grey:early:AVERAGE", "DEF:mearly=$rrd_grey:early:MAX", "CDEF:rearly=early,60,*", "CDEF:rmearly=mearly,60,*", "CDEF:dearly=early,UN,0,early,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl=$rrd_post:pswl:AVERAGE", "DEF:mpswl=$rrd_post:pswl:MAX", "CDEF:rpswl=pswl,60,*", "CDEF:rmpswl=mpswl,60,*", "CDEF:dpswl=pswl,UN,0,pswl,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold=$rrd_post:passold:AVERAGE", "DEF:mpassold=$rrd_post:passold:MAX", "CDEF:rpassold=passold,60,*", "CDEF:dpassold=passold,UN,0,passold,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold,60,*", "STACK:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew=$rrd_post:passnew:AVERAGE", "DEF:mpassnew=$rrd_post:passnew:MAX", "CDEF:rpassnew=passnew,60,*", "CDEF:dpassnew=passnew,UN,0,passnew,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl=$rrd_post:psbl:AVERAGE", "DEF:mpsbl=$rrd_post:psbl:MAX", "CDEF:rpsbl=psbl,60,*", "CDEF:dpsbl=psbl,UN,0,psbl,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto=$rrd_post:veto:AVERAGE", "DEF:mveto=$rrd_post:veto:MAX", "CDEF:rveto=veto,60,*", "CDEF:dveto=veto,UN,0,veto,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet=$rrd_post:pregreet:AVERAGE", "DEF:mpregreet=$rrd_post:pregreet:MAX", "CDEF:rpregreet=pregreet,60,*", "CDEF:dpregreet=pregreet,UN,0,pregreet,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl=$rrd_post:dnsbl:AVERAGE", "DEF:mdnsbl=$rrd_post:dnsbl:MAX", "CDEF:rdnsbl=dnsbl,60,*", "CDEF:ddnsbl=dnsbl,UN,0,dnsbl,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining=$rrd_post:pipelining:AVERAGE", "DEF:mpipelining=$rrd_post:pipelining:MAX", "CDEF:rpipelining=pipelining,60,*", "CDEF:dpipelining=pipelining,UN,0,pipelining,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp=$rrd_post:nonsmtp:AVERAGE", "DEF:mnonsmtp=$rrd_post:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp,60,*", "CDEF:dnonsmtp=nonsmtp,UN,0,nonsmtp,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline=$rrd_post:barenewline:AVERAGE", "DEF:mbarenewline=$rrd_post:barenewline:MAX", "CDEF:rbarenewline=barenewline,60,*", "CDEF:dbarenewline=barenewline,UN,0,barenewline,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command=$rrd_post:command:AVERAGE", "DEF:mcommand=$rrd_post:command:MAX", "CDEF:rcommand=command,60,*", "CDEF:dcommand=command,UN,0,command,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup=$rrd_post:hangup:AVERAGE", "DEF:mhangup=$rrd_post:hangup:MAX", "CDEF:rhangup=hangup,60,*", "CDEF:dhangup=hangup,UN,0,hangup,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtls=$rrd_dane:untrustedtls:AVERAGE", "DEF:muntrustedtls=$rrd_dane:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls,60,*", "CDEF:duntrustedtls=untrustedtls,UN,0,untrustedtls,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls,60,*", "AREA:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustls=$rrd_dane:anonymoustls:AVERAGE", "DEF:manonymoustls=$rrd_dane:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls,60,*", "CDEF:danonymoustls=anonymoustls,UN,0,anonymoustls,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls,60,*", "STACK:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls=$rrd_dane:trustedtls:AVERAGE", "DEF:mtrustedtls=$rrd_dane:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls,60,*", "CDEF:dtrustedtls=trustedtls,UN,0,trustedtls,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls=$rrd_dane:verifiedtls:AVERAGE", "DEF:mverifiedtls=$rrd_dane:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls,60,*", "CDEF:dverifiedtls=verifiedtls,UN,0,verifiedtls,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_smtpd($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtlsin=$rrd_smtpd:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin=$rrd_smtpd:untrustedtlsin:MAX", "CDEF:runtrustedtlsin=untrustedtlsin,60,*", "CDEF:duntrustedtlsin=untrustedtlsin,UN,0,untrustedtlsin,IF,$step,*", "CDEF:suntrustedtlsin=PREV,UN,duntrustedtlsin,PREV,IF,duntrustedtlsin,+", "CDEF:rmuntrustedtlsin=muntrustedtlsin,60,*", "AREA:runtrustedtlsin#$color{untrustedtlsin}:IN Untrusted TLS ", 'GPRINT:suntrustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustlsin=$rrd_smtpd:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin=$rrd_smtpd:anonymoustlsin:MAX", "CDEF:ranonymoustlsin=anonymoustlsin,60,*", "CDEF:danonymoustlsin=anonymoustlsin,UN,0,anonymoustlsin,IF,$step,*", "CDEF:sanonymoustlsin=PREV,UN,danonymoustlsin,PREV,IF,danonymoustlsin,+", "CDEF:rmanonymoustlsin=manonymoustlsin,60,*", "STACK:ranonymoustlsin#$color{anonymoustlsin}:IN Anonymous TLS ", 'GPRINT:sanonymoustlsin:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtlsin=$rrd_smtpd:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin=$rrd_smtpd:trustedtlsin:MAX", "CDEF:rtrustedtlsin=trustedtlsin,60,*", "CDEF:dtrustedtlsin=trustedtlsin,UN,0,trustedtlsin,IF,$step,*", "CDEF:strustedtlsin=PREV,UN,dtrustedtlsin,PREV,IF,dtrustedtlsin,+", "CDEF:rmtrustedtlsin=mtrustedtlsin,60,*", "STACK:rtrustedtlsin#$color{trustedtlsin}:In Trusted TLS ", 'GPRINT:strustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtlsin:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass=$rrd_dmarc:spfpass:AVERAGE", "DEF:mspfpass=$rrd_dmarc:spfpass:MAX", "CDEF:rspfpass=spfpass,60,*", "CDEF:dspfpass=spfpass,UN,0,spfpass,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone=$rrd_dmarc:spfnone:AVERAGE", "DEF:mspfnone=$rrd_dmarc:spfnone:MAX", "CDEF:rspfnone=spfnone,60,*", "CDEF:dspfnone=spfnone,UN,0,spfnone,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail=$rrd_dmarc:spffail:AVERAGE", "DEF:mspffail=$rrd_dmarc:spffail:MAX", "CDEF:rspffail=spffail,60,*", "CDEF:dspffail=spffail,UN,0,spffail,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass=$rrd_dmarc:dkimpass:AVERAGE", "DEF:mdkimpass=$rrd_dmarc:dkimpass:MAX", "CDEF:rdkimpass=dkimpass,60,*", "CDEF:ddkimpass=dkimpass,UN,0,dkimpass,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone=$rrd_dmarc:dkimnone:AVERAGE", "DEF:mdkimnone=$rrd_dmarc:dkimnone:MAX", "CDEF:rdkimnone=dkimnone,60,*", "CDEF:ddkimnone=dkimnone,UN,0,dkimnone,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail=$rrd_dmarc:dkimfail:AVERAGE", "DEF:mdkimfail=$rrd_dmarc:dkimfail:MAX", "CDEF:rdkimfail=dkimfail,60,*", "CDEF:ddkimfail=dkimfail,UN,0,dkimfail,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass=$rrd_dmarc:dmarcpass:AVERAGE", "DEF:mdmarcpass=$rrd_dmarc:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass,60,*", "CDEF:ddmarcpass=dmarcpass,UN,0,dmarcpass,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone=$rrd_dmarc:dmarcnone:AVERAGE", "DEF:mdmarcnone=$rrd_dmarc:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone,60,*", "CDEF:ddmarcnone=dmarcnone,UN,0,dmarcnone,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail=$rrd_dmarc:dmarcfail:AVERAGE", "DEF:mdmarcfail=$rrd_dmarc:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail,60,*", "CDEF:ddmarcfail=dmarcfail,UN,0,dmarcfail,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred=$rrd_queue:deferred:AVERAGE", "AREA:deferred#$color{deferred}:Deferred ", 'GPRINT:deferred:MAX:total\: %15.0lf msgs', 'GPRINT:deferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:deferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active=$rrd_queue:active:AVERAGE", "LINE2:active#$color{active}:Active+Incoming+Maildrop", 'GPRINT:active:MAX:total\: %15.0lf msgs', 'GPRINT:active:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:active:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für mx14.dmz.nausch.org</h1>\n"; print "<h3>(<a href='$url'>Summen-</a> und Einzelaufstellungen "; print "<a href='$url1'>MX11</a>, "; print "<a href='$url2'>MX12</a> und "; print "<a href='$url3'>MX13</a>)</h4>\n"; #print "<a href='$url4'>MX14</a></a>)</h4>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$urlg$n'>Summe</a>\n"; print "<a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-i$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_smtpd.png"; graph_smtpd($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
CGI-Erweiterung für die Σ-Seite
Wie auch schon bei den Einzelstatistiken sind bei der Σ-Seite, die die kumulierten Einzelgraphen enthält, umfangreichere Änderungen/Erweiterung des Originalscripts notwendig.
Datenquellen
Da wir bei der Gesamtstatistik die Summe der einzelnen Mailserver zur Anzeige bringen wollen, benötigen wir daher auch alle Datenquellen. Die Anzahl der Variablen $rrd_* erhöht sich daher entsprechend!
... my $rrd_11 = '/var/lib/mailgraph/mx11/mailgraph.rrd'; my $rrd_12 = '/var/lib/mailgraph/mx12/mailgraph.rrd'; my $rrd_13 = '/var/lib/mailgraph/mx13/mailgraph.rrd'; my $rrd_14 = '/var/lib/mailgraph/mx14/mailgraph.rrd'; my $rrd_virus_11 = '/var/lib/mailgraph/mx11/mailgraph_virus.rrd'; my $rrd_virus_12 = '/var/lib/mailgraph/mx12/mailgraph_virus.rrd'; my $rrd_virus_13 = '/var/lib/mailgraph/mx13/mailgraph_virus.rrd'; my $rrd_virus_14 = '/var/lib/mailgraph/mx14/mailgraph_virus.rrd'; my $rrd_grey_11 = '/var/lib/mailgraph/mx11/mailgraph_grey.rrd'; my $rrd_grey_12 = '/var/lib/mailgraph/mx12/mailgraph_grey.rrd'; my $rrd_grey_13 = '/var/lib/mailgraph/mx13/mailgraph_grey.rrd'; my $rrd_grey_14 = '/var/lib/mailgraph/mx14/mailgraph_grey.rrd'; my $rrd_dane_11 = '/var/lib/mailgraph/mx11/mailgraph_dane.rrd'; my $rrd_dane_12 = '/var/lib/mailgraph/mx12/mailgraph_dane.rrd'; my $rrd_dane_13 = '/var/lib/mailgraph/mx13/mailgraph_dane.rrd'; my $rrd_dane_14 = '/var/lib/mailgraph/mx14/mailgraph_dane.rrd'; my $rrd_dmarc_11 = '/var/lib/mailgraph/mx11/mailgraph_dmarc.rrd'; my $rrd_dmarc_12 = '/var/lib/mailgraph/mx12/mailgraph_dmarc.rrd'; my $rrd_dmarc_13 = '/var/lib/mailgraph/mx13/mailgraph_dmarc.rrd'; my $rrd_dmarc_14 = '/var/lib/mailgraph/mx14/mailgraph_dmarc.rrd'; my $rrd_smtpd_11 = '/var/lib/mailgraph/mx11/mailgraph_smtpd.rrd'; my $rrd_smtpd_12 = '/var/lib/mailgraph/mx12/mailgraph_smtpd.rrd'; my $rrd_smtpd_13 = '/var/lib/mailgraph/mx13/mailgraph_smtpd.rrd'; my $rrd_smtpd_14 = '/var/lib/mailgraph/mx14/mailgraph_smtpd.rrd'; my $rrd_queue_11 = '/var/lib/mailgraph/mx11/mailqueues.rrd'; my $rrd_queue_12 = '/var/lib/mailgraph/mx12/mailqueues.rrd'; my $rrd_queue_13 = '/var/lib/mailgraph/mx13/mailqueues.rrd'; my $rrd_queue_14 = '/var/lib/mailgraph/mx14/mailqueues.rrd'; my $rrd_post_11 = '/var/lib/mailgraph/mx11/mailgraph_post.rrd'; my $rrd_post_12 = '/var/lib/mailgraph/mx12/mailgraph_post.rrd'; my $rrd_post_13 = '/var/lib/mailgraph/mx13/mailgraph_post.rrd'; my $rrd_post_14 = '/var/lib/mailgraph/mx14/mailgraph_post.rrd'; ...
URL-Definitionen
Damit die Seite bei Portierungen ohne große Änderungen im Code realisiert werden können, wird für die Querverweise jeweils zugehörige URL's definiert.
... my $url = "http://mailgraph-ng.nausch.org/mx/"; my $urlg = "http://mailgraph-ng.nausch.org/mx/#G"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; ...
Unterprogramme graph_xxx - kumulierte Werte für die Generierung der RRD-Graphiken
Bei der Generierung der kumulierten Graphiken stellt sich grundsätzlich die Frage, wie führt man den Inhalt mehrerer RRD-Dateien zusammen um einen Summengraph zu erstellen. Anstatt nun einen neue RRD-Datei mit dem Inhalt der vier einzelnen RRD-Dateien zu erstellen um daraus dann den Graphen zu generieren, wählen wir einen anderen Weg. Bei der Erstellung eines Summengraphen mit Hilfe von RRD_GRAPH bedienen wir uns nicht nur einer Quelle (RRD-Datei), sondern wir lassen RRD_GRAPH die Daten aus den einzelnen RRD-Dateien zusammenrechnen und erstellen dann den Graphen.
Dazu müssen wir im ersten Schritt alle Datenquellen beim RRD_GRAPH Parameter DEF benennen. Somit wird z.B. beim Graphen mit den nicht angenommenen Nachrichten Rejected statt der beiden Zeilen
"DEF:rejected=$rrd:rejected:AVERAGE", "DEF:mrejected=$rrd:rejected:MAX",
entsprechend acht Zeilen
"DEF:rejected_11=$rrd_11:rejected:AVERAGE", "DEF:mrejected_11=$rrd_11:rejected:MAX", "DEF:rejected_12=$rrd_12:rejected:AVERAGE", "DEF:mrejected_12=$rrd_12:rejected:MAX", "DEF:rejected_13=$rrd_13:rejected:AVERAGE", "DEF:mrejected_13=$rrd_13:rejected:MAX", "DEF:rejected_14=$rrd_14:rejected:AVERAGE", "DEF:mrejected_14=$rrd_14:rejected:MAX",
Bei der Generierung der eigentlichen Graphen ziehen wir die Summe der vier RRD-Dateien heran, egal ob eine einfache Fläche, ein gestapelter Bereich oder ein Liniengraph erzeugt werden soll. Beim RRD_GRAPH Parameter CDEF lassen wir daher die Summe aus den Variablen rejected_11, rejected_12, rejected_13 und rejected_14 berechnen.
Hierzu verwenden wir die RRD_GRAPH eigene Berechnungssyntax : ∑ = (((A + B) + C) + D)
.
Im Falle unseres Graphen über die Abgelehneten Nachrichten (rejected) berechnet sich die Gesamtsumme also wie folgt:
∑ = (((rejected_11 + rejected_12) + rejected_13) + rejected_14)
Daraus wird dann in der RRD_GRAPH eigenen Berechnungssyntax:
∑=rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,
Anstatt der ursprünglichen CDEF-Definitionen
"CDEF:rrejected=rejected,60,*", "CDEF:drejected=rejected,UN,0,rejected,IF,$step,*", "CDEF:rmrejected=mrejected,60,*",
verwenden wir also nachfolgende CDEF-Definitionen:
"CDEF:rrejected=rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,60,*", "CDEF:drejected=rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,UN,0,rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,IF,$step,*", "CDEF:rmrejected=mrejected_11,mrejected_12,+,mrejected_13,+,mrejected_14,+,60,*",
Diese Änderungen tragen wir nun bei den Unterprogrammen graph_xxx nach.
Unterprogramm print_html
Wie auch schon bei den Unterprogrammen print_html der Einzelsysteme, erweitern wir nun noch das Unterprogramm print_html um die links zu den verweisenden Statistikseiten.
... sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für die <br> die Mailserver bei nausch.org</h1>\n"; print "<h3>(kumulierte Werte von "; print "<a href='$url1'>MX11</a>, "; print "<a href='$url2'>MX12</a>, "; print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h3>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } ...
CGI-Script für die ∑-Auswertung
Mit allen Änderungen und Erweiterungen ergibt sich dann folgendes Gesamt-CGI-Script:
# vim /usr/share/mailgraph-ng/mx/mailgraph.cgi
- /usr/share/mailgraph-ng/mx/mailgraph.cgi
#!/usr/bin/perl -w # mailgraph -- detailed postfix mail traffic statistics # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <david@schweikert.ch> # modified 2011 for queuegraph by Ralf Hildebrandt <Ralf.Hildebrandt@computerbeschimpfung.de> # modified 2015 for mailgraph-ng by Django <django@nausch.org> based on # patches from Sebastian van de Meer <kernel-error@kernel-error.de> # released under the GNU General Public License use RRDs; use POSIX qw(uname); my $VERSION = "1.15"; my $host = (POSIX::uname())[1]; my $scriptname = $ENV{"SCRIPT_NAME"}; my $xpoints = 800; my $points_per_sample = 3; my $ypoints = 160; my $rrd_11 = '/var/lib/mailgraph/mx11/mailgraph.rrd'; my $rrd_12 = '/var/lib/mailgraph/mx12/mailgraph.rrd'; my $rrd_13 = '/var/lib/mailgraph/mx13/mailgraph.rrd'; my $rrd_14 = '/var/lib/mailgraph/mx14/mailgraph.rrd'; my $rrd_virus_11 = '/var/lib/mailgraph/mx11/mailgraph_virus.rrd'; my $rrd_virus_12 = '/var/lib/mailgraph/mx12/mailgraph_virus.rrd'; my $rrd_virus_13 = '/var/lib/mailgraph/mx13/mailgraph_virus.rrd'; my $rrd_virus_14 = '/var/lib/mailgraph/mx14/mailgraph_virus.rrd'; my $rrd_grey_11 = '/var/lib/mailgraph/mx11/mailgraph_grey.rrd'; my $rrd_grey_12 = '/var/lib/mailgraph/mx12/mailgraph_grey.rrd'; my $rrd_grey_13 = '/var/lib/mailgraph/mx13/mailgraph_grey.rrd'; my $rrd_grey_14 = '/var/lib/mailgraph/mx14/mailgraph_grey.rrd'; my $rrd_dane_11 = '/var/lib/mailgraph/mx11/mailgraph_dane.rrd'; my $rrd_dane_12 = '/var/lib/mailgraph/mx12/mailgraph_dane.rrd'; my $rrd_dane_13 = '/var/lib/mailgraph/mx13/mailgraph_dane.rrd'; my $rrd_dane_14 = '/var/lib/mailgraph/mx14/mailgraph_dane.rrd'; my $rrd_dmarc_11 = '/var/lib/mailgraph/mx11/mailgraph_dmarc.rrd'; my $rrd_dmarc_12 = '/var/lib/mailgraph/mx12/mailgraph_dmarc.rrd'; my $rrd_dmarc_13 = '/var/lib/mailgraph/mx13/mailgraph_dmarc.rrd'; my $rrd_dmarc_14 = '/var/lib/mailgraph/mx14/mailgraph_dmarc.rrd'; my $rrd_smtpd_11 = '/var/lib/mailgraph/mx11/mailgraph_smtpd.rrd'; my $rrd_smtpd_12 = '/var/lib/mailgraph/mx12/mailgraph_smtpd.rrd'; my $rrd_smtpd_13 = '/var/lib/mailgraph/mx13/mailgraph_smtpd.rrd'; my $rrd_smtpd_14 = '/var/lib/mailgraph/mx14/mailgraph_smtpd.rrd'; my $rrd_queue_11 = '/var/lib/mailgraph/mx11/mailqueues.rrd'; my $rrd_queue_12 = '/var/lib/mailgraph/mx12/mailqueues.rrd'; my $rrd_queue_13 = '/var/lib/mailgraph/mx13/mailqueues.rrd'; my $rrd_queue_14 = '/var/lib/mailgraph/mx14/mailqueues.rrd'; my $rrd_post_11 = '/var/lib/mailgraph/mx11/mailgraph_post.rrd'; my $rrd_post_12 = '/var/lib/mailgraph/mx12/mailgraph_post.rrd'; my $rrd_post_13 = '/var/lib/mailgraph/mx13/mailgraph_post.rrd'; my $rrd_post_14 = '/var/lib/mailgraph/mx14/mailgraph_post.rrd'; my $tmp_dir = '/var/cache/mailgraph'; my @graphs = ( { title => 'Letzter Tag', seconds => 3600*24, }, { title => 'Letzte Woche', seconds => 3600*24*7, }, { title => 'Letzter Monat', seconds => 3600*24*31, }, { title => 'Letztes Jahr', seconds => 3600*24*365, }, ); my %color = ( # rrggbb in hex # n sent => '000099', received => '009900', bounced => '000000', virus => 'DDBB00', spam => '999999', rejected => 'AA0000', greylisted => 'CCCCCC', delayed => '006400', whitelist => '00D8FF', awl => 'FF7700', early => 'AA0000', pswl => 'E1FFC1', # psbl => 'EBBAD5', passold => 'BAFF70', # veto => 'EBEBD5', pregreet => 'EBA8D5', dnsbl => 'EB75D5', pipelining => 'B85BA7', nonsmtp => '793C6E', barenewline => '793C2E', command => '47231B', hangup => 'C12C0A', passnew => '468700', # new => 'FF77EE', reconnectok => '7700DD', active => 'EFEF00', deferred => 'DD8800', untrustedtls => 'ffebd1', anonymoustls => 'ffcf90', trustedtls => 'ffb24f', verifiedtls => 'ff5800', untrustedtlsin => 'ddd1ff', anonymoustlsin => 'a8a8ff', trustedtlsin => '6767ff', spfnone => '12FF0A', spffail => 'f80b6f', spfpass => '2E5fEC', dkimnone => 'E6E27A', dkimfail => 'FF6600', dkimpass => '3013EC', dmarcnone => 'F0B166', dmarcfail => 'f11717', dmarcpass => '00FFD5', ); my $url = "http://mailgraph-ng.nausch.org/mx/"; my $url1 = "http://mailgraph-ng.nausch.org/mx11/"; my $url2 = "http://mailgraph-ng.nausch.org/mx12/"; my $url3 = "http://mailgraph-ng.nausch.org/mx13/"; my $url4 = "http://mailgraph-ng.nausch.org/mx14/"; my $url11 = "http://mailgraph-ng.nausch.org/mx11/#G"; my $url12 = "http://mailgraph-ng.nausch.org/mx12/#G"; my $url13 = "http://mailgraph-ng.nausch.org/mx13/#G"; my $url14 = "http://mailgraph-ng.nausch.org/mx14/#G"; sub rrd_graph(@) { my ($range, $file, $ypoints, @rrdargs) = @_; my $step = $range*$points_per_sample/$xpoints; my $end = time; $end -= $end % $step; my $date = localtime(time); $date =~ s|:|\\:|g unless $RRDs::VERSION < 1.199908; my ($graphret,$xs,$ys) = RRDs::graph($file, '--imgformat', 'PNG', '--width', $xpoints, '--height', $ypoints, '--start', "-$range", '--end', $end, '--vertical-label', 'msgs/min', '--lower-limit', 0, '--units-exponent', 0, # don't show milli-messages/s '--lazy', '--color', 'SHADEA#ffffff', '--color', 'SHADEB#ffffff', '--color', 'BACK#ffffff', $RRDs::VERSION < 1.2002 ? () : ( '--slope-mode'), @rrdargs, 'COMMENT:['.$date.']\r', ); my $ERR=RRDs::error; die "ERROR: $ERR\n" if $ERR; } sub graph($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:sent_11=$rrd_11:sent:AVERAGE", "DEF:msent_11=$rrd_11:sent:MAX", "DEF:sent_12=$rrd_12:sent:AVERAGE", "DEF:msent_12=$rrd_12:sent:MAX", "DEF:sent_13=$rrd_13:sent:AVERAGE", "DEF:msent_13=$rrd_13:sent:MAX", "DEF:sent_14=$rrd_14:sent:AVERAGE", "DEF:msent_14=$rrd_14:sent:MAX", "CDEF:rsent=sent_11,sent_12,+,sent_13,+,sent_14,+,60,*", "CDEF:rmsent=msent_11,msent_12,+,msent_13,+,msent_14,+,60,*", "CDEF:dsent=sent_11,sent_12,+,sent_13,+,sent_14,+,UN,0,sent_11,sent_12,+,sent_13,+,sent_14,+,IF,$step,*", "CDEF:ssent=PREV,UN,dsent,PREV,IF,dsent,+", "AREA:rsent#$color{sent}:Sent ", 'GPRINT:ssent:MAX:total\: %15.0lf msgs', 'GPRINT:rsent:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmsent:MAX:max\: %11.0lf msgs/min\l', "DEF:recv_11=$rrd_11:recv:AVERAGE", "DEF:mrecv_11=$rrd_11:recv:MAX", "DEF:recv_12=$rrd_12:recv:AVERAGE", "DEF:mrecv_12=$rrd_12:recv:MAX", "DEF:recv_13=$rrd_13:recv:AVERAGE", "DEF:mrecv_13=$rrd_13:recv:MAX", "DEF:recv_14=$rrd_14:recv:AVERAGE", "DEF:mrecv_14=$rrd_14:recv:MAX", "CDEF:rrecv=recv_11,recv_12,+,recv_13,+,recv_14,+,60,*", "CDEF:rmrecv=mrecv_11,mrecv_12,+,mrecv_13,+,mrecv_14,+,60,*", "CDEF:drecv=recv_11,recv_12,+,recv_13,+,recv_14,+,UN,0,recv_11,recv_12,+,recv_13,+,recv_14,+,IF,$step,*", "CDEF:srecv=PREV,UN,drecv,PREV,IF,drecv,+", "LINE2:rrecv#$color{received}:Received ", 'GPRINT:srecv:MAX:total\: %15.0lf msgs', 'GPRINT:rrecv:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrecv:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_virus($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:rejected_11=$rrd_11:rejected:AVERAGE", "DEF:mrejected_11=$rrd_11:rejected:MAX", "DEF:rejected_12=$rrd_12:rejected:AVERAGE", "DEF:mrejected_12=$rrd_12:rejected:MAX", "DEF:rejected_13=$rrd_13:rejected:AVERAGE", "DEF:mrejected_13=$rrd_13:rejected:MAX", "DEF:rejected_14=$rrd_14:rejected:AVERAGE", "DEF:mrejected_14=$rrd_14:rejected:MAX", "CDEF:rrejected=rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,60,*", "CDEF:drejected=rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,UN,0,rejected_11,rejected_12,+,rejected_13,+,rejected_14,+,IF,$step,*", "CDEF:srejected=PREV,UN,drejected,PREV,IF,drejected,+", "CDEF:rmrejected=mrejected_11,mrejected_12,+,mrejected_13,+,mrejected_14,+,60,*", "AREA:rrejected#$color{rejected}:Rejected/SPAM/Viren ", 'GPRINT:srejected:MAX:total\: %15.0lf msgs', 'GPRINT:rrejected:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmrejected:MAX:max\: %11.0lf msgs/min\l', "DEF:virus_11=$rrd_virus_11:virus:AVERAGE", "DEF:mvirus_11=$rrd_virus_11:virus:MAX", "DEF:virus_12=$rrd_virus_12:virus:AVERAGE", "DEF:mvirus_12=$rrd_virus_12:virus:MAX", "DEF:virus_13=$rrd_virus_13:virus:AVERAGE", "DEF:mvirus_13=$rrd_virus_13:virus:MAX", "DEF:virus_14=$rrd_virus_14:virus:AVERAGE", "DEF:mvirus_14=$rrd_virus_14:virus:MAX", "CDEF:rvirus=virus_11,virus_12,+,virus_13,+,virus_14,+,60,*", "CDEF:dvirus=virus_11,virus_12,+,virus_13,+,virus_14,+,UN,0,virus_11,virus_12,+,virus_13,+,virus_14,+,IF,$step,*", "CDEF:svirus=PREV,UN,dvirus,PREV,IF,dvirus,+", "CDEF:rmvirus=mvirus_11,mvirus_12,+,mvirus_13,+,mvirus_14,+,60,*", "AREA:rvirus#$color{virus}:Viruses ", 'GPRINT:svirus:MAX:total\: %15.0lf msgs', 'GPRINT:rvirus:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmvirus:MAX:max\: %11.0lf msgs/min\l', "DEF:spam_11=$rrd_virus_11:spam:AVERAGE", "DEF:mspam_11=$rrd_virus_11:spam:MAX", "DEF:spam_12=$rrd_virus_12:spam:AVERAGE", "DEF:mspam_12=$rrd_virus_12:spam:MAX", "DEF:spam_13=$rrd_virus_13:spam:AVERAGE", "DEF:mspam_13=$rrd_virus_13:spam:MAX", "DEF:spam_14=$rrd_virus_14:spam:AVERAGE", "DEF:mspam_14=$rrd_virus_14:spam:MAX", "CDEF:rspam=spam_11,spam_12,+,spam_13,+,spam_14,+,60,*", "CDEF:dspam=spam_11,spam_12,+,spam_13,+,spam_14,+,UN,0,spam_11,spam_12,+,spam_13,+,spam_14,+,IF,$step,*", "CDEF:sspam=PREV,UN,dspam,PREV,IF,dspam,+", "CDEF:rmspam=mspam_11,mspam_12,+,mspam_13,+,mspam_14,+,60,*", "STACK:rspam#$color{spam}:Spam ", 'GPRINT:sspam:MAX:total\: %15.0lf msgs', 'GPRINT:rspam:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspam:MAX:max\: %11.0lf msgs/min\l', "DEF:bounced=$rrd:bounced:AVERAGE", "DEF:mbounced=$rrd:bounced:MAX", "DEF:bounced_11=$rrd_11:bounced:AVERAGE", "DEF:mbounced_11=$rrd_11:bounced:MAX", "DEF:bounced_12=$rrd_12:bounced:AVERAGE", "DEF:mbounced_12=$rrd_12:bounced:MAX", "DEF:bounced_13=$rrd_13:bounced:AVERAGE", "DEF:mbounced_13=$rrd_13:bounced:MAX", "DEF:bounced_14=$rrd_14:bounced:AVERAGE", "DEF:mbounced_14=$rrd_14:bounced:MAX", "CDEF:rbounced=bounced_11,bounced_12,+,bounced_13,+,bounced_14,+,60,*", "CDEF:dbounced=bounced_11,bounced_12,+,bounced_13,+,bounced_14,+,UN,0,bounced_11,bounced_12,+,bounced_13,+,bounced_14,+,IF,$step,*", "CDEF:sbounced=PREV,UN,dbounced,PREV,IF,dbounced,+", "CDEF:rmbounced=mbounced_11,mbounced_12,+,mbounced_13,+,mbounced_14,+,60,*", "LINE2:rbounced#$color{bounced}:Bounced ", 'GPRINT:sbounced:MAX:total\: %15.0lf msgs', 'GPRINT:rbounced:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbounced:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greylist($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:new_11=$rrd_grey_11:new:AVERAGE", "DEF:mnew_11=$rrd_grey_11:new:MAX", "DEF:new_12=$rrd_grey_12:new:AVERAGE", "DEF:mnew_12=$rrd_grey_12:new:MAX", "DEF:new_13=$rrd_grey_13:new:AVERAGE", "DEF:mnew_13=$rrd_grey_13:new:MAX", "DEF:new_14=$rrd_grey_14:new:AVERAGE", "DEF:mnew_14=$rrd_grey_14:new:MAX", "CDEF:rnew=new_11,new_12,+,new_13,+,new_14,+,60,*", "CDEF:rmnew=mnew_11,mnew_12,+,mnew_13,+,mnew_14,+,60,*", "CDEF:dnew=new_11,new_12,+,new_13,+,new_14,+,UN,0,new_11,new_12,+,new_13,+,new_14,+,IF,$step,*", "CDEF:snew=PREV,UN,dnew,PREV,IF,dnew,+", "AREA:rnew#$color{new}:New ", 'GPRINT:snew:MAX:total\: %15.0lf msgs', 'GPRINT:rnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnew:MAX:max\: %11.0lf msgs/min\l', "DEF:reconnectok_11=$rrd_grey_11:reconnectok:AVERAGE", "DEF:mreconnectok_11=$rrd_grey_11:reconnectok:MAX", "DEF:reconnectok_12=$rrd_grey_12:reconnectok:AVERAGE", "DEF:mreconnectok_12=$rrd_grey_12:reconnectok:MAX", "DEF:reconnectok_13=$rrd_grey_13:reconnectok:AVERAGE", "DEF:mreconnectok_13=$rrd_grey_13:reconnectok:MAX", "DEF:reconnectok_14=$rrd_grey_14:reconnectok:AVERAGE", "DEF:mreconnectok_14=$rrd_grey_14:reconnectok:MAX", "CDEF:rreconnectok=reconnectok_11,reconnectok_12,+,reconnectok_13,+,reconnectok_14,+,60,*", "CDEF:dreconnectok=reconnectok_11,reconnectok_12,+,reconnectok_13,+,reconnectok_14,+,UN,0,reconnectok_11,reconnectok_12,+,reconnectok_13,+,reconnectok_14,+,IF,$step,*", "CDEF:sreconnectok=PREV,UN,dreconnectok,PREV,IF,dreconnectok,+", "CDEF:rmreconnectok=mreconnectok_11,mreconnectok_12,+,mreconnectok_13,+,mreconnectok_14,+,60,*", "LINE2:rreconnectok#$color{reconnectok}:Reconnect O.K. ", 'GPRINT:sreconnectok:MAX:total\: %15.0lf msgs', 'GPRINT:rreconnectok:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmreconnectok:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_greystats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:greylisted_11=$rrd_grey_11:greylisted:AVERAGE", "DEF:mgreylisted_11=$rrd_grey_11:greylisted:MAX", "DEF:greylisted_12=$rrd_grey_12:greylisted:AVERAGE", "DEF:mgreylisted_12=$rrd_grey_12:greylisted:MAX", "DEF:greylisted_13=$rrd_grey_13:greylisted:AVERAGE", "DEF:mgreylisted_13=$rrd_grey_13:greylisted:MAX", "DEF:greylisted_14=$rrd_grey_14:greylisted:AVERAGE", "DEF:mgreylisted_14=$rrd_grey_14:greylisted:MAX", "CDEF:rgreylisted=greylisted_11,greylisted_12,+,greylisted_13,+,greylisted_14,+,60,*", "CDEF:rmgreylisted=mgreylisted_11,mgreylisted_12,+,mgreylisted_13,+,mgreylisted_14,+,60,*", "CDEF:dgreylisted=greylisted_11,greylisted_12,+,greylisted_13,+,greylisted_14,+,UN,0,greylisted_11,greylisted_12,+,greylisted_13,+,greylisted_14,+,IF,$step,*", "CDEF:sgreylisted=PREV,UN,dgreylisted,PREV,IF,dgreylisted,+", "AREA:rgreylisted#$color{greylisted}:Greylisted ", 'GPRINT:sgreylisted:MAX:total\: %15.0lf msgs', 'GPRINT:rgreylisted:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmgreylisted:MAX:max\: %11.0lf msgs/min\l', "DEF:delayed_11=$rrd_grey_11:delayed:AVERAGE", "DEF:mdelayed_11=$rrd_grey_11:delayed:MAX", "DEF:delayed_12=$rrd_grey_12:delayed:AVERAGE", "DEF:mdelayed_12=$rrd_grey_12:delayed:MAX", "DEF:delayed_13=$rrd_grey_13:delayed:AVERAGE", "DEF:mdelayed_13=$rrd_grey_13:delayed:MAX", "DEF:delayed_14=$rrd_grey_14:delayed:AVERAGE", "DEF:mdelayed_14=$rrd_grey_14:delayed:MAX", "CDEF:rdelayed=delayed_11,delayed_12,+,delayed_13,+,delayed_14,+,60,*", "CDEF:rmdelayed=mdelayed_11,mdelayed_12,+,mdelayed_13,+,mdelayed_14,+,60,*", "CDEF:ddelayed=delayed_11,delayed_12,+,delayed_13,+,delayed_14,+,UN,0,delayed_11,delayed_12,+,delayed_13,+,delayed_14,+,IF,$step,*", "CDEF:sdelayed=PREV,UN,ddelayed,PREV,IF,ddelayed,+", "LINE2:rdelayed#$color{delayed}:Delayed ", 'GPRINT:sdelayed:MAX:total\: %15.0lf msgs', 'GPRINT:rdelayed:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdelayed:MAX:max\: %11.0lf msgs/min\l', "DEF:whitelist_11=$rrd_grey_11:whitelist:AVERAGE", "DEF:mwhitelist_11=$rrd_grey_11:whitelist:MAX", "DEF:whitelist_12=$rrd_grey_12:whitelist:AVERAGE", "DEF:mwhitelist_12=$rrd_grey_12:whitelist:MAX", "DEF:whitelist_13=$rrd_grey_13:whitelist:AVERAGE", "DEF:mwhitelist_13=$rrd_grey_13:whitelist:MAX", "DEF:whitelist_14=$rrd_grey_14:whitelist:AVERAGE", "DEF:mwhitelist_14=$rrd_grey_14:whitelist:MAX", "CDEF:rwhitelist=whitelist_11,whitelist_12,+,whitelist_13,+,whitelist_14,+,60,*", "CDEF:rmwhitelist=mwhitelist_11,mwhitelist_12,+,mwhitelist_13,+,mwhitelist_14,+,60,*", "CDEF:dwhitelist=whitelist_11,whitelist_12,+,whitelist_13,+,whitelist_14,+,UN,0,whitelist_11,whitelist_12,+,whitelist_13,+,whitelist_14,+,IF,$step,*", "CDEF:swhitelist=PREV,UN,dwhitelist,PREV,IF,dwhitelist,+", "AREA:rwhitelist#$color{whitelist}:Whitelist ", 'GPRINT:swhitelist:MAX:total\: %15.0lf msgs', 'GPRINT:rwhitelist:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmwhitelist:MAX:max\: %11.0lf msgs/min\l', "DEF:awl_11=$rrd_grey_11:awl:AVERAGE", "DEF:mawl_11=$rrd_grey_11:awl:MAX", "DEF:awl_12=$rrd_grey_12:awl:AVERAGE", "DEF:mawl_12=$rrd_grey_12:awl:MAX", "DEF:awl_13=$rrd_grey_13:awl:AVERAGE", "DEF:mawl_13=$rrd_grey_13:awl:MAX", "DEF:awl_14=$rrd_grey_14:awl:AVERAGE", "DEF:mawl_14=$rrd_grey_14:awl:MAX", "CDEF:rawl=awl_11,awl_12,+,awl_13,+,awl_14,+,60,*", "CDEF:dawl=awl_11,awl_12,+,awl_13,+,awl_14,+,UN,0,awl_11,awl_12,+,awl_13,+,awl_14,+,IF,$step,*", "CDEF:sawl=PREV,UN,dawl,PREV,IF,dawl,+", "CDEF:rmawl=mawl_11,mawl_12,+,mawl_13,+,mawl_14,+,60,*", "LINE2:rawl#$color{awl}:Auto whitelist ", 'GPRINT:sawl:MAX:total\: %15.0lf msgs', 'GPRINT:rawl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmawl:MAX:max\: %11.0lf msgs/min\l', "DEF:early_11=$rrd_grey_11:early:AVERAGE", "DEF:mearly_11=$rrd_grey_11:early:MAX", "DEF:early_12=$rrd_grey_12:early:AVERAGE", "DEF:mearly_12=$rrd_grey_12:early:MAX", "DEF:early_13=$rrd_grey_13:early:AVERAGE", "DEF:mearly_13=$rrd_grey_13:early:MAX", "DEF:early_14=$rrd_grey_14:early:AVERAGE", "DEF:mearly_14=$rrd_grey_14:early:MAX", "CDEF:rearly=early_11,early_12,+,early_13,+,early_14,+,60,*", "CDEF:rmearly=mearly_11,mearly_12,+,mearly_13,+,mearly_14,+,60,*", "CDEF:dearly=early_11,early_12,+,early_13,+,early_14,+,UN,0,early_11,early_12,+,early_13,+,early_14,+,IF,$step,*", "CDEF:searly=PREV,UN,dearly,PREV,IF,dearly,+", "AREA:rearly#$color{early}:Early connect ", 'GPRINT:searly:MAX:total\: %15.0lf msgs', 'GPRINT:rearly:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmearly:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreen($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:pswl_11=$rrd_post_11:pswl:AVERAGE", "DEF:mpswl_11=$rrd_post_11:pswl:MAX", "DEF:pswl_12=$rrd_post_12:pswl:AVERAGE", "DEF:mpswl_12=$rrd_post_12:pswl:MAX", "DEF:pswl_13=$rrd_post_13:pswl:AVERAGE", "DEF:mpswl_13=$rrd_post_13:pswl:MAX", "DEF:pswl_14=$rrd_post_14:pswl:AVERAGE", "DEF:mpswl_14=$rrd_post_14:pswl:MAX", "CDEF:rpswl=pswl_11,pswl_12,+,pswl_13,+,pswl_14,+,60,*", "CDEF:rmpswl=mpswl_11,mpswl_12,+,mpswl_13,+,mpswl_14,+,60,*", "CDEF:dpswl=pswl_11,pswl_12,+,pswl_13,+,pswl_14,+,UN,0,pswl_11,pswl_12,+,pswl_13,+,pswl_14,+,IF,$step,*", "CDEF:spswl=PREV,UN,dpswl,PREV,IF,dpswl,+", "AREA:rpswl#$color{pswl}:WHITELISTED ", 'GPRINT:spswl:MAX:total\: %15.0lf msgs', 'GPRINT:rpswl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpswl:MAX:max\: %11.0lf msgs/min\l', "DEF:passold_11=$rrd_post_11:passold:AVERAGE", "DEF:mpassold_11=$rrd_post_11:passold:MAX", "DEF:passold_12=$rrd_post_12:passold:AVERAGE", "DEF:mpassold_12=$rrd_post_12:passold:MAX", "DEF:passold_13=$rrd_post_13:passold:AVERAGE", "DEF:mpassold_13=$rrd_post_13:passold:MAX", "DEF:passold_14=$rrd_post_14:passold:AVERAGE", "DEF:mpassold_14=$rrd_post_14:passold:MAX", "CDEF:rpassold=passold_11,passold_12,+,passold_13,+,passold_14,+,60,*", "CDEF:dpassold=passold_11,passold_12,+,passold_13,+,passold_14,+,UN,0,passold_11,passold_12,+,passold_13,+,passold_14,+,IF,$step,*", "CDEF:spassold=PREV,UN,dpassold,PREV,IF,dpassold,+", "CDEF:rmpassold=mpassold_11,mpassold_12,+,mpassold_13,+,mpassold_14,+,60,*", "STACK:rpassold#$color{passold}:PASS OLD ", 'GPRINT:spassold:MAX:total\: %15.0lf msgs', 'GPRINT:rpassold:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassold:MAX:max\: %11.0lf msgs/min\l', "DEF:passnew_11=$rrd_post_11:passnew:AVERAGE", "DEF:mpassnew_11=$rrd_post_11:passnew:MAX", "DEF:passnew_12=$rrd_post_12:passnew:AVERAGE", "DEF:mpassnew_12=$rrd_post_12:passnew:MAX", "DEF:passnew_13=$rrd_post_13:passnew:AVERAGE", "DEF:mpassnew_13=$rrd_post_13:passnew:MAX", "DEF:passnew_14=$rrd_post_14:passnew:AVERAGE", "DEF:mpassnew_14=$rrd_post_14:passnew:MAX", "CDEF:rpassnew=passnew_11,passnew_12,+,passnew_13,+,passnew_14,+,60,*", "CDEF:dpassnew=passnew_11,passnew_12,+,passnew_13,+,passnew_14,+,UN,0,passnew_11,passnew_12,+,passnew_13,+,passnew_14,+,IF,$step,*", "CDEF:spassnew=PREV,UN,dpassnew,PREV,IF,dpassnew,+", "CDEF:rmpassnew=mpassnew_11,mpassnew_12,+,mpassnew_13,+,mpassnew_14,+,60,*", "LINE2:rpassnew#$color{passnew}:PASS NEW ", 'GPRINT:spassnew:MAX:total\: %15.0lf msgs', 'GPRINT:rpassnew:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpassnew:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_postscreenstats($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:psbl_11=$rrd_post_11:psbl:AVERAGE", "DEF:mpsbl_11=$rrd_post_11:psbl:MAX", "DEF:psbl_12=$rrd_post_12:psbl:AVERAGE", "DEF:mpsbl_12=$rrd_post_12:psbl:MAX", "DEF:psbl_13=$rrd_post_13:psbl:AVERAGE", "DEF:mpsbl_13=$rrd_post_13:psbl:MAX", "DEF:psbl_14=$rrd_post_14:psbl:AVERAGE", "DEF:mpsbl_14=$rrd_post_14:psbl:MAX", "CDEF:rpsbl=psbl_11,psbl_12,+,psbl_13,+,psbl_14,+,60,*", "CDEF:dpsbl=psbl_11,psbl_12,+,psbl_13,+,psbl_14,+,UN,0,psbl_11,psbl_12,+,psbl_13,+,psbl_14,+,IF,$step,*", "CDEF:spsbl=PREV,UN,dpsbl,PREV,IF,dpsbl,+", "CDEF:rmpsbl=mpsbl_11,mpsbl_12,+,mpsbl_13,+,mpsbl_14,+,60,*", "AREA:rpsbl#$color{psbl}:BLACKLISTED ", 'GPRINT:spsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rpsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:veto_11=$rrd_post_11:veto:AVERAGE", "DEF:mveto_11=$rrd_post_11:veto:MAX", "DEF:veto_12=$rrd_post_12:veto:AVERAGE", "DEF:mveto_12=$rrd_post_12:veto:MAX", "DEF:veto_13=$rrd_post_13:veto:AVERAGE", "DEF:mveto_13=$rrd_post_13:veto:MAX", "DEF:veto_14=$rrd_post_14:veto:AVERAGE", "DEF:mveto_14=$rrd_post_14:veto:MAX", "CDEF:rveto=veto_11,veto_12,+,veto_13,+,veto_14,+,60,*", "CDEF:dveto=veto_11,veto_12,+,veto_13,+,veto_14,+,UN,0,veto_11,veto_12,+,veto_13,+,veto_14,+,IF,$step,*", "CDEF:sveto=PREV,UN,dveto,PREV,IF,dveto,+", "CDEF:rmveto=mveto_11,mveto_12,+,mveto_13,+,mveto_14,+,60,*", "STACK:rveto#$color{veto}:WHITELIST VETO ", 'GPRINT:sveto:MAX:total\: %15.0lf msgs', 'GPRINT:rveto:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmveto:MAX:max\: %11.0lf msgs/min\l', "DEF:pregreet_11=$rrd_post_11:pregreet:AVERAGE", "DEF:mpregreet_11=$rrd_post_11:pregreet:MAX", "DEF:pregreet_12=$rrd_post_12:pregreet:AVERAGE", "DEF:mpregreet_12=$rrd_post_12:pregreet:MAX", "DEF:pregreet_13=$rrd_post_13:pregreet:AVERAGE", "DEF:mpregreet_13=$rrd_post_13:pregreet:MAX", "DEF:pregreet_14=$rrd_post_14:pregreet:AVERAGE", "DEF:mpregreet_14=$rrd_post_14:pregreet:MAX", "CDEF:rpregreet=pregreet_11,pregreet_12,+,pregreet_13,+,pregreet_14,+,60,*", "CDEF:dpregreet=pregreet_11,pregreet_12,+,pregreet_13,+,pregreet_14,+,UN,0,pregreet_11,pregreet_12,+,pregreet_13,+,pregreet_14,+,IF,$step,*", "CDEF:spregreet=PREV,UN,dpregreet,PREV,IF,dpregreet,+", "CDEF:rmpregreet=mpregreet_11,mpregreet_12,+,mpregreet_13,+,mpregreet_14,+,60,*", "STACK:rpregreet#$color{pregreet}:PREGREET ", 'GPRINT:spregreet:MAX:total\: %15.0lf msgs', 'GPRINT:rpregreet:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpregreet:MAX:max\: %11.0lf msgs/min\l', "DEF:dnsbl_11=$rrd_post_11:dnsbl:AVERAGE", "DEF:mdnsbl_11=$rrd_post_11:dnsbl:MAX", "DEF:dnsbl_12=$rrd_post_12:dnsbl:AVERAGE", "DEF:mdnsbl_12=$rrd_post_12:dnsbl:MAX", "DEF:dnsbl_13=$rrd_post_13:dnsbl:AVERAGE", "DEF:mdnsbl_13=$rrd_post_13:dnsbl:MAX", "DEF:dnsbl_14=$rrd_post_14:dnsbl:AVERAGE", "DEF:mdnsbl_14=$rrd_post_14:dnsbl:MAX", "CDEF:rdnsbl=dnsbl_11,dnsbl_12,+,dnsbl_13,+,dnsbl_14,+,60,*", "CDEF:ddnsbl=dnsbl_11,dnsbl_12,+,dnsbl_13,+,dnsbl_14,+,UN,0,dnsbl_11,dnsbl_12,+,dnsbl_13,+,dnsbl_14,+,IF,$step,*", "CDEF:sdnsbl=PREV,UN,ddnsbl,PREV,IF,ddnsbl,+", "CDEF:rmdnsbl=mdnsbl_11,mdnsbl_12,+,mdnsbl_13,+,mdnsbl_14,+,60,*", "STACK:rdnsbl#$color{dnsbl}:DNSBL ", 'GPRINT:sdnsbl:MAX:total\: %15.0lf msgs', 'GPRINT:rdnsbl:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdnsbl:MAX:max\: %11.0lf msgs/min\l', "DEF:pipelining_11=$rrd_post_11:pipelining:AVERAGE", "DEF:mpipelining_11=$rrd_post_11:pipelining:MAX", "DEF:pipelining_12=$rrd_post_12:pipelining:AVERAGE", "DEF:mpipelining_12=$rrd_post_12:pipelining:MAX", "DEF:pipelining_13=$rrd_post_13:pipelining:AVERAGE", "DEF:mpipelining_13=$rrd_post_13:pipelining:MAX", "DEF:pipelining_14=$rrd_post_14:pipelining:AVERAGE", "DEF:mpipelining_14=$rrd_post_14:pipelining:MAX", "CDEF:rpipelining=pipelining_11,pipelining_12,+,pipelining_13,+,pipelining_14,+,60,*", "CDEF:dpipelining=pipelining_11,pipelining_12,+,pipelining_13,+,pipelining_14,+,UN,0,pipelining_11,pipelining_12,+,pipelining_13,+,pipelining_14,+,IF,$step,*", "CDEF:spipelining=PREV,UN,dpipelining,PREV,IF,dpipelining,+", "CDEF:rmpipelining=mpipelining_11,mpipelining_12,+,mpipelining_13,+,mpipelining_14,+,60,*", "STACK:rpipelining#$color{pipelining}:PIPELINING ", 'GPRINT:spipelining:MAX:total\: %15.0lf msgs', 'GPRINT:rpipelining:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmpipelining:MAX:max\: %11.0lf msgs/min\l', "DEF:nonsmtp_11=$rrd_post_11:nonsmtp:AVERAGE", "DEF:mnonsmtp_11=$rrd_post_11:nonsmtp:MAX", "DEF:nonsmtp_12=$rrd_post_12:nonsmtp:AVERAGE", "DEF:mnonsmtp_12=$rrd_post_12:nonsmtp:MAX", "DEF:nonsmtp_13=$rrd_post_13:nonsmtp:AVERAGE", "DEF:mnonsmtp_13=$rrd_post_13:nonsmtp:MAX", "DEF:nonsmtp_14=$rrd_post_14:nonsmtp:AVERAGE", "DEF:mnonsmtp_14=$rrd_post_14:nonsmtp:MAX", "CDEF:rnonsmtp=nonsmtp_11,nonsmtp_12,+,nonsmtp_13,+,nonsmtp_14,+,60,*", "CDEF:dnonsmtp=nonsmtp_11,nonsmtp_12,+,nonsmtp_13,+,nonsmtp_14,+,UN,0,nonsmtp_11,nonsmtp_12,+,nonsmtp_13,+,nonsmtp_14,+,IF,$step,*", "CDEF:snonsmtp=PREV,UN,dnonsmtp,PREV,IF,dnonsmtp,+", "CDEF:rmnonsmtp=mnonsmtp_11,mnonsmtp_12,+,mnonsmtp_13,+,mnonsmtp_14,+,60,*", "STACK:rnonsmtp#$color{nonsmtp}:NON SMTP COMMAND ", 'GPRINT:snonsmtp:MAX:total\: %15.0lf msgs', 'GPRINT:rnonsmtp:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmnonsmtp:MAX:max\: %11.0lf msgs/min\l', "DEF:barenewline_11=$rrd_post_11:barenewline:AVERAGE", "DEF:mbarenewline_11=$rrd_post_11:barenewline:MAX", "DEF:barenewline_12=$rrd_post_12:barenewline:AVERAGE", "DEF:mbarenewline_12=$rrd_post_12:barenewline:MAX", "DEF:barenewline_13=$rrd_post_13:barenewline:AVERAGE", "DEF:mbarenewline_13=$rrd_post_13:barenewline:MAX", "DEF:barenewline_14=$rrd_post_14:barenewline:AVERAGE", "DEF:mbarenewline_14=$rrd_post_14:barenewline:MAX", "CDEF:rbarenewline=barenewline_11,barenewline_12,+,barenewline_13,+,barenewline_14,+,60,*", "CDEF:dbarenewline=barenewline_11,barenewline_12,+,barenewline_13,+,barenewline_14,+,UN,0,barenewline_11,barenewline_12,+,barenewline_13,+,barenewline_14,+,IF,$step,*", "CDEF:sbarenewline=PREV,UN,dbarenewline,PREV,IF,dbarenewline,+", "CDEF:rmbarenewline=mbarenewline_11,mbarenewline_12,+,mbarenewline_13,+,mbarenewline_14,+,60,*", "STACK:rbarenewline#$color{barenewline}:BARE NEWLINE ", 'GPRINT:sbarenewline:MAX:total\: %15.0lf msgs', 'GPRINT:rbarenewline:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmbarenewline:MAX:max\: %11.0lf msgs/min\l', "DEF:command_11=$rrd_post_11:command:AVERAGE", "DEF:mcommand_11=$rrd_post_11:command:MAX", "DEF:command_12=$rrd_post_12:command:AVERAGE", "DEF:mcommand_12=$rrd_post_12:command:MAX", "DEF:command_13=$rrd_post_13:command:AVERAGE", "DEF:mcommand_13=$rrd_post_13:command:MAX", "DEF:command_14=$rrd_post_14:command:AVERAGE", "DEF:mcommand_14=$rrd_post_14:command:MAX", "CDEF:rcommand=command_11,command_12,+,command_13,+,command_14,+,60,*", "CDEF:dcommand=command_11,command_12,+,command_13,+,command_14,+,UN,0,command_11,command_12,+,command_13,+,command_14,+,IF,$step,*", "CDEF:scommand=PREV,UN,dcommand,PREV,IF,dcommand,+", "CDEF:rmcommand=mcommand_11,mcommand_12,+,mcommand_13,+,mcommand_14,+,60,*", "STACK:rcommand#$color{command}:COMMAND LIMITS ", 'GPRINT:scommand:MAX:total\: %15.0lf msgs', 'GPRINT:rcommand:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmcommand:MAX:max\: %11.0lf msgs/min\l', "DEF:hangup_11=$rrd_post_11:hangup:AVERAGE", "DEF:mhangup_11=$rrd_post_11:hangup:MAX", "DEF:hangup_12=$rrd_post_12:hangup:AVERAGE", "DEF:mhangup_12=$rrd_post_12:hangup:MAX", "DEF:hangup_13=$rrd_post_13:hangup:AVERAGE", "DEF:mhangup_13=$rrd_post_13:hangup:MAX", "DEF:hangup_14=$rrd_post_14:hangup:AVERAGE", "DEF:mhangup_14=$rrd_post_14:hangup:MAX", "CDEF:rhangup=hangup_11,hangup_12,+,hangup_13,+,hangup_14,+,60,*", "CDEF:dhangup=hangup_11,hangup_12,+,hangup_13,+,hangup_14,+,UN,0,hangup_11,hangup_12,+,hangup_13,+,hangup_14,+,IF,$step,*", "CDEF:shangup=PREV,UN,dhangup,PREV,IF,dhangup,+", "CDEF:rmhangup=mhangup_11,mhangup_12,+,mhangup_13,+,mhangup_14,+,60,*", "STACK:rhangup#$color{hangup}:HUNGUP ", 'GPRINT:shangup:MAX:total\: %15.0lf msgs', 'GPRINT:rhangup:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmhangup:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dane($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtls_11=$rrd_dane_11:untrustedtls:AVERAGE", "DEF:muntrustedtls_11=$rrd_dane_11:untrustedtls:MAX", "DEF:untrustedtls_12=$rrd_dane_12:untrustedtls:AVERAGE", "DEF:muntrustedtls_12=$rrd_dane_12:untrustedtls:MAX", "DEF:untrustedtls_13=$rrd_dane_13:untrustedtls:AVERAGE", "DEF:muntrustedtls_13=$rrd_dane_13:untrustedtls:MAX", "DEF:untrustedtls_14=$rrd_dane_14:untrustedtls:AVERAGE", "DEF:muntrustedtls_14=$rrd_dane_14:untrustedtls:MAX", "CDEF:runtrustedtls=untrustedtls_11,untrustedtls_12,+,untrustedtls_13,+,untrustedtls_14,+,60,*", "CDEF:duntrustedtls=untrustedtls_11,untrustedtls_12,+,untrustedtls_13,+,untrustedtls_14,+,UN,0,untrustedtls_11,untrustedtls_12,+,untrustedtls_13,+,untrustedtls_14,+,IF,$step,*", "CDEF:suntrustedtls=PREV,UN,duntrustedtls,PREV,IF,duntrustedtls,+", "CDEF:rmuntrustedtls=muntrustedtls_11,muntrustedtls_12,+,muntrustedtls_13,+,muntrustedtls_14,+,60,*", "AREA:runtrustedtls#$color{untrustedtls}:Out Untrusted TLS ", 'GPRINT:suntrustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustls_11=$rrd_dane_11:anonymoustls:AVERAGE", "DEF:manonymoustls_11=$rrd_dane_11:anonymoustls:MAX", "DEF:anonymoustls_12=$rrd_dane_12:anonymoustls:AVERAGE", "DEF:manonymoustls_12=$rrd_dane_12:anonymoustls:MAX", "DEF:anonymoustls_13=$rrd_dane_13:anonymoustls:AVERAGE", "DEF:manonymoustls_13=$rrd_dane_13:anonymoustls:MAX", "DEF:anonymoustls_14=$rrd_dane_14:anonymoustls:AVERAGE", "DEF:manonymoustls_14=$rrd_dane_14:anonymoustls:MAX", "CDEF:ranonymoustls=anonymoustls_11,anonymoustls_12,+,anonymoustls_13,+,anonymoustls_14,+,60,*", "CDEF:danonymoustls=anonymoustls_11,anonymoustls_12,+,anonymoustls_13,+,anonymoustls_14,+,UN,0,anonymoustls_11,anonymoustls_12,+,anonymoustls_13,+,anonymoustls_14,+,IF,$step,*", "CDEF:sanonymoustls=PREV,UN,danonymoustls,PREV,IF,danonymoustls,+", "CDEF:rmanonymoustls=manonymoustls_11,manonymoustls_12,+,manonymoustls_13,+,manonymoustls_14,+,60,*", "STACK:ranonymoustls#$color{anonymoustls}:Out Anonymous TLS ", 'GPRINT:sanonymoustls:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustls:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtls_11=$rrd_dane_11:trustedtls:AVERAGE", "DEF:mtrustedtls_11=$rrd_dane_11:trustedtls:MAX", "DEF:trustedtls_12=$rrd_dane_12:trustedtls:AVERAGE", "DEF:mtrustedtls_12=$rrd_dane_12:trustedtls:MAX", "DEF:trustedtls_13=$rrd_dane_13:trustedtls:AVERAGE", "DEF:mtrustedtls_13=$rrd_dane_13:trustedtls:MAX", "DEF:trustedtls_14=$rrd_dane_14:trustedtls:AVERAGE", "DEF:mtrustedtls_14=$rrd_dane_14:trustedtls:MAX", "CDEF:rtrustedtls=trustedtls_11,trustedtls_12,+,trustedtls_13,+,trustedtls_14,+,60,*", "CDEF:dtrustedtls=trustedtls_11,trustedtls_12,+,trustedtls_13,+,trustedtls_14,+,UN,0,trustedtls_11,trustedtls_12,+,trustedtls_13,+,trustedtls_14,+,IF,$step,*", "CDEF:strustedtls=PREV,UN,dtrustedtls,PREV,IF,dtrustedtls,+", "CDEF:rmtrustedtls=mtrustedtls_11,mtrustedtls_12,+,mtrustedtls_13,+,mtrustedtls_14,+,60,*", "STACK:rtrustedtls#$color{trustedtls}:Out Trusted TLS ", 'GPRINT:strustedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtls:MAX:max\: %11.0lf msgs/min\l', "DEF:verifiedtls_11=$rrd_dane_11:verifiedtls:AVERAGE", "DEF:mverifiedtls_11=$rrd_dane_11:verifiedtls:MAX", "DEF:verifiedtls_12=$rrd_dane_12:verifiedtls:AVERAGE", "DEF:mverifiedtls_12=$rrd_dane_12:verifiedtls:MAX", "DEF:verifiedtls_13=$rrd_dane_13:verifiedtls:AVERAGE", "DEF:mverifiedtls_13=$rrd_dane_13:verifiedtls:MAX", "DEF:verifiedtls_14=$rrd_dane_14:verifiedtls:AVERAGE", "DEF:mverifiedtls_14=$rrd_dane_14:verifiedtls:MAX", "CDEF:rverifiedtls=verifiedtls_11,verifiedtls_12,+,verifiedtls_13,+,verifiedtls_14,+,60,*", "CDEF:dverifiedtls=verifiedtls_11,verifiedtls_12,+,verifiedtls_13,+,verifiedtls_14,+,UN,0,verifiedtls_11,verifiedtls_12,+,verifiedtls_13,+,verifiedtls_14,+,IF,$step,*", "CDEF:sverifiedtls=PREV,UN,dverifiedtls,PREV,IF,dverifiedtls,+", "CDEF:rmverifiedtls=mverifiedtls_11,mverifiedtls_12,+,mverifiedtls_13,+,mverifiedtls_14,+,60,*", "LINE2:rverifiedtls#$color{verifiedtls}:Out Verified TLS ", 'GPRINT:sverifiedtls:MAX:total\: %15.0lf msgs', 'GPRINT:rverifiedtls:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmverifiedtls:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_smtpd($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:untrustedtlsin_11=$rrd_smtpd_11:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin_11=$rrd_smtpd_11:untrustedtlsin:MAX", "DEF:untrustedtlsin_12=$rrd_smtpd_12:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin_12=$rrd_smtpd_12:untrustedtlsin:MAX", "DEF:untrustedtlsin_13=$rrd_smtpd_13:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin_13=$rrd_smtpd_13:untrustedtlsin:MAX", "DEF:untrustedtlsin_14=$rrd_smtpd_14:untrustedtlsin:AVERAGE", "DEF:muntrustedtlsin_14=$rrd_smtpd_14:untrustedtlsin:MAX", "CDEF:runtrustedtlsin=untrustedtlsin_11,untrustedtlsin_12,+,untrustedtlsin_13,+,untrustedtlsin_14,+,60,*", "CDEF:duntrustedtlsin=untrustedtlsin_11,untrustedtlsin_12,+,untrustedtlsin_13,+,untrustedtlsin_14,+,UN,0,untrustedtlsin_11,untrustedtlsin_12,+,untrustedtlsin_13,+,untrustedtlsin_14,+,IF,$step,*", "CDEF:suntrustedtlsin=PREV,UN,duntrustedtlsin,PREV,IF,duntrustedtlsin,+", "CDEF:rmuntrustedtlsin=muntrustedtlsin_11,muntrustedtlsin_12,+,muntrustedtlsin_13,+,muntrustedtlsin_14,+,60,*", "AREA:runtrustedtlsin#$color{untrustedtlsin}:IN Untrusted TLS ", 'GPRINT:suntrustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:runtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmuntrustedtlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:anonymoustlsin_11=$rrd_smtpd_11:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin_11=$rrd_smtpd_11:anonymoustlsin:MAX", "DEF:anonymoustlsin_12=$rrd_smtpd_12:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin_12=$rrd_smtpd_12:anonymoustlsin:MAX", "DEF:anonymoustlsin_13=$rrd_smtpd_13:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin_13=$rrd_smtpd_13:anonymoustlsin:MAX", "DEF:anonymoustlsin_14=$rrd_smtpd_14:anonymoustlsin:AVERAGE", "DEF:manonymoustlsin_14=$rrd_smtpd_14:anonymoustlsin:MAX", "CDEF:ranonymoustlsin=anonymoustlsin_11,anonymoustlsin_12,+,anonymoustlsin_13,+,anonymoustlsin_14,+,60,*", "CDEF:danonymoustlsin=anonymoustlsin_11,anonymoustlsin_12,+,anonymoustlsin_13,+,anonymoustlsin_14,+,UN,0,anonymoustlsin_11,anonymoustlsin_12,+,anonymoustlsin_13,+,anonymoustlsin_14,+,IF,$step,*", "CDEF:sanonymoustlsin=PREV,UN,danonymoustlsin,PREV,IF,danonymoustlsin,+", "CDEF:rmanonymoustlsin=manonymoustlsin_11,manonymoustlsin_12,+,manonymoustlsin_13,+,manonymoustlsin_14,+,60,*", "STACK:ranonymoustlsin#$color{anonymoustlsin}:IN Anonymous TLS ", 'GPRINT:sanonymoustlsin:MAX:total\: %15.0lf msgs', 'GPRINT:ranonymoustlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmanonymoustlsin:MAX:max\: %11.0lf msgs/min\l', "DEF:trustedtlsin_11=$rrd_smtpd_11:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin_11=$rrd_smtpd_11:trustedtlsin:MAX", "DEF:trustedtlsin_12=$rrd_smtpd_12:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin_12=$rrd_smtpd_12:trustedtlsin:MAX", "DEF:trustedtlsin_13=$rrd_smtpd_13:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin_13=$rrd_smtpd_13:trustedtlsin:MAX", "DEF:trustedtlsin_14=$rrd_smtpd_14:trustedtlsin:AVERAGE", "DEF:mtrustedtlsin_14=$rrd_smtpd_14:trustedtlsin:MAX", "CDEF:rtrustedtlsin=trustedtlsin_11,trustedtlsin_12,+,trustedtlsin_13,+,trustedtlsin_14,+,60,*", "CDEF:dtrustedtlsin=trustedtlsin_11,trustedtlsin_12,+,trustedtlsin_13,+,trustedtlsin_14,+,UN,0,trustedtlsin_11,trustedtlsin_12,+,trustedtlsin_13,+,trustedtlsin_14,+,IF,$step,*", "CDEF:strustedtlsin=PREV,UN,dtrustedtlsin,PREV,IF,dtrustedtlsin,+", "CDEF:rmtrustedtlsin=mtrustedtlsin_11,mtrustedtlsin_12,+,mtrustedtlsin_13,+,mtrustedtlsin_14,+,60,*", "STACK:rtrustedtlsin#$color{trustedtlsin}:In Trusted TLS ", 'GPRINT:strustedtlsin:MAX:total\: %15.0lf msgs', 'GPRINT:rtrustedtlsin:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmtrustedtlsin:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_spf($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:spfpass_11=$rrd_dmarc_11:spfpass:AVERAGE", "DEF:mspfpass_11=$rrd_dmarc_11:spfpass:MAX", "DEF:spfpass_12=$rrd_dmarc_12:spfpass:AVERAGE", "DEF:mspfpass_12=$rrd_dmarc_12:spfpass:MAX", "DEF:spfpass_13=$rrd_dmarc_13:spfpass:AVERAGE", "DEF:mspfpass_13=$rrd_dmarc_13:spfpass:MAX", "DEF:spfpass_14=$rrd_dmarc_14:spfpass:AVERAGE", "DEF:mspfpass_14=$rrd_dmarc_14:spfpass:MAX", "CDEF:rspfpass=spfpass_11,spfpass_12,+,spfpass_13,+,spfpass_14,+,60,*", "CDEF:dspfpass=spfpass_11,spfpass_12,+,spfpass_13,+,spfpass_14,+,UN,0,spfpass_11,spfpass_12,+,spfpass_13,+,spfpass_14,+,IF,$step,*", "CDEF:sspfpass=PREV,UN,dspfpass,PREV,IF,dspfpass,+", "CDEF:rmspfpass=mspfpass_11,mspfpass_12,+,mspfpass_13,+,mspfpass_14,+,60,*", "AREA:rspfpass#$color{spfpass}:SPF pass ", 'GPRINT:sspfpass:MAX:total\: %15.0lf msgs', 'GPRINT:rspfpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfpass:MAX:max\: %11.0lf msgs/min\l', "DEF:spfnone_11=$rrd_dmarc_11:spfnone:AVERAGE", "DEF:mspfnone_11=$rrd_dmarc_11:spfnone:MAX", "DEF:spfnone_12=$rrd_dmarc_12:spfnone:AVERAGE", "DEF:mspfnone_12=$rrd_dmarc_12:spfnone:MAX", "DEF:spfnone_13=$rrd_dmarc_13:spfnone:AVERAGE", "DEF:mspfnone_13=$rrd_dmarc_13:spfnone:MAX", "DEF:spfnone_14=$rrd_dmarc_14:spfnone:AVERAGE", "DEF:mspfnone_14=$rrd_dmarc_14:spfnone:MAX", "CDEF:rspfnone=spfnone_11,spfnone_12,+,spfnone_13,+,spfnone_14,+,60,*", "CDEF:dspfnone=spfnone_11,spfnone_12,+,spfnone_13,+,spfnone_14,+,UN,0,spfnone_11,spfnone_12,+,spfnone_13,+,spfnone_14,+,IF,$step,*", "CDEF:sspfnone=PREV,UN,dspfnone,PREV,IF,dspfnone,+", "CDEF:rmspfnone=mspfnone_11,mspfnone_12,+,mspfnone_13,+,mspfnone_14,+,60,*", "LINE2:rspfnone#$color{spfnone}:SPF none ", 'GPRINT:sspfnone:MAX:total\: %15.0lf msgs', 'GPRINT:rspfnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspfnone:MAX:max\: %11.0lf msgs/min\l', "DEF:spffail_11=$rrd_dmarc_11:spffail:AVERAGE", "DEF:mspffail_11=$rrd_dmarc_11:spffail:MAX", "DEF:spffail_12=$rrd_dmarc_12:spffail:AVERAGE", "DEF:mspffail_12=$rrd_dmarc_12:spffail:MAX", "DEF:spffail_13=$rrd_dmarc_13:spffail:AVERAGE", "DEF:mspffail_13=$rrd_dmarc_13:spffail:MAX", "DEF:spffail_14=$rrd_dmarc_14:spffail:AVERAGE", "DEF:mspffail_14=$rrd_dmarc_14:spffail:MAX", "CDEF:rspffail=spffail_11,spffail_12,+,spffail_13,+,spffail_14,+,60,*", "CDEF:dspffail=spffail_11,spffail_12,+,spffail_13,+,spffail_14,+,UN,0,spffail_11,spffail_12,+,spffail_13,+,spffail_14,+,IF,$step,*", "CDEF:sspffail=PREV,UN,dspffail,PREV,IF,dspffail,+", "CDEF:rmspffail=mspffail_11,mspffail_12,+,mspffail_13,+,mspffail_14,+,60,*", "LINE2:rspffail#$color{spffail}:SPF fail ", 'GPRINT:sspffail:MAX:total\: %15.0lf msgs', 'GPRINT:rspffail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmspffail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dkim($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dkimpass_11=$rrd_dmarc_11:dkimpass:AVERAGE", "DEF:mdkimpass_11=$rrd_dmarc_11:dkimpass:MAX", "DEF:dkimpass_12=$rrd_dmarc_12:dkimpass:AVERAGE", "DEF:mdkimpass_12=$rrd_dmarc_12:dkimpass:MAX", "DEF:dkimpass_13=$rrd_dmarc_13:dkimpass:AVERAGE", "DEF:mdkimpass_13=$rrd_dmarc_13:dkimpass:MAX", "DEF:dkimpass_14=$rrd_dmarc_14:dkimpass:AVERAGE", "DEF:mdkimpass_14=$rrd_dmarc_14:dkimpass:MAX", "CDEF:rdkimpass=dkimpass_11,dkimpass_12,+,dkimpass_13,+,dkimpass_14,+,60,*", "CDEF:ddkimpass=dkimpass_11,dkimpass_12,+,dkimpass_13,+,dkimpass_14,+,UN,0,dkimpass_11,dkimpass_12,+,dkimpass_13,+,dkimpass_14,+,IF,$step,*", "CDEF:sdkimpass=PREV,UN,ddkimpass,PREV,IF,ddkimpass,+", "CDEF:rmdkimpass=mdkimpass_11,mdkimpass_12,+,mdkimpass_13,+,mdkimpass_14,+,60,*", "AREA:rdkimpass#$color{dkimpass}:DKIM pass ", 'GPRINT:sdkimpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimnone_11=$rrd_dmarc_11:dkimnone:AVERAGE", "DEF:mdkimnone_11=$rrd_dmarc_11:dkimnone:MAX", "DEF:dkimnone_12=$rrd_dmarc_12:dkimnone:AVERAGE", "DEF:mdkimnone_12=$rrd_dmarc_12:dkimnone:MAX", "DEF:dkimnone_13=$rrd_dmarc_13:dkimnone:AVERAGE", "DEF:mdkimnone_13=$rrd_dmarc_13:dkimnone:MAX", "DEF:dkimnone_14=$rrd_dmarc_14:dkimnone:AVERAGE", "DEF:mdkimnone_14=$rrd_dmarc_14:dkimnone:MAX", "CDEF:rdkimnone=dkimnone_11,dkimnone_12,+,dkimnone_13,+,dkimnone_14,+,60,*", "CDEF:ddkimnone=dkimnone_11,dkimnone_12,+,dkimnone_13,+,dkimnone_14,+,UN,0,dkimnone_11,dkimnone_12,+,dkimnone_13,+,dkimnone_14,+,IF,$step,*", "CDEF:sdkimnone=PREV,UN,ddkimnone,PREV,IF,ddkimnone,+", "CDEF:rmdkimnone=mdkimnone_11,mdkimnone_12,+,mdkimnone_13,+,mdkimnone_14,+,60,*", "LINE2:rdkimnone#$color{dkimnone}:DKIM none ", 'GPRINT:sdkimnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dkimfail_11=$rrd_dmarc_11:dkimfail:AVERAGE", "DEF:mdkimfail_11=$rrd_dmarc_11:dkimfail:MAX", "DEF:dkimfail_12=$rrd_dmarc_12:dkimfail:AVERAGE", "DEF:mdkimfail_12=$rrd_dmarc_12:dkimfail:MAX", "DEF:dkimfail_13=$rrd_dmarc_13:dkimfail:AVERAGE", "DEF:mdkimfail_13=$rrd_dmarc_13:dkimfail:MAX", "DEF:dkimfail_14=$rrd_dmarc_14:dkimfail:AVERAGE", "DEF:mdkimfail_14=$rrd_dmarc_14:dkimfail:MAX", "CDEF:rdkimfail=dkimfail_11,dkimfail_12,+,dkimfail_13,+,dkimfail_14,+,60,*", "CDEF:ddkimfail=dkimfail_11,dkimfail_12,+,dkimfail_13,+,dkimfail_14,+,UN,0,dkimfail_11,dkimfail_12,+,dkimfail_13,+,dkimfail_14,+,IF,$step,*", "CDEF:sdkimfail=PREV,UN,ddkimfail,PREV,IF,ddkimfail,+", "CDEF:rmdkimfail=mdkimfail_11,mdkimfail_12,+,mdkimfail_13,+,mdkimfail_14,+,60,*", "LINE2:rdkimfail#$color{dkimfail}:DKIM fail ", 'GPRINT:sdkimfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdkimfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdkimfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_dmarc($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:dmarcpass_11=$rrd_dmarc_11:dmarcpass:AVERAGE", "DEF:mdmarcpass_11=$rrd_dmarc_11:dmarcpass:MAX", "DEF:dmarcpass_12=$rrd_dmarc_12:dmarcpass:AVERAGE", "DEF:mdmarcpass_12=$rrd_dmarc_12:dmarcpass:MAX", "DEF:dmarcpass_13=$rrd_dmarc_13:dmarcpass:AVERAGE", "DEF:mdmarcpass_13=$rrd_dmarc_13:dmarcpass:MAX", "DEF:dmarcpass_14=$rrd_dmarc_14:dmarcpass:AVERAGE", "DEF:mdmarcpass_14=$rrd_dmarc_14:dmarcpass:MAX", "CDEF:rdmarcpass=dmarcpass_11,dmarcpass_12,+,dmarcpass_13,+,dmarcpass_14,+,60,*", "CDEF:ddmarcpass=dmarcpass_11,dmarcpass_12,+,dmarcpass_13,+,dmarcpass_14,+,UN,0,dmarcpass_11,dmarcpass_12,+,dmarcpass_13,+,dmarcpass_14,+,IF,$step,*", "CDEF:sdmarcpass=PREV,UN,ddmarcpass,PREV,IF,ddmarcpass,+", "CDEF:rmdmarcpass=mdmarcpass_11,mdmarcpass_12,+,mdmarcpass_13,+,mdmarcpass_14,+,60,*", "AREA:rdmarcpass#$color{dmarcpass}:DMARC pass ", 'GPRINT:sdmarcpass:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcpass:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcpass:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcnone_11=$rrd_dmarc_11:dmarcnone:AVERAGE", "DEF:mdmarcnone_11=$rrd_dmarc_11:dmarcnone:MAX", "DEF:dmarcnone_12=$rrd_dmarc_12:dmarcnone:AVERAGE", "DEF:mdmarcnone_12=$rrd_dmarc_12:dmarcnone:MAX", "DEF:dmarcnone_13=$rrd_dmarc_13:dmarcnone:AVERAGE", "DEF:mdmarcnone_13=$rrd_dmarc_13:dmarcnone:MAX", "DEF:dmarcnone_14=$rrd_dmarc_14:dmarcnone:AVERAGE", "DEF:mdmarcnone_14=$rrd_dmarc_14:dmarcnone:MAX", "CDEF:rdmarcnone=dmarcnone_11,dmarcnone_12,+,dmarcnone_13,+,dmarcnone_14,+,60,*", "CDEF:ddmarcnone=dmarcnone_11,dmarcnone_12,+,dmarcnone_13,+,dmarcnone_14,+,UN,0,dmarcnone_11,dmarcnone_12,+,dmarcnone_13,+,dmarcnone_14,+,IF,$step,*", "CDEF:sdmarcnone=PREV,UN,ddmarcnone,PREV,IF,ddmarcnone,+", "CDEF:rmdmarcnone=mdmarcnone_11,mdmarcnone_12,+,mdmarcnone_13,+,mdmarcnone_14,+,60,*", "LINE2:rdmarcnone#$color{dmarcnone}:DMARC none ", 'GPRINT:sdmarcnone:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcnone:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcnone:MAX:max\: %11.0lf msgs/min\l', "DEF:dmarcfail_11=$rrd_dmarc_11:dmarcfail:AVERAGE", "DEF:mdmarcfail_11=$rrd_dmarc_11:dmarcfail:MAX", "DEF:dmarcfail_12=$rrd_dmarc_12:dmarcfail:AVERAGE", "DEF:mdmarcfail_12=$rrd_dmarc_12:dmarcfail:MAX", "DEF:dmarcfail_13=$rrd_dmarc_13:dmarcfail:AVERAGE", "DEF:mdmarcfail_13=$rrd_dmarc_13:dmarcfail:MAX", "DEF:dmarcfail_14=$rrd_dmarc_14:dmarcfail:AVERAGE", "DEF:mdmarcfail_14=$rrd_dmarc_14:dmarcfail:MAX", "CDEF:rdmarcfail=dmarcfail_11,dmarcfail_12,+,dmarcfail_13,+,dmarcfail_14,+,60,*", "CDEF:ddmarcfail=dmarcfail_11,dmarcfail_12,+,dmarcfail_13,+,dmarcfail_14,+,UN,0,dmarcfail_11,dmarcfail_12,+,dmarcfail_13,+,dmarcfail_14,+,IF,$step,*", "CDEF:sdmarcfail=PREV,UN,ddmarcfail,PREV,IF,ddmarcfail,+", "CDEF:rmdmarcfail=mdmarcfail_11,mdmarcfail_12,+,mdmarcfail_13,+,mdmarcfail_14,+,60,*", "LINE2:rdmarcfail#$color{dmarcfail}:DMARC fail ", 'GPRINT:sdmarcfail:MAX:total\: %15.0lf msgs', 'GPRINT:rdmarcfail:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rmdmarcfail:MAX:max\: %11.0lf msgs/min\l', ); } sub graph_queue($$) { my ($range, $file) = @_; my $step = $range*$points_per_sample/$xpoints; rrd_graph($range, $file, $ypoints, "DEF:deferred_11=$rrd_queue_11:deferred:AVERAGE", "DEF:deferred_12=$rrd_queue_12:deferred:AVERAGE", "DEF:deferred_13=$rrd_queue_13:deferred:AVERAGE", "DEF:deferred_14=$rrd_queue_14:deferred:AVERAGE", "CDEF:rdeferred=deferred_11,deferred_12,+,deferred_13,+,deferred_14,+", "AREA:rdeferred#$color{deferred}:Deferred ", 'GPRINT:rdeferred:MAX:total\: %15.0lf msgs', 'GPRINT:rdeferred:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:rdeferred:MAX:max\: %11.0lf msgs/min\l', "DEF:active_11=$rrd_queue_11:active:AVERAGE", "DEF:active_12=$rrd_queue_12:active:AVERAGE", "DEF:active_13=$rrd_queue_13:active:AVERAGE", "DEF:active_14=$rrd_queue_14:active:AVERAGE", "CDEF:ractive=active_11,active_12,+,active_13,+,active_14,+,active_11,+,active_12,+,active_13,+,active_14,+", "LINE2:ractive#$color{active}:Active+Incoming+Maildrop", 'GPRINT:ractive:MAX:total\: %15.0lf msgs', 'GPRINT:ractive:AVERAGE:avg\: %12.2lf msgs/min', 'GPRINT:ractive:MAX:max\: %11.0lf msgs/min\l', ); } sub print_html() { print "Content-Type: text/html\n\n"; print <<HEADER; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mailserver Statistiken auf $host</title> <meta http-equiv="Refresh" content="300" /> <meta http-equiv="Pragma" content="no-cache" /> <link rel="stylesheet" href="mailgraph.css" type="text/css" /> </head> <body> HEADER print "<h1>Mailserver Statistiken für die <br> die Mailserver bei nausch.org</h1>\n"; print "<h3>(kumulierte Werte von "; print "<a href='$url1'>MX11</a>, "; print "<a href='$url2'>MX12</a>, "; print "<a href='$url3'>MX13</a> und "; print "<a href='$url4'>MX14</a></a>)</h3>\n"; print "<ul id=\"jump\">\n"; for my $n (0..$#graphs) { print " <li><a href=\"#G$n\">$graphs[$n]{title}</a> </li>\n"; } print "</ul>\n"; for my $n (0..$#graphs) { print "<h2 id=\"G$n\">$graphs[$n]{title}</h2>\n"; print "<h4><center>Mail Ein und -Ausgang</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-n\" alt=\"mailgraph - received and sent\"/></p>\n"; print "<h4><center>geblockte Nachrichten</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-e\" alt=\"mailgraph - blocked\"/></p>\n"; print "<h4><center>Greylisting Übersicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-g\" alt=\"mailgraph - greylist\"/></p>\n"; print "<h4><center>Greylisting Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-d\" alt=\"mailgraph - greystats\"/></p>\n"; print "<h4><center>Postscreen (positive) Übersicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-v\" alt=\"mailgraph - postscreen\"/></p>\n"; print "<h4><center>Postscreen Detailansicht</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-w\" alt=\"mailgraph - postscreenstats\"/></p>\n"; print "<h4><center>Übersicht Mail-Queues</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-q\" alt=\"mailgraph - mailqueues\"/></p>\n"; print "<h4><center>ausgehende DANE/TLSA-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-t\" alt=\"mailgraph - dane checked\"/></p>\n"; print "<h4><center>ankommende TLS-gesicherte Verbindungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-i\" alt=\"mailgraph - smtpd details\"/></p>\n"; print "<h4><center>Sender policy Framework - SPF-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-f\" alt=\"mailgraph - spf checked\"/></p>\n"; print "<h4><center>DomainKeys Identified Mail - DKIM-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-m\" alt=\"mailgraph - dkim checked\"/></p>\n"; print "<h4><center>Domain-based Message Authentication, Reporting & Conformance - DMARC-Prüfungen</center></h4>\n"; print "<h5><center>( <a href='$url11$n'>MX11</a>\n"; print "<a href='$url12$n'>MX12</a>\n"; print "<a href='$url13$n'>MX13</a>\n"; print "<a href='$url14$n'>MX14</a> )</center></h5>\n"; print "<p><img src=\"$scriptname?${n}-c\" alt=\"mailgraph - dmarc checked\"/></p>\n"; } print <<FOOTER; <hr/> <table border="0" style="font-size:12px" width="900"> <colgroup> <col width="225"> <col width="430"> <col width="125"> </colgroup> <tr class="row0"> <td class="col0 leftalign"> <a href="http://dokuwiki.nausch.org/doku.php/centos:mail_c7:mta_13?&#mailgraph_nextgeneration">Mailgraph(-ng) </a>$VERSION by <a href="mailto:django@nausch.org?subject=Mailgraph-NG%20for%20my%20Mailserver">Django</a> based on </td> <td> <a href="http://david.schweikert.ch/">David Schweikert's</a> <a href="http://mailgraph.schweikert.ch/">Mailgraph</a>, <a href="http://www.gichenbacher.de/kontakt">Markus Neubauer's </a> <a href="http://www.std-soft.com/bfaq/46-k-faq-server/117-greygraph-mail-statistik.html">Greygraph</a>, </td> <td class="col2 rightalign" rowspan="3"> <a href="http://oss.oetiker.ch/rrdtool/"><img src="rrdtool-3dlogo.png" alt="" width="135" height="50" align="right" align="middle"/></a> </td> </tr> <tr class="row1"> <td class="col0 leftalign"> </td> <td class="col1 leftalign"> <a href="http://www.arschkrebs.de/">Ralf Hildebrandt's </a><a href="http://www.arschkrebs.de/postfix/queuegraph">Queuegraph</a> and <a href="https://www.kernel-error.de/">Sebastian van de Meer's </a> <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt">mailgraphpatch 1</a> and <a href="https://www.kernel-error.de/postfix/mailgraph-aufgebohrt-2">mailgraphpatch 2</a> </td> </tr> </table> </body> </html> FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { my $uri = $ENV{REQUEST_URI} || ''; $uri =~ s/\/[^\/]+$//; $uri =~ s/\//,/g; $uri =~ s/(\~|\%7E)/tilde,/g; mkdir $tmp_dir, 0777 unless -d $tmp_dir; mkdir "$tmp_dir/$uri", 0777 unless -d "$tmp_dir/$uri"; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-n$/) { my $file = "$tmp_dir/$uri/mailgraph_$1.png"; graph($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-e$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_err.png"; graph_virus($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-g$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greylist.png"; graph_greylist($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_greystats.png"; graph_greystats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-v$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreen.png"; graph_postscreen($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-w$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_postscreenstats.png"; graph_postscreenstats($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-q$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_queue.png"; graph_queue($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dane.png"; graph_dane($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-i$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_smtpd.png"; graph_smtpd($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-f$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_spf.png"; graph_spf($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dkim.png"; graph_dkim($graphs[$1]{seconds}, $file); send_image($file); } elsif($img =~ /^(\d+)-c$/) { my $file = "$tmp_dir/$uri/mailgraph_$1_dmarc.png"; graph_dmarc($graphs[$1]{seconds}, $file); send_image($file); } else { die "ERROR: invalid argument\n"; } } else { print_html; } } main;
Apache VHost anlegen
Damit wir bequem von unserem Browser aus, die aktuellen Graphiken abfragen können, bearbeiten wir entweder die aus dem RPM stammende Konfigurationsdatei oder legen wir nun einen passenden VHost an.
# vim /etc/httpd/conf.d/vhosts.conf
- /etc/httpd/conf.d/vhosts.conf
# # mailgraph.nausch.org # <VirtualHost *:80> ServerAdmin webmaster@nausch.org ServerName mailgraph.nausch.org ServerAlias www.mailgraph.nausch.org ServerPath / DocumentRoot "/usr/share/mailgraph-ng/mx" AddHandler cgi-script .cgi <Directory "/usr/share/mailgraph/mx"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi require IP 10.0. </Directory> <Directory "/usr/share/mailgraph/mx11"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi require IP 10.0. </Directory> <Directory "/usr/share/mailgraph/mx12"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi require IP 10.0. </Directory> <Directory "/usr/share/mailgraph/mx13"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi require IP 10.0. </Directory <Directory "/usr/share/mailgraph/mx14"> AllowOverride None Options +ExecCGI DirectoryIndex mailgraph.cgi require IP 10.0. </Directory ErrorLog logs/mailgraph_error.log CustomLog logs/mailgraph_access.log combined </VirtualHost>
Bevor wir bei unserem Webserver eine Reload der Konfiguration vornehmen, testen wir unsere neue Konfigurationsdatei auf syntaktische Fehler.
# apachectl -t
Syntax OK
Da keine Fehler aufgetreten sind, aktivieren wir die neue Konfiguration durch einen Reload des Webserver-Daemon.
# systemctl reload postfix
NGiNX vHost anlegen
Nutzen wir als Webserver NGiNX können wir auch hier schnell und einfach einen passenden vHOST anlegen.
# vim /etc/nginx/conf.d/vhosts.conf
- /etc/nginx/conf.d/vhosts.conf
server { listen 80; server_name mailgraph.nausch.org; access_log /var/log/nginx/mailgraph_access.log; error_log /var/log/nginx/mailgraph_errors.log; root /usr/share/mailgraph-ng/; index mailgraph.cgi; location ~ \.php { fastcgi_split_path_info ^(.+\.cgi)(/.+)$; fastcgi_index mailgraph.cgi; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
Den Parameter fastcgi_pass setzen wir im übrigen auf den Wert aus der Konfigurationsdatei /etc/php-fpm.d/www.conf des PHP FastCGI Process Manager-Daemon php-fpm.
Haben wir die Konfigurationsdatei vervollständigt, prüfen wir diese noch auf syntaktische Fehler.
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Somit können wir unsere Konfiguration nun noch aktivieren.
# systemctl reload nginx
Webaufruf
Rufen wir nun die definierte Webseite unserer Gesamtstatistikübersicht auf, erhalten wir nachfolgende Ansicht.
Hier können wir nun gezielt einzelne Systeme auswählen, bzw. bei einer ausgewählten Detailstatistikseite zur Summenseite hin und her wechseln.
AWStats
Auch mit Hilfe des Projektes/Paketes AWStats kann man das Maillog unseres Mailserver auswerten. Mit Hilfe zweier Perlscripte erstellen wir nun täglich einen Statusbericht über das was unserem MX widerfahren ist, und rufen später diese Information via Firefox/Konqueror ab. :)
Installation
Die Grundinstallation von AWStats ist im Kapitel Logfileanalyse und -Auswertung mit AWStats unter CentOS 7.x ausfürlich beschrieben.
Konfiguration
Zur eigentlichen Logfileauswertung legen wir uns nun eine eigene Konfigurationdatei für AWStats an. Als Vorlage nutzen wir hier die, im RPM mitgelieferte default-Datei /etc/awstats/awstats.localhost.localdomain.conf.
# cp /etc/awstats/awstats.localhost.localdomain.conf /etc/awstats/awstats.mx1.nausch.org.conf
Darin passen wir nun alle Konfigurationsoptionen, die für das maillog von Postfix relevant sind, an.
# vim /etc/awstats/awstats.mx1.nausch.org.conf
- /etc/awstats/awstats.mx1.nausch.org.conf
# AWSTATS CONFIGURE FILE 7.0 #----------------------------------------------------------------------------- # Copy this file into awstats.www.mydomain.conf and edit this new config file # to setup AWStats (See documentation in docs/ directory). # The config file must be in /etc/awstats, /usr/local/etc/awstats or /etc (for # Unix/Linux) or same directory than awstats.pl (Windows, Mac, Unix/Linux...) # To include an environment variable in any parameter (AWStats will replace # it with its value when reading it), follow the example: # Parameter="__ENVNAME__" # Note that environment variable AWSTATS_CURRENT_CONFIG is always defined with # the config value in an AWStats running session and can be used like others. #----------------------------------------------------------------------------- # $Revision: 1.350 $ - $Author: eldy $ - $Date: 2010/09/29 19:16:21 $ #----------------------------------------------------------------------------- # MAIN SETUP SECTION (Required to make AWStats work) #----------------------------------------------------------------------------- # "LogFile" contains the web, ftp or mail server log file to analyze. # Possible values: A full path, or a relative path from awstats.pl directory. # Example: "/var/log/apache/access.log" # Example: "../logs/mycombinedlog.log" # You can also use tags in this filename if you need a dynamic file name # depending on date or time (Replacement is made by AWStats at the beginning # of its execution). This is available tags : # %YYYY-n is replaced with 4 digits year we were n hours ago # %YY-n is replaced with 2 digits year we were n hours ago # %MM-n is replaced with 2 digits month we were n hours ago # %MO-n is replaced with 3 letters month we were n hours ago # %DD-n is replaced with day we were n hours ago # %HH-n is replaced with hour we were n hours ago # %NS-n is replaced with number of seconds at 00:00 since 1970 # %WM-n is replaced with the week number in month (1-5) # %Wm-n is replaced with the week number in month (0-4) # %WY-n is replaced with the week number in year (01-52) # %Wy-n is replaced with the week number in year (00-51) # %DW-n is replaced with the day number in week (1-7, 1=sunday) # use n=24 if you need (1-7, 1=monday) # %Dw-n is replaced with the day number in week (0-6, 0=sunday) # use n=24 if you need (0-6, 0=monday) # Use 0 for n if you need current year, month, day, hour... # Example: "/var/log/access_log.%YYYY-0%MM-0%DD-0.log" # Example: "C:/WINNT/system32/LogFiles/W3SVC1/ex%YY-24%MM-24%DD-24.log" # You can also use a pipe if log file come from a pipe : # Example: "gzip -d </var/log/apache/access.log.gz |" # If there are several log files from load balancing servers : # Example: "/pathtotools/logresolvemerge.pl *.log |" # # Django : 2012-07-03 # default: LogFile="/var/log/httpd/access_log" LogFile="perl /usr/bin/maillogconvert.pl standard < /var/log/maillog |" # Enter the log file type you want to analyze. # Possible values: # W - For a web log file # S - For a streaming log file # M - For a mail log file # F - For a ftp log file # Example: W # Default: W # # Django : 2012-07-03 # default: LogType=W LogType=M # Enter here your log format (Must match your web server config. See setup # instructions in documentation to know how to configure your web server to # have the required log format). # Possible values: 1,2,3,4 or "your_own_personalized_log_format" # 1 - Apache or Lotus Notes/Domino native combined log format (NCSA combined/XLF/ELF log format) # 2 - IIS or ISA format (IIS W3C log format). See FAQ-COM115 For ISA. # 3 - Webstar native log format. # 4 - Apache or Squid native common log format (NCSA common/CLF log format) # With LogFormat=4, some features (browsers, os, keywords...) can't work. # "your_own_personalized_log_format" = If your log is ftp, mail or other format, # you must use following keys to define the log format string (See FAQ for # ftp, mail or exotic web log format examples): # %host Client hostname or IP address (or Sender host for mail log) # %host_r Receiver hostname or IP address (for mail log) # %lognamequot Authenticated login/user with format: "john" # %logname Authenticated login/user with format: john # %time1 Date and time with format: [dd/mon/yyyy:hh:mm:ss +0000] or [dd/mon/yyyy:hh:mm:ss] # %time2 Date and time with format: yyyy-mm-dd hh:mm:ss # %time3 Date and time with format: Mon dd hh:mm:ss or Mon dd hh:mm:ss yyyy # %time4 Date and time with unix timestamp format: dddddddddd # %methodurl Method and URL with format: "GET /index.html HTTP/x.x" # %methodurlnoprot Method and URL with format: "GET /index.html" # %method Method with format: GET # %url URL only with format: /index.html # %query Query string (used by URLWithQuery option) # %code Return code status (with format for web log: 999) # %bytesd Size of document in bytes # %refererquot Referer page with format: "http://from.com/from.htm" # %referer Referer page with format: http://from.com/from.htm # %uabracket User agent with format: [Mozilla/4.0 (compatible, ...)] # %uaquot User agent with format: "Mozilla/4.0 (compatible, ...)" # %ua User agent with format: Mozilla/4.0_(compatible...) # %gzipin mod_gzip compression input bytes: In:XXX # %gzipout mod_gzip compression output bytes & ratio: Out:YYY:ZZpct. # %gzipratio mod_gzip compression ratio: ZZpct. # %deflateratio mod_deflate compression ratio with format: (ZZ) # %email EMail sender (for mail log) # %email_r EMail receiver (for mail log) # %virtualname Web sever virtual hostname. Use this tag when same log # contains data of several virtual web servers. AWStats # will discard records not in SiteDomain nor HostAliases # %cluster If log file is provided from several computers (merged by # logresolvemerge.pl), use this to define cluster id field. # %extraX Another field that you plan to use for building a # personalized report with ExtraSection feature (See later). # If your log format has some fields not included in this list, use: # %other Means another not used field # %otherquot Means another not used double quoted field # # Examples for Apache combined logs (following two examples are equivalent): # LogFormat = 1 # LogFormat = "%host %other %logname %time1 %methodurl %code %bytesd %refererquot %uaquot" # # Example for IIS: # LogFormat = 2 # # Django : 2012-07-03 # default: LogFormat=1 LogFormat="%time2 %email %email_r %host %host_r %method %url %code %bytesd" # If your log field's separator is not a space, you can change this parameter. # This parameter is not used if LogFormat is a predefined value (1,2,3,4) # Backslash can be used as escape character. # Example: " " # Example: "\t" # Example: "\|" # Example: "," # Default: " " # LogSeparator=" " # "SiteDomain" must contain the main domain name, or the main intranet web # server name, used to reach the web site. # If you share the same log file for several virtual web servers, this # parameter is used to tell AWStats to filter record that contains records for # this virtual host name only (So check that this virtual hostname can be # found in your log file and use a personalized log format that include the # %virtualname tag). # But for multi hosting a better solution is to have one log file for each # virtual web server. In this case, this parameter is only used to generate # full URL's links when ShowLinksOnUrl option is set to 1. # If analyzing mail log, enter here the domain name of mail server. # Example: "myintranetserver" # Example: "www.domain.com" # Example: "ftp.domain.com" # Example: "domain.com" # # Django : 2012-07-03 # default: SiteDomain="localhost.localdomain" SiteDomain="mx1.nausch.org" # Enter here all other possible domain names, addresses or virtual host # aliases someone can use to access your site. Try to keep only the minimum # number of possible names/addresses to have the best performances. # You can repeat the "SiteDomain" value in this list. # This parameter is used to analyze referer field in log file and to help # AWStats to know if a referer URL is a local URL of same site or an URL of # another site. # Note: Use space between each value. # Note: You can use regular expression values writing value with REGEX[value]. # Note: You can also use @/mypath/myfile if list of aliases are in a file. # Example: "www.myserver.com localhost 127.0.0.1 REGEX[mydomain\.(net|org)$]" # # Django : 2012-07-03 # default: HostAliases="localhost 127.0.0.1" HostAliases=„localhost 127.0.0.1 REGEX[nausch\.org$] REGEX[www\.nausch\.org$]“ # If you want to have hosts reported by name instead of ip address, AWStats # need to make reverse DNS lookups (if not already done in your log file). # With DNSLookup to 0, all hosts will be reported by their IP addresses and # not by the full hostname of visitors (except if names are already available # in log file). # If you want/need to set DNSLookup to 1, don't forget that this will reduce # dramatically AWStats update process speed. Do not use on large web sites. # Note: Reverse DNS lookup is done on IPv4 only (Enable ipv6 plugin for IPv6). # Note: Result of DNS Lookup can be used to build the Country report. However # it is highly recommanded to enable the plugin 'geoip' or 'geoipfree' to # have an accurate Country report with no need of DNS Lookup. # Possible values: # 0 - No DNS Lookup # 1 - DNS Lookup is fully enabled # 2 - DNS Lookup is made only from static DNS cache file (if it exists) # Default: 2 # DNSLookup=2 # When AWStats updates its statistics, it stores results of its analysis in # files (AWStats database). All those files are written in the directory # defined by the "DirData" parameter. Set this value to the directory where # you want AWStats to save its database and working files into. # Warning: If you want to be able to use the "AllowToUpdateStatsFromBrowser" # feature (see later), you need "Write" permissions by web server user on this # directory (and "Modify" for Windows NTFS file systems). # Example: "/var/lib/awstats" # Example: "../data" # Example: "C:/awstats_data_dir" # Default: "." (means same directory as awstats.pl) # DirData="/var/www/awstats" # Relative or absolute web URL of your awstats cgi-bin directory. # This parameter is used only when AWStats is run from command line # with -output option (to generate links in HTML reported page). # Example: "/awstats" # Default: "/cgi-bin" (means awstats.pl is in "/yourwwwroot/cgi-bin") # DirCgi="/awstats" # Relative or absolute web URL of your awstats icon directory. # If you build static reports ("... -output > outputpath/output.html"), enter # path of icon directory relative to the output directory 'outputpath'. # Example: "/awstatsicons" # Example: "../icon" # Default: "/icon" (means you must copy icon directories in "/mywwwroot/icon") # DirIcons="/awstats/icon" # When this parameter is set to 1, AWStats adds a button on report page to # allow to "update" statistics from a web browser. Warning, when "update" is # made from a browser, AWStats is run as a CGI by the web server user defined # in your web server (user "nobody" by default with Apache, "IUSR_XXX" with # IIS), so the "DirData" directory and all already existing history files # awstatsMMYYYY[.xxx].txt must be writable by this user. Change permissions if # necessary to "Read/Write" (and "Modify" for Windows NTFS file systems). # Warning: Update process can be long so you might experience "time out" # browser errors if you don't launch AWStats frequently enough. # When set to 0, update is only made when AWStats is run from the command # line interface (or a task scheduler). # Possible values: 0 or 1 # Default: 0 # # Django : 2012-07-03 # default: AllowToUpdateStatsFromBrowser=0 AllowToUpdateStatsFromBrowser=1 # AWStats saves and sorts its database on a month basis (except if using # databasebreak option from command line). # However, if you choose the -month=all from command line or # value '-Year-' from CGI combo form to have a report for all year, AWStats # needs to reload all data for full year (each month), and sort them, # requiring a large amount of time, memory and CPU. This might be a problem # for web hosting providers that offer AWStats for large sites, on shared # servers, to non CPU cautious customers. # For this reason, the 'full year' is only enabled on Command Line by default. # You can change this by setting this parameter to 0, 1, 2 or 3. # Possible values: # 0 - Never allowed # 1 - Allowed on CLI only, -Year- value in combo is not visible # 2 - Allowed on CLI only, -Year- value in combo is visible but not allowed # 3 - Possible on CLI and CGI # Default: 2 # AllowFullYearView=2 #----------------------------------------------------------------------------- # OPTIONAL SETUP SECTION (Not required but increase AWStats features) #----------------------------------------------------------------------------- # When the update process runs, AWStats can set a lock file in TEMP or TMP # directory. This lock is to avoid to have 2 update processes running at the # same time to prevent unknown conflicts problems and avoid DoS attacks when # AllowToUpdateStatsFromBrowser is set to 1. # Because, when you use lock file, you can experience sometimes problems in # lock file not correctly removed (killed process for example requires that # you remove the file manualy), this option is not enabled by default (Do # not enable this option with no console server access). # Change : Effective immediatly # Possible values: 0 or 1 # Default: 0 # EnableLockForUpdate=1 # AWStats can do reverse DNS lookups through a static DNS cache file that was # previously created manually. If no path is given in static DNS cache file # name, AWStats will search DirData directory. This file is never changed. # This option is not used if DNSLookup=0. # Note: DNS cache file format is 'minsince1970 ipaddress resolved_hostname' # or just 'ipaddress resolved_hostname' # Change : Effective for new updates only # Example: "/mydnscachedir/dnscache" # Default: "dnscache.txt" # DNSStaticCacheFile="dnscache.txt" # AWStats can do reverse DNS lookups through a DNS cache file that was created # by a previous run of AWStats. This file is erased and recreated after each # statistics update process. You don't need to create and/or edit it. # AWStats will read and save this file in DirData directory. # This option is used only if DNSLookup=1. # Note: If a DNSStaticCacheFile is available, AWStats will check for DNS # lookup in DNSLastUpdateCacheFile after checking into DNSStaticCacheFile. # Change : Effective for new updates only # Example: "/mydnscachedir/dnscachelastupdate" # Default: "dnscachelastupdate.txt" # DNSLastUpdateCacheFile="dnscachelastupdate.txt" # You can specify specific IP addresses that should NOT be looked up in DNS. # This option is used only if DNSLookup=1. # Note: Use space between each value. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "123.123.123.123 REGEX[^192\.168\.]" # Default: "" # SkipDNSLookupFor="" # The following two parameters allow you to protect a config file from being # read by AWStats when called from a browser if web user has not been # authenticated. Your AWStats program must be in a web protected "realm" (With # Apache, you can use .htaccess files to do so. With other web servers, see # your server setup manual). # Change : Effective immediatly # Possible values: 0 or 1 # Default: 0 # # Django : 2012-07-03 # default: AllowAccessFromWebToAuthenticatedUsersOnly=0 #AllowAccessFromWebToAuthenticatedUsersOnly=1 AllowAccessFromWebToAuthenticatedUsersOnly=0 # This parameter gives the list of all authorized authenticated users to view # statistics for this domain/config file. This parameter is used only if # AllowAccessFromWebToAuthenticatedUsersOnly is set to 1. # Change : Effective immediatly # Example: "user1 user2" # Example: "__REMOTE_USER__" # Default: "" # # Django : 2012-07-03 # default: AllowAccessFromWebToFollowingAuthenticatedUsers="" # AllowAccessFromWebToFollowingAuthenticatedUsers="django bigchief" AllowAccessFromWebToFollowingAuthenticatedUsers="" # When this parameter is defined to something, the IP address of the user that # reads its statistics from a browser (when AWStats is used as a CGI) is # checked and must match one of the IP address values or ranges. # Change : Effective immediatly # Example: "127.0.0.1 123.123.123.1-123.123.123.255" # Default: "" # AllowAccessFromWebToFollowingIPAddresses="" # If the "DirData" directory (see above) does not exist, AWStats return an # error. However, you can ask AWStats to create it. # This option can be used by some Web Hosting Providers that has defined a # dynamic value for DirData (for example DirData="/home/__REMOTE_USER__") and # don't want to have to create a new directory each time they add a new user. # Change : Effective immediatly # Possible values: 0 or 1 # Default: 0 # CreateDirDataIfNotExists=0 # You can choose in which format the Awstats history database is saved. # Note: Using "xml" format make AWStats building database files three times # larger than using "text" format. # Change : Database format is switched after next update # Possible values: text or xml # Default: text # BuildHistoryFormat=text # If you prefer having the report output pages be built as XML compliant pages # instead of simple HTML pages, you can set this to 'xhtml' (May not work # properly with old browsers). # Change : Effective immediatly # Possible values: html or xhtml # Default: html # BuildReportFormat=html # AWStats databases can be updated from command line of from a browser (when # used as a cgi program). So AWStats database files need write permission # for both command line user and default web server user (nobody for Unix, # IUSR_xxx for IIS/Windows,...). # To avoid permission problems between update process (run by an admin user) # and CGI process (ran by a low level user), AWStats can save its database # files with read and write permissions for everyone. # By default, AWStats keeps default user permissions on updated files. If you # set AllowToUpdateStatsFromBrowser to 1, you can change this parameter to 1. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # # Django : 2012-07-03 # default: SaveDatabaseFilesWithPermissionsForEveryone=0 SaveDatabaseFilesWithPermissionsForEveryone=1 # AWStats can purge log file, after analyzing it. Note that AWStats is able # to detect new lines in a log file, to process only them, so you can launch # AWStats as often as you want, even with this parameter to 0. # With 0, no purge is made, so you must use a scheduled task or a web server # that make this purge frequently. # With 1, the purge of the log file is made each time AWStats update is run. # This parameter doesn't work with IIS (This web server doesn't let its log # file to be purged). # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # PurgeLogFile=0 # When PurgeLogFile is setup to 1, AWStats will clean your log file after # processing it. You can however keep an archive file of all processed log # records by setting this parameter (For example if you want to use another # log analyzer). The archived log file is saved in "DirData" with name # awstats_archive.configname[.suffix].log # This parameter is not used if PurgeLogFile=0 # Change : Effective for new updates only # Possible values: 0, 1, or tags (See LogFile parameter) for suffix # Example: 1 # Example: %YYYY%MM%DD # Default: 0 # ArchiveLogRecords=0 # Each time you run the update process, AWStats overwrites the 'historic file' # for the month (awstatsMMYYYY[.*].txt) with the updated one. # When write errors occurs (IO, disk full,...), this historic file can be # corrupted and must be deleted. Because this file contains information of all # past processed log files, you will loose old stats if removed. So you can # ask AWStats to save last non corrupted file in a .bak file. This file is # stored in "DirData" directory with other 'historic files'. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # KeepBackupOfHistoricFiles=1 # Default index page name for your web server. # Change : Effective for new updates only # Example: "index.php index.html default.html" # Default: "index.php index.html" # DefaultFile="index.php index.html" # Do not include access from clients that match following criteria. # If your log file contains IP addresses in host field, you must enter here # matching IP addresses criteria. # If DNS lookup is already done in your log file, you must enter here hostname # criteria, else enter ip address criteria. # The opposite parameter of "SkipHosts" is "OnlyHosts". # Note: Use space between each value. This parameter is not case sensitive. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "127.0.0.1 REGEX[^192\.168\.] REGEX[^10\.]" # Example: "localhost REGEX[^.*\.localdomain$]" # Default: "" # # Django : 2012-07-03 # default: SkipHosts="127.0.0.1 SkipHosts="" # Do not include access from clients with a user agent that match following # criteria. If you want to exclude a robot, you should update the robots.pm # file instead of this parameter. # The opposite parameter of "SkipUserAgents" is "OnlyUserAgents". # Note: Use space between each value. This parameter is not case sensitive. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "konqueror REGEX[ua_test_v\d\.\d]" # Default: "" # SkipUserAgents="" # Use SkipFiles to ignore access to URLs that match one of following entries. # You can enter a list of not important URLs (like framed menus, hidden pages, # etc...) to exclude them from statistics. You must enter here exact relative # URL as found in log file, or a matching REGEX value. Check apply on URL with # all its query paramaters. # For example, to ignore /badpage.php, just add "/badpage.php". To ignore all # pages in a particular directory, add "REGEX[^\/directorytoexclude]". # The opposite parameter of "SkipFiles" is "OnlyFiles". # Note: Use space between each value. This parameter is or not case sensitive # depending on URLNotCaseSensitive parameter. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "/badpage.php /page.php?param=x REGEX[^\/excludedirectory]" # Default: "" # SkipFiles="" # Use SkipReferrersBlackList if you want to exclude records coming from a SPAM # referrer. Parameter must receive a local file name containing rules applied # on referrer field. If parameter is empty, no filter is applied. # An example of such a file is available in lib/blacklist.txt # Change : Effective for new updates only # Example: "/mylibpath/blacklist.txt" # Default: "" # # WARNING!! Using this feature make AWStats running very slower (5 times slower # with black list file provided with AWStats ! # SkipReferrersBlackList="" # Include in stats, only accesses from hosts that match one of following # entries. For example, if you want AWStats to filter access to keep only # stats for visits from particular hosts, you can add those host names in # this parameter. # If DNS lookup is already done in your log file, you must enter here hostname # criteria, else enter ip address criteria. # The opposite parameter of "OnlyHosts" is "SkipHosts". # Note: Use space between each value. This parameter is not case sensitive. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "127.0.0.1 REGEX[^192\.168\.] REGEX[^10\.]" # Default: "" # OnlyHosts="" # Include in stats, only accesses from user agent that match one of following # entries. For example, if you want AWStats to filter access to keep only # stats for visits from particular browsers, you can add their user agents # string in this parameter. # The opposite parameter of "OnlyUserAgents" is "SkipUserAgents". # Note: Use space between each value. This parameter is not case sensitive. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "msie" # Default: "" # OnlyUserAgents="" # Include in stats, only accesses from authenticated users that match one of # following entries. For example, if you want AWStats to filter access to keep # only stats for authenticated users, you can add those users names in # this parameter. Useful for statistics for per user ftp logs. # Note: Use space between each value. This parameter is not case sensitive. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "john bob REGEX[^testusers]" # Default: "" # OnlyUsers="" # Include in stats, only accesses to URLs that match one of following entries. # For example, if you want AWStats to filter access to keep only stats that # match a particular string, like a particular directory, you can add this # directory name in this parameter. # The opposite parameter of "OnlyFiles" is "SkipFiles". # Note: Use space between each value. This parameter is or not case sensitive # depending on URLNotCaseSensitive parameter. # Note: You can use regular expression values writing value with REGEX[value]. # Change : Effective for new updates only # Example: "REGEX[marketing_directory] REGEX[office\/.*\.(csv|sxw)$]" # Default: "" # OnlyFiles="" # Add here a list of kind of url (file extension) that must be counted as # "Hit only" and not as a "Hit" and "Page/Download". You can set here all # image extensions as they are hit downloaded that must be counted but they # are not viewed pages. URLs with such extensions are not included in the TOP # Pages/URL report. # Note: If you want to exclude particular URLs from stats (No Pages and no # Hits reported), you must use SkipFiles parameter. # Change : Effective for new updates only # Example: "css js class gif jpg jpeg png bmp ico rss xml swf zip arj rar gz z bz2 wav mp3 wma mpg avi" # Example: "" # Default: "css js class gif jpg jpeg png bmp ico rss xml swf" # NotPageList="css js class gif jpg jpeg png bmp ico rss xml swf" # By default, AWStats considers that records found in web log file are # successful hits if HTTP code returned by server is a valid HTTP code (200 # and 304). Any other code are reported in HTTP status chart. # Note that HTTP 'control codes', like redirection (302, 305) are not added by # default in this list as they are not pages seen by a visitor but are # protocol exchange codes to tell the browser to ask another page. Because # this other page will be counted and seen with a 200 or 304 code, if you # add such codes, you will have 2 pages viewed reported for only one in facts. # Change : Effective for new updates only # Example: "200 304 302 305" # Default: "200 304" # ValidHTTPCodes="200 304" # By default, AWStats considers that records found in mail log file are # successful mail transfers if field that represent return code in analyzed # log file match values defined by this parameter. # Change : Effective for new updates only # Example: "1 250 200" # Default: "1 250" # ValidSMTPCodes="1 250" # Some web servers on some Operating systems (IIS-Windows) consider that a # login with same value but different case are the same login. To tell AWStats # to also consider them as one, set this parameter to 1. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # AuthenticatedUsersNotCaseSensitive=0 # Some web servers on some Operating systems (IIS-Windows) considers that two # URLs with same value but different case are the same URL. To tell AWStats to # also considers them as one, set this parameter to 1. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # URLNotCaseSensitive=0 # Keep or remove the anchor string you can find in some URLs. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # URLWithAnchor=0 # In URL links, "?" char is used to add parameter's list in URLs. Syntax is: # /mypage.html?param1=value1¶m2=value2 # However, some servers/sites use also other chars to isolate dynamic part of # their URLs. You can complete this list with all such characters. # Change : Effective for new updates only # Example: "?;," # Default: "?;" # URLQuerySeparators="?;" # Keep or remove the query string to the URL in the statistics for individual # pages. This is primarily used to differentiate between the URLs of dynamic # pages. If set to 1, mypage.html?id=x and mypage.html?id=y are counted as two # different pages. # Warning, when set to 1, memory required to run AWStats is dramatically # increased if you have a lot of changing URLs (for example URLs with a random # id inside). Such web sites should not set this option to 1 or use seriously # the next parameter URLWithQueryWithOnlyFollowingParameters (or eventually # URLWithQueryWithoutFollowingParameters). # Change : Effective for new updates only # Possible values: # 0 - URLs are cleaned from the query string (ie: "/mypage.html") # 1 - Full URL with query string is used (ie: "/mypage.html?p=x&q=y") # Default: 0 # URLWithQuery=0 # When URLWithQuery is on, you will get the full URL with all parameters in # URL reports. But among thoose parameters, sometimes you don't need a # particular parameter because it does not identify the page or because it's # a random ID changing for each access even if URL points to same page. In # such cases, it is higly recommanded to ask AWStats to keep only parameters # you need (if you know them) before counting, manipulating and storing URL. # Enter here list of wanted parameters. For example, with "param", one hit on # /mypage.cgi?param=abc&id=Yo4UomP9d and /mypage.cgi?param=abc&id=Mu8fdxl3r # will be reported as 2 hits on /mypage.cgi?param=abc # This parameter is not used when URLWithQuery is 0 and can't be used with # URLWithQueryWithoutFollowingParameters. # Change : Effective for new updates only # Example: "param" # Default: "" # URLWithQueryWithOnlyFollowingParameters="" # When URLWithQuery is on, you will get the full URL with all parameters in # URL reports. But among thoose parameters, sometimes you don't need a # particular parameter because it does not identify the page or because it's # a random ID changing for each access even if URL points to same page. In # such cases, it is higly recommanded to ask AWStats to remove such parameters # from the URL before counting, manipulating and storing URL. Enter here list # of all non wanted parameters. For example if you enter "id", one hit on # /mypage.cgi?param=abc&id=Yo4UomP9d and /mypage.cgi?param=abc&id=Mu8fdxl3r # will be reported as 2 hits on /mypage.cgi?param=abc # This parameter is not used when URLWithQuery is 0 and can't be used with # URLWithQueryWithOnlyFollowingParameters. # Change : Effective for new updates only # Example: "PHPSESSID jsessionid" # Default: "" # URLWithQueryWithoutFollowingParameters="" # Keep or remove the query string to the referrer URL in the statistics for # external referrer pages. This is used to differentiate between the URLs of # dynamic referrer pages. If set to 1, mypage.html?id=x and mypage.html?id=y # are counted as two different referrer pages. # Change : Effective for new updates only # Possible values: # 0 - Referrer URLs are cleaned from the query string (ie: "/mypage.html") # 1 - Full URL with query string is used (ie: "/mypage.html?p=x&q=y") # Default: 0 # URLReferrerWithQuery=0 # AWStats can detect setup problems or show you important informations to have # a better use. Keep this to 1, except if AWStats says you can change it. # Change : Effective immediatly # Possible values: 0 or 1 # Default: 1 # WarningMessages=1 # When an error occurs, AWStats outputs a message related to errors. If you # want (in most cases for security reasons) to have no error messages, you # can set this parameter to your personalized generic message. # Change : Effective immediatly # Example: "An error occurred. Contact your Administrator" # Default: "" # ErrorMessages="" # AWStat can be run with debug=x parameter to output various informations # to help in debugging or solving troubles. If you want to allow this (not # enabled by default for security reasons), set this parameter to 0. # Change : Effective immediatly # Possible values: 0 or 1 # Default: 0 # DebugMessages=0 # To help you to detect if your log format is good, AWStats reports an error # if all the first NbOfLinesForCorruptedLog lines have a format that does not # match the LogFormat parameter. # However, some worm virus attack on your web server can result in a very high # number of corrupted lines in your log. So if you experience awstats stop # because of bad virus records at the beginning of your log file, you can # increase this parameter (very rare). # Change : Effective for new updates only # Default: 50 # NbOfLinesForCorruptedLog=50 # For some particular integration needs, you may want to have CGI links to # point to another script than awstats.pl. # Use the name of this script in WrapperScript parameter. # Change : Effective immediatly # Example: "awstatslauncher.pl" # Example: "awstatswrapper.cgi?key=123" # Default: "" # WrapperScript="" # DecodeUA must be set to 1 if you use Roxen web server. This server converts # all spaces in user agent field into %20. This make the AWStats robots, OS # and browsers detection fail in some cases. Just change it to 1 if and only # if your web server is Roxen. # Change : Effective for new updates only # Possible values: 0 or 1 # Default: 0 # DecodeUA=0 # MiscTrackerUrl can be used to make AWStats able to detect some miscellaneous # things, that can not be tracked on other way, like: # - Javascript disabled # - Java enabled # - Screen size # - Color depth # - Macromedia Director plugin # - Macromedia Shockwave plugin # - Realplayer G2 plugin # - QuickTime plugin # - Mediaplayer plugin # - Acrobat PDF plugin # To enable all these features, you must copy the awstats_misc_tracker.js file # into a /js/ directory stored in your web document root and add the following # HTML code at the end of your index page (but before </BODY>) : # # <script type="text/javascript" src="/js/awstats_misc_tracker.js"></script> # <noscript><img src="/js/awstats_misc_tracker.js?nojs=y" height=0 width=0 border=0 style="display: none"></noscript> # # If code is not added in index page, all those detection capabilities will be # disabled. You must also check that ShowScreenSizeStats and ShowMiscStats # parameters are set to 1 to make results appear in AWStats report page. # If you want to use another directory than /js/, you must also change the # awstatsmisctrackerurl variable into the awstats_misc_tracker.js file. # Change : Effective for new updates only. # Possible value: URL of javascript tracker file added in your HTML code. # Default: "/js/awstats_misc_tracker.js" # MiscTrackerUrl="/js/awstats_misc_tracker.js" #----------------------------------------------------------------------------- # OPTIONAL ACCURACY SETUP SECTION (Not required but increase AWStats features) #----------------------------------------------------------------------------- # The following values allow you to define accuracy of AWStats entities # (robots, browsers, os, referers, file types) detection. # It might be a good idea for large web sites or ISP that provides AWStats to # high number of customers, to set this parameter to 1 (or 0), instead of 2. # Possible values: # 0 = No detection, # 1 = Medium/Standard detection # 2 = Full detection # Change : Effective for new updates only # Note : LevelForBrowsersDetection can also accept value "allphones". This # enable detailed detection of phone/pda browsers. # Default: 2 (0 for LevelForWormsDetection) # # Django : 2012-07-03 # default: LevelForBrowsersDetection=2 LevelForBrowsersDetection=0 # 0 disables Browsers detection. # 2 reduces AWStats speed by 2% # allphones reduces AWStats speed by 5% # Django : 2012-07-03 # default: LevelForOSDetection=2 LevelForOSDetection=0 # 0 disables OS detection. # 2 reduces AWStats speed by 3% # Django : 2012-07-03 # default: LevelForRefererAnalyze=2 LevelForRefererAnalyze=0 # 0 disables Origin detection. # 2 reduces AWStats speed by 14% # Django : 2012-07-03 # default: LevelForRobotsDetection=2 LevelForRobotsDetection=0 # 0 disables Robots detection. # 2 reduces AWStats speed by 2.5% # Django : 2012-07-03 # default: LevelForSearchEnginesDetection=2 LevelForSearchEnginesDetection=0 # 0 disables Search engines detection. # 2 reduces AWStats speed by 9% # Django : 2012-07-03 # default: LevelForKeywordsDetection=2 LevelForKeywordsDetection=0 # 0 disables Keyphrases/Keywords detection. # 2 reduces AWStats speed by 1% # Django : 2012-07-03 # default: LevelForFileTypesDetection=2 LevelForFileTypesDetection=0 # 0 disables File types detection. # 2 reduces AWStats speed by 1% LevelForWormsDetection=0 # 0 disables Worms detection. # 2 reduces AWStats speed by 15% #----------------------------------------------------------------------------- # OPTIONAL APPEARANCE SETUP SECTION (Not required but increase AWStats features) #----------------------------------------------------------------------------- # When you use AWStats as a CGI, you can have the reports shown in HTML frames. # Frames are only available for report viewed dynamically. When you build # pages from command line, this option is not used and no frames are built. # Possible values: 0 or 1 # Default: 1 # UseFramesWhenCGI=1 # This parameter asks your browser to open detailed reports into a different # window than the main page. # Possible values: # 0 - Open all in same browser window # 1 - Open detailed reports in another window except if using frames # 2 - Open always in a different window even if reports are framed # Default: 1 # DetailedReportsOnNewWindows=1 # You can add, in the HTML report page, a cache lifetime (in seconds) that # will be returned to the browser in HTTP header answer by server. # This parameter is not used when reports are built with -staticlinks option. # Example: 3600 # Default: 0 # # Django : 2012-07-03 # default: Expires=3600 Expires=0 # To avoid too large web pages, you can ask AWStats to limit number of rows of # all reported charts to this number when no other limits apply. # Default: 1000 # MaxRowsInHTMLOutput=1000 # Set your primary language (ISO-639-1 language codes). # Possible values: # Albanian=al, Bosnian=ba, Bulgarian=bg, Catalan=ca, # Chinese (Taiwan)=tw, Chinese (Simpliefied)=cn, Croatian=hr, Czech=cz, # Danish=dk, Dutch=nl, English=en, Estonian=et, Euskara=eu, Finnish=fi, # French=fr, Galician=gl, German=de, Greek=gr, Hebrew=he, Hungarian=hu, # Icelandic=is, Indonesian=id, Italian=it, Japanese=jp, Korean=ko, # Latvian=lv, Norwegian (Nynorsk)=nn, Norwegian (Bokmal)=nb, Polish=pl, # Portuguese=pt, Portuguese (Brazilian)=br, Romanian=ro, Russian=ru, # Serbian=sr, Slovak=sk, Slovenian=si, Spanish=es, Swedish=se, Turkish=tr, # Ukrainian=ua, Welsh=cy. # First available language accepted by browser=auto # Default: "auto" # # Django : 2012-07-03 # default: Lang="auto" Lang="de" # Set the location of language files. # Example: "/usr/share/awstats/lang" # Default: "./lang" (means lang directory is in same location than awstats.pl) # DirLang="./lang" # Show menu header with reports' links # Possible values: 0 or 1 # Default: 1 # ShowMenu=1 # You choose here which reports you want to see in the main page and what you # want to see in those reports. # Possible values: # 0 - Report is not shown at all # 1 - Report is shown in main page with an entry in menu and default columns # XYZ - Report shows column informations defined by code X,Y,Z... # X,Y,Z... are code letters among the following: # U = Unique visitors # V = Visits # P = Number of pages # H = Number of hits (or mails) # B = Bandwith (or total mail size for mail logs) # L = Last access date # E = Entry pages # X = Exit pages # C = Web compression (mod_gzip,mod_deflate) # M = Average mail size (mail logs) # # Show monthly summary # Context: Web, Streaming, Mail, Ftp # Default: UVPHB, Possible column codes: UVPHB # Django : 2012-07-03 # default: ShowSummary=UVPHB ShowSummary=HB # Show monthly chart # Context: Web, Streaming, Mail, Ftp # Default: UVPHB, Possible column codes: UVPHB # Django : 2012-07-03 # default: ShowMonthStats=UVPHB ShowMonthStats=HB # Show days of month chart # Context: Web, Streaming, Mail, Ftp # Default: VPHB, Possible column codes: VPHB # Django : 2012-07-03 # default: ShowDaysOfMonthStats=VPHB ShowDaysOfMonthStats=HB # Show days of week chart # Context: Web, Streaming, Mail, Ftp # Default: PHB, Possible column codes: PHB # Default: VPHB, Possible column codes: VPHB # Django : 2012-07-03 # default: ShowDaysOfWeekStats=PHB ShowDaysOfWeekStats=HB # Show hourly chart # Context: Web, Streaming, Mail, Ftp # Default: PHB, Possible column codes: PHB # Django : 2012-07-03 # default: ShowHoursStats=PHB ShowHoursStats=HB # Show domains/country chart # Context: Web, Streaming, Mail, Ftp # Default: PHB, Possible column codes: PHB # Django : 2012-07-03 # default: ShowDomainsStats=PHB ShowDomainsStats=0 # Show hosts chart # Context: Web, Streaming, Mail, Ftp # Default: PHBL, Possible column codes: PHBL # Django : 2012-07-03 # default: ShowHostsStats=PHBL ShowHostsStats=HBL # Show authenticated users chart # Context: Web, Streaming, Ftp # Default: 0, Possible column codes: PHBL ShowAuthenticatedUsers=0 # Show robots chart # Context: Web, Streaming # Default: HBL, Possible column codes: HBL # Django : 2012-07-03 # default: ShowRobotsStats=HBL ShowRobotsStats=0 # Show worms chart # Context: Web, Streaming # Default: 0 (If set to other than 0, see also LevelForWormsDetection), Possible column codes: HBL ShowWormsStats=0 # Show email senders chart (For use when analyzing mail log files) # Context: Mail # Default: 0, Possible column codes: HBML # Django : 2012-07-03 # default: ShowEMailSenders=0 ShowEMailSenders=HBML # Show email receivers chart (For use when analyzing mail log files) # Context: Mail # Default: 0, Possible column codes: HBML # Django : 2012-07-03 # default: ShowEMailReceivers=0 ShowEMailReceivers=HBML # Show session chart # Context: Web, Streaming, Ftp # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowSessionsStats=1 ShowSessionsStats=0 # Show pages-url chart. # Context: Web, Streaming, Ftp # Default: PBEX, Possible column codes: PBEX # Django : 2012-07-03 # default: ShowPagesStats=PBEX ShowPagesStats=0 # Show file types chart. # Context: Web, Streaming, Ftp # Default: HB, Possible column codes: HBC # Django : 2012-07-03 # default: ShowFileTypesStats=HB ShowFileTypesStats=0 # Show file size chart (Not yet available) # Context: Web, Streaming, Mail, Ftp # Default: 1, Possible column codes: None ShowFileSizesStats=0 # Show downloads chart. # Context: Web, Streaming, Ftp # Default: HB, Possible column codes: HB # Django : 2012-07-03 # default: ShowDownloadsStats=HB ShowDownloadsStats=0 # Show operating systems chart # Context: Web, Streaming, Ftp # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowOSStats=1 ShowOSStats=0 # Show browsers chart # Context: Web, Streaming # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowBrowsersStats=1 ShowBrowsersStats=0 # Show screen size chart # Context: Web, Streaming # Default: 0 (If set to 1, see also MiscTrackerUrl), Possible column codes: None ShowScreenSizeStats=0 # Show origin chart # Context: Web, Streaming # Default: PH, Possible column codes: PH # Django : 2012-07-03 # default: ShowOriginStats=PH ShowOriginStats=0 # Show keyphrases chart # Context: Web, Streaming # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowKeyphrasesStats=1 ShowKeyphrasesStats=0 # Show keywords chart # Context: Web, Streaming # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowKeywordsStats=1 ShowKeywordsStats=0 # Show misc chart # Context: Web, Streaming # Default: a (See also MiscTrackerUrl parameter), Possible column codes: anjdfrqwp # Django : 2012-07-03 # default: ShowMiscStats=a ShowMiscStats=0 # Show http errors chart # Context: Web, Streaming # Default: 1, Possible column codes: None # Django : 2012-07-03 # default: ShowHTTPErrorsStats=1 ShowHTTPErrorsStats=0 # Show smtp errors chart (For use when analyzing mail log files) # Context: Mail # Default: 0, Possible column codes: None # Django : 2012-07-03 # default: ShowSMTPErrorsStats=0 ShowSMTPErrorsStats=1 # Show the cluster report (Your LogFormat must contains the %cluster tag) # Context: Web, Streaming, Ftp # Default: 0, Possible column codes: PHB ShowClusterStats=0 # Some graphical reports are followed by the data array of values. # If you don't want this array (to reduce the report size for example), you # can set thoose options to 0. # Possible values: 0 or 1 # Default: 1 # # Data array values for the ShowMonthStats report AddDataArrayMonthStats=1 # Data array values for the ShowDaysOfMonthStats report AddDataArrayShowDaysOfMonthStats=1 # Data array values for the ShowDaysOfWeekStats report AddDataArrayShowDaysOfWeekStats=1 # Data array values for the ShowHoursStats report AddDataArrayShowHoursStats=1 # In the Origin chart, you have stats on where your hits came from. You can # include hits on pages that come from pages of same sites in this chart. # Possible values: 0 or 1 # Default: 0 # IncludeInternalLinksInOriginSection=0 # The following parameters can be used to choose the maximum number of lines # shown for the particular following reports. # # Stats by countries/domains MaxNbOfDomain = 10 MinHitDomain = 1 # Stats by hosts MaxNbOfHostsShown = 10 MinHitHost = 1 # Stats by authenticated users MaxNbOfLoginShown = 10 MinHitLogin = 1 # Stats by robots MaxNbOfRobotShown = 10 MinHitRobot = 1 # Stats for Downloads MaxNbOfDownloadsShown = 10 MinHitDownloads = 1 # Stats by pages MaxNbOfPageShown = 10 MinHitFile = 1 # Stats by OS MaxNbOfOsShown = 10 MinHitOs = 1 # Stats by browsers MaxNbOfBrowsersShown = 10 MinHitBrowser = 1 # Stats by screen size MaxNbOfScreenSizesShown = 5 MinHitScreenSize = 1 # Stats by window size (following 2 parameters are not yet used) MaxNbOfWindowSizesShown = 5 MinHitWindowSize = 1 # Stats by referers MaxNbOfRefererShown = 10 MinHitRefer = 1 # Stats for keyphrases MaxNbOfKeyphrasesShown = 10 MinHitKeyphrase = 1 # Stats for keywords MaxNbOfKeywordsShown = 10 MinHitKeyword = 1 # Stats for sender or receiver emails MaxNbOfEMailsShown = 20 MinHitEMail = 1 # Choose if you want the week report to start on sunday or monday # Possible values: # 0 - Week starts on sunday # 1 - Week starts on monday # Default: 1 # # Django : 2012-07-03 # default: FirstDayOfWeek=0 FirstDayOfWeek=1 # List of visible flags that link to other language translations. # See Lang parameter for list of allowed flag/language codes. # If you don't want any flag link, set ShowFlagLinks to "". # This parameter is used only if ShowMenu parameter is set to 1. # Possible values: "" or "language_codes_separated_by_space" # Example: "en es fr nl de" # Default: "" # ShowFlagLinks="" # Each URL, shown in stats report views, are links you can click. # Possible values: 0 or 1 # Default: 1 # ShowLinksOnUrl=1 # When AWStats builds HTML links in its report pages, it starts those links # with "http://". However some links might be HTTPS links, so you can enter # here the root of all your HTTPS links. If all your site is a SSL web site, # just enter "/". # This parameter is not used if ShowLinksOnUrl is 0. # Example: "/shopping" # Example: "/" # Default: "" # UseHTTPSLinkForUrl="" # Maximum length of URL part shown on stats page (number of characters). # This affects only URL visible text, links still work. # Default: 64 # MaxLengthOfShownURL=64 # You can enter HTML code that will be added at the top of AWStats reports. # Default: "" # HTMLHeadSection="" # You can enter HTML code that will be added at the end of AWStats reports. # Great to add advert ban. # Default: "" # HTMLEndSection="" # By default AWStats page contains meta tag robots=noindex,nofollow # If you want to have your statistics to be indexed, set this option to 1. # Default: 0 # MetaRobot=0 # You can set Logo and LogoLink to use your own logo. # Logo must be the name of image file (must be in $DirIcons/other directory). # LogoLink is the expected URL when clicking on Logo. # Default: "awstats_logo6.png" # Logo="awstats_logo6.png" LogoLink="http://awstats.sourceforge.net" # Value of maximum bar width/height for horizontal/vertical HTML graphics bars. # Default: 260/90 # BarWidth = 260 BarHeight = 90 # You can ask AWStats to use a particular CSS (Cascading Style Sheet) to # change its look. To create a style sheet, you can use samples provided with # AWStats in wwwroot/css directory. # Example: "/awstatscss/awstats_bw.css" # Example: "/css/awstats_bw.css" # Default: "" # StyleSheet="" # Those color parameters can be used (if StyleSheet parameter is not used) # to change AWStats look. # Example: color_name="RRGGBB" # RRGGBB is Red Green Blue components in Hex # color_Background="FFFFFF" # Background color for main page (Default = "FFFFFF") color_TableBGTitle="CCCCDD" # Background color for table title (Default = "CCCCDD") color_TableTitle="000000" # Table title font color (Default = "000000") color_TableBG="CCCCDD" # Background color for table (Default = "CCCCDD") color_TableRowTitle="FFFFFF" # Table row title font color (Default = "FFFFFF") color_TableBGRowTitle="ECECEC" # Background color for row title (Default = "ECECEC") color_TableBorder="ECECEC" # Table border color (Default = "ECECEC") color_text="000000" # Color of text (Default = "000000") color_textpercent="606060" # Color of text for percent values (Default = "606060") color_titletext="000000" # Color of text title within colored Title Rows (Default = "000000") color_weekend="EAEAEA" # Color for week-end days (Default = "EAEAEA") color_link="0011BB" # Color of HTML links (Default = "0011BB") color_hover="605040" # Color of HTML on-mouseover links (Default = "605040") color_u="FFAA66" # Background color for number of unique visitors (Default = "FFAA66") color_v="F4F090" # Background color for number of visites (Default = "F4F090") color_p="4477DD" # Background color for number of pages (Default = "4477DD") color_h="66DDEE" # Background color for number of hits (Default = "66DDEE") color_k="2EA495" # Background color for number of bytes (Default = "2EA495") color_s="8888DD" # Background color for number of search (Default = "8888DD") color_e="CEC2E8" # Background color for number of entry pages (Default = "CEC2E8") color_x="C1B2E2" # Background color for number of exit pages (Default = "C1B2E2") #----------------------------------------------------------------------------- # PLUGINS #----------------------------------------------------------------------------- # Add here all plugin files you want to load. # Plugin files must be .pm files stored in 'plugins' directory. # Uncomment LoadPlugin lines to enable a plugin after checking that perl # modules required by the plugin are installed. # PLUGIN: Tooltips # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: Add tooltips pop-up help boxes to HTML report pages. # NOTE: This will increased HTML report pages size, thus server load and bandwidth. # # Django : 2012-07-03 # default: #LoadPlugin="tooltips" LoadPlugin="tooltips" # PLUGIN: DecodeUTFKeys # REQUIRED MODULES: Encode and URI::Escape # PARAMETERS: None # DESCRIPTION: Allow AWStats to show correctly (in language charset) # keywords/keyphrases strings even if they were UTF8 coded by the # referer search engine. # #LoadPlugin="decodeutfkeys" # PLUGIN: IPv6 # PARAMETERS: None # REQUIRED MODULES: Net::IP and Net::DNS # DESCRIPTION: This plugin gives AWStats capability to make reverse DNS # lookup on IPv6 addresses. # #LoadPlugin="ipv6" # PLUGIN: HashFiles # REQUIRED MODULES: Storable # PARAMETERS: None # DESCRIPTION: AWStats DNS cache files are read/saved as native hash files. # This increases DNS cache files loading speed, above all for very large web sites. # #LoadPlugin="hashfiles" # PLUGIN: UserInfo # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: Add a text (Firtname, Lastname, Office Department, ...) in # authenticated user reports for each login value. # A text file called userinfo.myconfig.txt, with two fields (first is login, # second is text to show, separated by a tab char) must be created in DirData # directory. # #LoadPlugin="userinfo" # PLUGIN: HostInfo # REQUIRED MODULES: Net::XWhois # PARAMETERS: None # DESCRIPTION: Add a column into host chart with a link to open a popup window that shows # info on host (like whois records). # # Django : 2012-07-03 # default: #LoadPlugin="hostinfo" LoadPlugin="hostinfo" # PLUGIN: ClusterInfo # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: Add a text (for example a full hostname) in cluster reports for each cluster # number. A text file called clusterinfo.myconfig.txt, with two fields (first is # cluster number, second is text to show) separated by a tab char. must be # created into DirData directory. # Note this plugin is useless if ShowClusterStats is set to 0 or if you don't # use a personalized log format that contains %cluster tag. # #LoadPlugin="clusterinfo" # PLUGIN: UrlAliases # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: Add a text (Page title, description...) in URL reports before URL value. # A text file called urlalias.myconfig.txt, with two fields (first is URL, # second is text to show, separated by a tab char) must be created into # DirData directory. # #LoadPlugin="urlalias" # PLUGIN: TimeHiRes # REQUIRED MODULES: Time::HiRes (if Perl < 5.8) # PARAMETERS: None # DESCRIPTION: Time reported by -showsteps option is in millisecond. For debug purpose. # #LoadPlugin="timehires" # PLUGIN: TimeZone # REQUIRED MODULES: Time::Local # PARAMETERS: [timezone offset] # DESCRIPTION: Allow AWStats to adjust time stamps for a different timezone # This plugin reduces AWStats speed of 10% !!!!!!! # LoadPlugin="timezone" # LoadPlugin="timezone +2" # LoadPlugin="timezone CET" # #LoadPlugin="timezone +2" # PLUGIN: Rawlog # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: This plugin adds a form in AWStats main page to allow users to see raw # content of current log files. A filter is also available. # #LoadPlugin="rawlog" # PLUGIN: GraphApplet # REQUIRED MODULES: None # PARAMETERS: [CSS classes to override] # DESCRIPTION: Supported charts are built by a 3D graphic applet. # #LoadPlugin="graphapplet /awstatsclasses" # EXPERIMENTAL FEATURE # PLUGIN: GraphGoogleChartAPI # REQUIRED MODULES: None # PARAMETERS: None # DESCRIPTION: Replaces the standard charts with free Google API generated images # in HTML reports. If country data is available and more than one country has hits, # a map will be generated using Google Visualizations. # Note: The machine where reports are displayed must have Internet access for the # charts to be generated. The only data sent to Google includes the statistic numbers, # legend names and country names. # Warning: This plugin is not compatible with option BuildReportFormat=xhtml. # # Django : 2012-07-03 # default: #LoadPlugin="graphgooglechartapi" LoadPlugin="graphgooglechartapi" # PLUGIN: GeoIPfree # REQUIRED MODULES: Geo::IPfree version 0.2+ (from Graciliano M.P.) # PARAMETERS: None # DESCRIPTION: Country chart is built from an Internet IP-Country database. # This plugin is useless for intranet only log files. # Note: You must choose between using this plugin (need Perl Geo::IPfree # module, database is free but not up to date) or the GeoIP plugin (need # Perl Geo::IP module from Maxmind, database is also free and up to date). # Note: Activestate provide a corrupted version of Geo::IPfree 0.2 Perl # module, so install it from elsewhere (from www.cpan.org for example). # This plugin reduces AWStats speed by up to 10% ! # #LoadPlugin="geoipfree" # MAXMIND GEO IP MODULES: Please see documentation for notes on all Maxmind modules # PLUGIN: GeoIP # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/geoip.dat[+/pathto/override.txt]] # DESCRIPTION: Builds a country chart and adds an entry to the hosts # table with country name # Replace spaces in the path of geoip data file with string "%20". # # Django : 2012-07-03 # default: #LoadPlugin="geoip GEOIP_STANDARD /pathto/GeoIP.dat" LoadPlugin="geoip GEOIP_STANDARD /usr/local/lib64/perl5/GeoIP.dat" # PLUGIN: GeoIP_City_Maxmind # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/GeoIPCity.dat[+/pathto/override.txt]] # DESCRIPTION: This plugin adds a column under the hosts field and tracks the pageviews # and hits by city including regions. # Replace spaces in the path of geoip data file with string "%20". # # Django : 2012-07-03 # default: #LoadPlugin="geoip_city_maxmind GEOIP_STANDARD /pathto/GeoIPCity.dat" LoadPlugin="geoip_city_maxmind GEOIP_STANDARD /usr/local/lib64/perl5/GeoLiteCity.dat" # PLUGIN: GeoIP_ASN_Maxmind # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/GeoIPASN.dat[+/pathto/override.txt][+http://linktoASlookup]] # DESCRIPTION: This plugin adds a chart of AS numbers where the host IP address is registered. # This plugin can display some ISP information if included in the database. You can also provide # a link that will be used to lookup additional registration data. Put the link at the end of # the parameter string and the report page will include the link with the full AS number at the end. # Replace spaces in the path of geoip data file with string "%20". # #LoadPlugin="geoip_asn_maxmind GEOIP_STANDARD /usr/local/geoip.dat+http://enc.com.au/itools/aut-num.php?autnum=" # PLUGIN: GeoIP_Region_Maxmind # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/GeoIPRegion.dat[+/pathto/override.txt]] # DESCRIPTION:This plugin adds a chart of hits by regions. Only regions for US and # Canada can be detected. # Replace spaces in the path of geoip data file with string "%20". # #LoadPlugin="geoip_region_maxmind GEOIP_STANDARD /pathto/GeoIPRegion.dat" # PLUGIN: GeoIP_ISP_Maxmind # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/GeoIPISP.dat[+/pathto/override.txt]] # DESCRIPTION: This plugin adds a chart of hits by ISP. # Replace spaces in the path of geoip data file with string "%20". # #LoadPlugin="geoip_isp_maxmind GEOIP_STANDARD /pathto/GeoIPISP.dat" # PLUGIN: GeoIP_Org_Maxmind # REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind) # PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/GeoIPOrg.dat[+/pathto/override.txt]] # DESCRIPTION: This plugin add a chart of hits by Organization name # Replace spaces in the path of geoip data file with string "%20". # #LoadPlugin="geoip_org_maxmind GEOIP_STANDARD /pathto/GeoIPOrg.dat" #----------------------------------------------------------------------------- # EXTRA SECTIONS #----------------------------------------------------------------------------- # You can define your own charts, you choose here what are rows and columns # keys. This feature is particularly useful for marketing purpose, tracking # products orders for example. # For this, edit all parameters of Extra section. Each set of parameter is a # different chart. For several charts, duplicate section changing the number. # Note: Each Extra section reduces AWStats speed by 8%. # # WARNING: A wrong setup of Extra section might result in too large arrays # that will consume all your memory, making AWStats unusable after several # updates, so be sure to setup it correctly. # In most cases, you don't need this feature. # # ExtraSectionNameX is title of your personalized chart. # ExtraSectionCodeFilterX is list of codes the record code field must match. # Put an empty string for no test on code. # ExtraSectionConditionX are conditions you can use to count or not the hit, # Use one of the field condition # (URL,URLWITHQUERY,QUERY_STRING,REFERER,UA,HOSTINLOG,HOST,VHOST,extraX) # and a regex to match, after a coma. Use "||" for "OR". # ExtraSectionFirstColumnTitleX is the first column title of the chart. # ExtraSectionFirstColumnValuesX is a string to tell AWStats which field to # extract value from # (URL,URLWITHQUERY,QUERY_STRING,REFERER,UA,HOSTINLOG,HOST,VHOST,extraX) # and how to extract the value (using regex syntax). Each different value # found will appear in first column of report on a different row. Be sure # that list of different possible values will not grow indefinitely. # ExtraSectionFirstColumnFormatX is the string used to write value. # ExtraSectionStatTypesX are things you want to count. You can use standard # code letters (P for pages,H for hits,B for bandwidth,L for last access). # ExtraSectionAddAverageRowX add a row at bottom of chart with average values. # ExtraSectionAddSumRowX add a row at bottom of chart with sum values. # MaxNbOfExtraX is maximum number of rows shown in chart. # MinHitExtraX is minimum number of hits required to be shown in chart. # # Example to report the 20 products the most ordered by "order.cgi" script #ExtraSectionName1="Product orders" #ExtraSectionCodeFilter1="200 304" #ExtraSectionCondition1="URL,\/cgi\-bin\/order\.cgi||URL,\/cgi\-bin\/order2\.cgi" #ExtraSectionFirstColumnTitle1="Product ID" #ExtraSectionFirstColumnValues1="QUERY_STRING,productid=([^&]+)" #ExtraSectionFirstColumnFormat1="%s" #ExtraSectionStatTypes1=PL #ExtraSectionAddAverageRow1=0 #ExtraSectionAddSumRow1=1 #MaxNbOfExtra1=20 #MinHitExtra1=1 # There is also a global parameter ExtraTrackedRowsLimit that limits the # number of possible rows an ExtraSection can report. This parameter is # here to protect too much memory use when you make a bad setup in your # ExtraSection. It applies to all ExtraSection independently meaning that # none ExtraSection can report more rows than value defined by ExtraTrackedRowsLimit. # If you know an ExtraSection will report more rows than its value, you should # increase this parameter or AWStats will stop with an error. # Example: 2000 # Default: 500 # ExtraTrackedRowsLimit=500 #----------------------------------------------------------------------------- # INCLUDES #----------------------------------------------------------------------------- # You can include other config files using the directive with the name of the # config file. # This is particularly useful for users who have a lot of virtual servers, so # a lot of config files and want to maintain common values in only one file. # Note that when a variable is defined both in a config file and in an # included file, AWStats will use the last value read for parameters that # contains one value and AWStats will concat all values from both files for # parameters that are lists of values. # #Include ""
Zusammengefasst sieht dann unsere Konfigurationdatei entsprechend wie folgt aus:
# egrep -v '(^#|^$)' /etc/awstats/awstats.mx1.nausch.org.conf
LogFile="perl /usr/bin/maillogconvert.pl standard < /var/log/maillog |" LogType=M LogFormat="%time2 %email %email_r %host %host_r %method %url %code %bytesd" LogSeparator=" " SiteDomain="mx1.nausch.org" HostAliases=„localhost 127.0.0.1 REGEX[nausch\.org$] REGEX[www\.nausch\.org$]“ DNSLookup=2 DirData="/var/www/awstats" DirCgi="/awstats" DirIcons="/awstats/icon" AllowToUpdateStatsFromBrowser=1 AllowFullYearView=2 EnableLockForUpdate=1 DNSStaticCacheFile="dnscache.txt" DNSLastUpdateCacheFile="dnscachelastupdate.txt" SkipDNSLookupFor="" AllowAccessFromWebToAuthenticatedUsersOnly=0 AllowAccessFromWebToFollowingAuthenticatedUsers="" AllowAccessFromWebToFollowingIPAddresses="" CreateDirDataIfNotExists=0 BuildHistoryFormat=text BuildReportFormat=html SaveDatabaseFilesWithPermissionsForEveryone=1 PurgeLogFile=0 ArchiveLogRecords=0 KeepBackupOfHistoricFiles=1 DefaultFile="index.php index.html" SkipHosts="" SkipUserAgents="" SkipFiles="" SkipReferrersBlackList="" OnlyHosts="" OnlyUserAgents="" OnlyUsers="" OnlyFiles="" NotPageList="css js class gif jpg jpeg png bmp ico rss xml swf" ValidHTTPCodes="200 304" ValidSMTPCodes="1 250" AuthenticatedUsersNotCaseSensitive=0 URLNotCaseSensitive=0 URLWithAnchor=0 URLQuerySeparators="?;" URLWithQuery=0 URLWithQueryWithOnlyFollowingParameters="" URLWithQueryWithoutFollowingParameters="" URLReferrerWithQuery=0 WarningMessages=1 ErrorMessages="" DebugMessages=0 NbOfLinesForCorruptedLog=50 WrapperScript="" DecodeUA=0 MiscTrackerUrl="/js/awstats_misc_tracker.js" LevelForBrowsersDetection=0 # 0 disables Browsers detection. # 2 reduces AWStats speed by 2% # allphones reduces AWStats speed by 5% LevelForOSDetection=0 # 0 disables OS detection. # 2 reduces AWStats speed by 3% LevelForRefererAnalyze=0 # 0 disables Origin detection. # 2 reduces AWStats speed by 14% LevelForRobotsDetection=0 # 0 disables Robots detection. # 2 reduces AWStats speed by 2.5% LevelForSearchEnginesDetection=0 # 0 disables Search engines detection. # 2 reduces AWStats speed by 9% LevelForKeywordsDetection=0 # 0 disables Keyphrases/Keywords detection. # 2 reduces AWStats speed by 1% LevelForFileTypesDetection=0 # 0 disables File types detection. # 2 reduces AWStats speed by 1% LevelForWormsDetection=0 # 0 disables Worms detection. # 2 reduces AWStats speed by 15% UseFramesWhenCGI=1 DetailedReportsOnNewWindows=1 Expires=0 MaxRowsInHTMLOutput=1000 Lang="de" DirLang="./lang" ShowMenu=1 ShowSummary=HB ShowMonthStats=HB ShowDaysOfMonthStats=HB ShowDaysOfWeekStats=HB ShowHoursStats=HB ShowDomainsStats=0 ShowHostsStats=HBL ShowAuthenticatedUsers=0 ShowRobotsStats=0 ShowWormsStats=0 ShowEMailSenders=HBML ShowEMailReceivers=HBML ShowSessionsStats=0 ShowPagesStats=0 ShowFileTypesStats=0 ShowFileSizesStats=0 ShowDownloadsStats=0 ShowOSStats=0 ShowBrowsersStats=0 ShowScreenSizeStats=0 ShowOriginStats=0 ShowKeyphrasesStats=0 ShowKeywordsStats=0 ShowMiscStats=0 ShowHTTPErrorsStats=0 ShowSMTPErrorsStats=1 ShowClusterStats=0 AddDataArrayMonthStats=1 AddDataArrayShowDaysOfMonthStats=1 AddDataArrayShowDaysOfWeekStats=1 AddDataArrayShowHoursStats=1 IncludeInternalLinksInOriginSection=0 MaxNbOfDomain = 10 MinHitDomain = 1 MaxNbOfHostsShown = 10 MinHitHost = 1 MaxNbOfLoginShown = 10 MinHitLogin = 1 MaxNbOfRobotShown = 10 MinHitRobot = 1 MaxNbOfDownloadsShown = 10 MinHitDownloads = 1 MaxNbOfPageShown = 10 MinHitFile = 1 MaxNbOfOsShown = 10 MinHitOs = 1 MaxNbOfBrowsersShown = 10 MinHitBrowser = 1 MaxNbOfScreenSizesShown = 5 MinHitScreenSize = 1 MaxNbOfWindowSizesShown = 5 MinHitWindowSize = 1 MaxNbOfRefererShown = 10 MinHitRefer = 1 MaxNbOfKeyphrasesShown = 10 MinHitKeyphrase = 1 MaxNbOfKeywordsShown = 10 MinHitKeyword = 1 MaxNbOfEMailsShown = 20 MinHitEMail = 1 FirstDayOfWeek=1 ShowFlagLinks="" ShowLinksOnUrl=1 UseHTTPSLinkForUrl="" MaxLengthOfShownURL=64 HTMLHeadSection="" HTMLEndSection="" MetaRobot=0 Logo="awstats_logo6.png" LogoLink="http://awstats.sourceforge.net" BarWidth = 260 BarHeight = 90 StyleSheet="" color_Background="FFFFFF" # Background color for main page (Default = "FFFFFF") color_TableBGTitle="CCCCDD" # Background color for table title (Default = "CCCCDD") color_TableTitle="000000" # Table title font color (Default = "000000") color_TableBG="CCCCDD" # Background color for table (Default = "CCCCDD") color_TableRowTitle="FFFFFF" # Table row title font color (Default = "FFFFFF") color_TableBGRowTitle="ECECEC" # Background color for row title (Default = "ECECEC") color_TableBorder="ECECEC" # Table border color (Default = "ECECEC") color_text="000000" # Color of text (Default = "000000") color_textpercent="606060" # Color of text for percent values (Default = "606060") color_titletext="000000" # Color of text title within colored Title Rows (Default = "000000") color_weekend="EAEAEA" # Color for week-end days (Default = "EAEAEA") color_link="0011BB" # Color of HTML links (Default = "0011BB") color_hover="605040" # Color of HTML on-mouseover links (Default = "605040") color_u="FFAA66" # Background color for number of unique visitors (Default = "FFAA66") color_v="F4F090" # Background color for number of visites (Default = "F4F090") color_p="4477DD" # Background color for number of pages (Default = "4477DD") color_h="66DDEE" # Background color for number of hits (Default = "66DDEE") color_k="2EA495" # Background color for number of bytes (Default = "2EA495") color_s="8888DD" # Background color for number of search (Default = "8888DD") color_e="CEC2E8" # Background color for number of entry pages (Default = "CEC2E8") color_x="C1B2E2" # Background color for number of exit pages (Default = "C1B2E2") LoadPlugin="tooltips" LoadPlugin="hostinfo" LoadPlugin="graphgooglechartapi" LoadPlugin="geoip GEOIP_STANDARD /usr/local/lib64/perl5/GeoIP.dat" LoadPlugin="geoip_city_maxmind GEOIP_STANDARD /usr/local/lib64/perl5/GeoLiteCity.dat" ExtraTrackedRowsLimit=500
Nach erfolgreicher Konfiguration steht dem ersten Aufruf nichts mehr im Wege. Der Aufruf erfolgt über folgende URL:
$ firefox http://aw-stats.nausch.org/awstats/awstats.pl?config=mx1.nausch.org