From 7936ebaaed5ebe9606e0f4bab34e74afe7b4ceb6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 19 Apr 2014 03:09:10 -0400 Subject: [PATCH] Add ability to test email from launcher --- launcher | 16 +++++- scripts/mailtest | 127 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100755 scripts/mailtest diff --git a/launcher b/launcher index 76c03a5..2a71164 100755 --- a/launcher +++ b/launcher @@ -4,6 +4,7 @@ command=$1 config=$2 opt=$3 +cd "$(dirname "$0")" config_file=containers/"$config".yml cidfile=cids/"$config".cid @@ -28,6 +29,7 @@ usage () { echo " destroy: Stop and remove a container" echo " ssh: Start a bash shell in a running container" echo " logs: Docker logs for container" + echo " mailtest: Test the mail settings in a container" echo " bootstrap: Bootstrap a container for the config based on a template" echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)" echo @@ -189,6 +191,14 @@ if [ ! -e $config_file ] fi +run_mailtest(){ + if [ ! -e $config_file ]; then + echo "Config does not exist: $config_file" >&2 + exit 1 + fi + exec scripts/mailtest $config_file +} + run_stop(){ if [ ! -e $cidfile ] then @@ -294,12 +304,16 @@ run_bootstrap(){ case "$command" in bootstrap) - run_bootstrap echo "Successfully bootstrapped, to startup use ./launcher start $config" exit 0 ;; + mailtest) + run_mailtest + exit 0 + ;; + ssh) if [ ! -e $cidfile ] then diff --git a/scripts/mailtest b/scripts/mailtest new file mode 100755 index 0000000..3484310 --- /dev/null +++ b/scripts/mailtest @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# vim:sw=4 ts=4 et: + +import email +import smtplib +import ssl +import socket +import sys +import os + +try: + import yaml +except ImportError: + print("ERROR: python yaml module not installed - run the following and try again:", file=sys.stderr) + print("sudo apt-get install python3-yaml", file=sys.stderr) + sys.exit(1) + +def do_tls(conn, sslv): + # Possible values of smtp_sslv: none|peer|client_once|fail_if_no_peer_cert + try: + # Creating a context with the purpose of server authentication implies verifying the certificate + sslcontext=ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) + # The None below looks like might be a typo but it's not - it represents the ActiveRecord default (to verify) + if sslv in (None, 'peer', 'client_once', 'fail_if_no_peer_cert'): + # defaults are good + conn.starttls(context=sslcontext) + elif sslv in ('none',): + # disable cert checking + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + conn.starttls(context=sslcontext) + else: + raise ValueError('invalid value for DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: {}'.format(sslv)) + except smtplib.SMTPException as e: + if (sslv is None) and ('STARTTLS extension not supported by server' in e.args[0]): + print("unable to establish TLS, continuing: {}".format(e.args[0])) + else: + raise + +### Start of execution ### +cfgfile = sys.argv[1] +try: + destemail = sys.argv[2] +except IndexError: + destemail = input('Enter your email address: ') +srcemail = 'nobody+launcher-mailtest@discourse.org' + +# Read in the container yaml and grab the env section +cfgdata = yaml.safe_load(open(cfgfile).read()) +envdata = cfgdata['env'] + +# Here are the variables we'll test +smtp_addr = envdata.get('DISCOURSE_SMTP_ADDRESS') +smtp_port = envdata.get('DISCOURSE_SMTP_PORT') +smtp_user = envdata.get('DISCOURSE_SMTP_USER_NAME') +smtp_pass = envdata.get('DISCOURSE_SMTP_PASSWORD') +smtp_sslv = envdata.get('DISCOURSE_SMTP_OPENSSL_VERIFY_MODE') + +# Yoink out the settings from the file - we'll print them and put them in the email +testinfo = 'DISCOURSE_SMTP_ settings:\n' +for k,v in filter(lambda x: x[0].startswith('DISCOURSE_SMTP_'), envdata.items()): + if 'PASSWORD' in k: + v = '(hidden)' + testinfo += ' {} = {}\n'.format(k,v) +print(testinfo) + +# Ensure at least smtp-addr is specified - everything else is optional +if smtp_addr is None: + print("ERROR: DISCOURSE_SMTP_ADDRESS not specified", file=sys.stderr) + sys.exit(1) + +if (smtp_user is None and smtp_pass is not None) or (smtp_user is not None and smtp_pass is None): + print("ERROR: both username and password must be specified for auth", file=sys.stderr) + sys.exit(1) + +# Try and ensure the test is valid +if destemail.split('@',1)[1] in smtp_addr: + print('WARNING: {} may be allowed to relay mail to {}, this may not be a valid test!'.format(smtp_addr, destemail)) + +msg = email.message.Message() +msg.add_header('From', 'nobody+launcher-mailtest@discourse.org') +msg.add_header('To', destemail) +msg.add_header('Subject', 'discourse launcher mailtest for {}'.format(os.path.basename(cfgfile))) +msg.set_payload(testinfo) + +try: + smtp = smtplib.SMTP(smtp_addr, smtp_port, timeout=5) + #smtp.debuglevel=1 + do_tls(smtp,smtp_sslv) + if smtp_user: + smtp.login(smtp_user, smtp_pass) + result = smtp.sendmail('nobody+launcher-mailtest@discourse.org', destemail, msg.as_string()) +except socket.gaierror as e: + print("ERROR: {}".format(e.args[-1]), file=sys.stderr) + print(" Ensure that the host '{}' exists".format(smtp_addr), file=sys.stderr) + sys.exit(1) +except socket.timeout as e: + print("ERROR: {}".format(e.args[-1]), file=sys.stderr) + print(" Ensure that the host '{}' is up and reachable".format(smtp_addr), file=sys.stderr) + sys.exit(1) +except smtplib.SMTPConnectError as e: + print("ERROR: {}".format(e.args[-1]), file=sys.stderr) + print(" Ensure that the host '{}' is up and reachable", file=sys.stderr) + sys.exit(1) +except ssl.SSLError as e: + print("ERROR: unable to establish TLS: {}".format(e.args[-1]), file=sys.stderr) + if 'certificate verify failed' in e.args[-1]: + print(" Fix the host certificate or disable validation".format(smtp_addr), file=sys.stderr) + sys.exit(1) +except smtplib.SMTPRecipientsRefused as e: + print("ERROR: {}".format(e.args[-1].popitem()[1][1].decode()), file=sys.stderr) + print(" You must provide a username/password to send to this host", file=sys.stderr) + sys.exit(1) +except smtplib.SMTPAuthenticationError as e: + print("ERROR: {}".format(e.args[-1].decode()), file=sys.stderr) + print(" Check to ensure your username and password are correct", file=sys.stderr) + sys.exit(1) +except smtplib.SMTPException as e: + print("ERROR: {}".format(e.args[-1]), file=sys.stderr) + if 'SMTP AUTH extension not supported by server' in e.args[0]: + print(" Authorization is not available - you may need to use TLS", file=sys.stderr) + sys.exit(1) +except ValueError: + print("ERROR: {}".format(e.args[-1]), file=sys.stderr) + sys.exit(1) + +print("Success!") -- 2.25.1