diff --git a/.travis.yml b/.travis.yml index 09fd485..f6a8180 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,16 @@ addons: packages: - python-flake8 - python3-flake8 + - shellcheck script: # the travis environment is a bit outdated - thus we need to run flake8 manually # (instead of "make lint") - - which flake8 + # TODO: use "make lint" as soon as something newer than "trusty" is supported by travis + # See https://docs.travis-ci.com/user/reference/overview/ + # Keep the three commands below in sync with the "lint" target in the Makefile. + - flake8 --version - python /usr/bin/flake8 --max-line-length=99 mail-tls-helper.py tests - python3 /usr/bin/flake8 --max-line-length=99 --ignore=N802,N803,N806 mail-tls-helper.py tests + - shellcheck -s dash munin-plugin + # The "test" target works as expected. - make test diff --git a/Makefile b/Makefile index b9feafa..cf074bc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ PYTHON_LOCATIONS = mail-tls-helper.py tests +SHELL_SCRIPTS = munin-plugin .PHONY: dist @@ -16,6 +17,8 @@ test: .PHONY: lint lint: + @# keep these commands in sync with the "script" section of .travis.yml python -m flake8 $(PYTHON_LOCATIONS) # TODO: fix remaining python3 style hints python3 -m flake8 $(PYTHON_LOCATIONS) --ignore=N802,N803,N806 + shellcheck -s dash $(SHELL_SCRIPTS) diff --git a/README.md b/README.md index cb178dc..c397e66 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,11 @@ request* on Github. } ``` +# Monitoring + +* [munin](https://munin-monitoring.org): see the documentation header of `munin-plugin` + + ## Changelog * 2017-11-11: version 0.8.1 diff --git a/mail-tls-helper.py b/mail-tls-helper.py index f30ed2a..bd8f933 100755 --- a/mail-tls-helper.py +++ b/mail-tls-helper.py @@ -88,6 +88,8 @@ def parse_args(): parser.add_argument('-a', '--alerts', dest='send_alerts', action='store_true', help=('send out alert mails to the "postmaster" addresses of external ' 'mail domains lacking TLS support')) + parser.add_argument('--print-statistics', action='store_true', + help='Print statistics of recent transport security usage') parser.add_argument('-S', '--no-summary', dest='send_summary', action='store_false', help='do not send out summary mail') parser.add_argument('-P', '--no-postfix-map', dest='use_postfix_map', action='store_false', @@ -415,6 +417,11 @@ def postfixParseLog(logfile, whitelist): summary_lines.append("Total count of messages sent with TLS: %s" % sentCountTLS) summary_lines.append("Total count of messages sent with Tor: %s" % sentCountTor) summary_lines.append("Total count of messages sent unencrypted: %s" % insecure_count) + if args.print_statistics: + print("count_total={:d}".format(sentCountTotal)) + print("count_tls={:d}".format(sentCountTLS)) + print("count_tor={:d}".format(sentCountTor)) + print("count_plain={:d}".format(insecure_count)) if sentCountTotal: summary_lines.append("Percentage of messages sent unencrypted: %.2f%%" % (100 * insecure_count / float(sentCountTotal))) @@ -431,8 +438,8 @@ def postfixParseLog(logfile, whitelist): # Sadly the "TLS required" message only includes the relay name (no mail domains). summary_lines.append(" * MX {} (no related mail domains known)".format(relay)) - # update the SQLite database with noTLS domains - if len(domainsNoTLS) > 0: + if (len(domainsNoTLS) > 0) and (args.send_summary or args.send_alerts): + # update the SQLite database with noTLS domains alertable_domains = notlsProcess(domainsTLS, domainsNoTLS, args.sqlite_db) summary_lines.append("") summary_lines.append("List of domains with no-TLS connections:") diff --git a/munin-plugin b/munin-plugin new file mode 100755 index 0000000..684c3be --- /dev/null +++ b/munin-plugin @@ -0,0 +1,155 @@ +#!/bin/sh + +: <<=cut + +=head1 NAME + +mail-tls-helper - monitor the transport security of mails submitted via SMTP + + +=head1 APPLICABLE SYSTEMS + +The plugin works with any mail server using +[mail-tls-helper](https://github.com/systemli/mail-tls-helper) for semi-automatic configuration of +the TLS submission policy. + + +=head1 USAGE + +Copy or (preferably) symlink the plugin file to /etc/munin/plugins/mail-tls-helper. +Copy or symlink the plugin configuration file 'munin-plugins.conf' below /etc/munin/plugin-conf.d/. +Restart munin-node in order to let it discover the new plugin. +Depending on the permissions of your mail log file, it may be necessary to adjust the "group" or +"user" setting in the configuration file. The default configuration file works for Debian. + + +=head1 CONFIGURATION + +Symlink or copy this script to /etc/munin/plugins/mail-tls-helper and restart munin-node. + +The plugin probably requires explicit configuration in order to gain the necessary privileges for +reading the mail log file. + +The following settings are evaluated by the plugin: + + [mail-tls-helper] + group adm + env.mail_tls_helper_path /usr/local/bin/mail-tls-helper.py + env.mail_tls_helper_arguments --no-summary --no-postmap + env.log_file /var/log/mail.log + env.python_bin /usr/bin/python3 + +The location of 'mail-tls-helper.py' can either be defined by its full path (see +'mail_tls_helper_path') or left empty. In the latter case, the potential symlink of the plugin is +resolved and a file named 'mail-tls-helper.py' is assumed to reside just next to the real plugin +file. In short: just checkout the mail-tls-helper repository and symlink the plugin file to +/etc/munin/plugins/mail-tls-helper and maybe the plugin config file ('munin-plugin.conf') to +/etc/munin/plugin-conf.d/. In this case no further configuration should be necessary. + +Please note, that the above defaults for "mail_tls_arguments" should work for most systems. +If you want to change this value, you should not forget the "--no-summary --no-postmap" arguments +in order to avoid unwanted side-effects. + +The "group" setting (or "user") should be set to a reasonable value that allows read access to the +mail log file. + + +=head1 AUTHOR + + Copyright 2018 Lars Kruse + + +=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 3 of the License, or +(at your option) any later version. + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +set -eu + + +MAIL_TLS_HELPER_ARGUMENTS=${mail_tls_helper_arguments:-"--no-summary --no-postmap"} +MAIL_LOG_FILE=${log_file:-/var/log/mail.log} +PYTHON_BIN=${python_bin:-/usr/bin/python3} + +# determine the path of the script +if [ -z "${mail_tls_helper_path:-}" ]; then + # assume a checkout (the script being next to the plugin file) + MAIL_TLS_HELPER=$(dirname "$(realpath "$0")")/mail-tls-helper.py +else + MAIL_TLS_HELPER=$mail_tls_helper_path +fi + + +do_autoconf() { + if [ -e "$MAIL_TLS_HELPER" ]; then + if [ -r "$MAIL_LOG_FILE" ]; then + echo "yes" + else + echo "no (cannot read log file: $MAIL_LOG_FILE)" + fi + else + echo "no (non-existing mail-tls-helper script: $MAIL_TLS_HELPER)" + fi +} + + +do_config() { + echo "graph_title Mail Transport Security of SMTP submissions" + echo "graph_category mail" + echo "graph_order count_tls count_tor count_plain" + echo "graph_vlabel submitted mails per second" + echo "count_tls.label TLS" + echo "count_tls.draw AREASTACK" + echo "count_tls.type DERIVE" + echo "count_tls.min 0" + echo "count_tor.label Tor" + echo "count_tor.draw AREASTACK" + echo "count_tor.type DERIVE" + echo "count_tor.min 0" + echo "count_plain.label no encryption" + echo "count_plain.draw AREASTACK" + echo "count_plain.type DERIVE" + echo "count_plain.min 0" +} + + +do_fetch() { + local output + local key + local value + # shellcheck disable=SC2086 + output=$("$PYTHON_BIN" "$MAIL_TLS_HELPER" $MAIL_TLS_HELPER_ARGUMENTS --mail-log "$MAIL_LOG_FILE" --print-statistics) + for key in "count_tls" "count_tor" "count_plain"; do + value=$(echo "$output" | grep "^$key=" | cut -f 2- -d "=") + [ -z "$value" ] && value="U" + echo "${key}.value $value" + done +} + + +case "${1:-}" in + autoconf) + do_autoconf + ;; + config) + do_config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ]; then do_fetch; fi + ;; + "") + do_fetch + ;; + *) + echo >&2 "Unknown action: $1" + exit 1 + ;; +esac diff --git a/munin-plugin.conf b/munin-plugin.conf new file mode 100644 index 0000000..a0f3e10 --- /dev/null +++ b/munin-plugin.conf @@ -0,0 +1,10 @@ +[mail-tls-helper] +# read permissions for the mail log file are required +group adm + +# The location of the 'mail-tls-helper.py' script is determined based on the +# plugin file symlink, if possible. +#env.mail_tls_helper_path /usr/local/bin/mail-tls-helper.py +#env.mail_tls_helper_arguments --no-summary --no-postmap +#env.log_file /var/log/mail.log +#env.python_bin /usr/bin/python3