From 5cc317408ca73c7b741a65db5b20ccde4e032402 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Tue, 29 Mar 2016 14:19:13 -0700 Subject: [PATCH 01/14] Move attributes into init and allow for injecting file contents for testing. --- letsencrypt-postfix/PostfixConfigGenerator.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index af1953208..34654a2d1 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -3,6 +3,7 @@ import sys import string import os, os.path + def parse_line(line_data): """ Return the (line number, left hand side, right hand side) of a stripped @@ -17,15 +18,30 @@ def parse_line(line_data): return None return (num, left.strip(), right.strip()) + class ExistingConfigError(ValueError): pass + class PostfixConfigGenerator: - def __init__(self, policy_config, postfix_dir, fixup=False): + def __init__(self, policy_config, postfix_dir, fixup=False, fopen=open): self.fixup = fixup self.postfix_dir = postfix_dir self.policy_config = policy_config self.policy_file = os.path.join(postfix_dir, "starttls_everywhere_policy") + self.additions = [] + self.deletions = [] + self.fn = self.find_postfix_cf() + self.raw_cf = fopen(self.fn).readlines() + self.cf = map(string.strip, self.raw_cf) + #self.cf = [line for line in cf if line and not line.startswith("#")] + self.policy_lines = [] + self.new_cf = "" + + def find_postfix_cf(self): + "Search far and wide for the correct postfix configuration file" + return os.path.join(self.postfix_dir, "main.cf") + def ensure_cf_var(self, var, ideal, also_acceptable): """ Ensure that existing postfix config @var is in the list of @acceptable @@ -60,13 +76,6 @@ class PostfixConfigGenerator: Try to ensure/mutate that the config file is in a sane state. Fixup means we'll delete existing lines if necessary to get there. """ - self.additions = [] - self.deletions = [] - self.fn = self.find_postfix_cf() - self.raw_cf = open(self.fn).readlines() - self.cf = map(string.strip, self.raw_cf) - #self.cf = [line for line in cf if line and not line.startswith("#")] - # Check we're currently accepting inbound STARTTLS sensibly self.ensure_cf_var("smtpd_use_tls", "yes", []) # Ideally we use it opportunistically in the outbound direction @@ -91,7 +100,6 @@ class PostfixConfigGenerator: if self.raw_cf[-1][-1] == "\n": sep = "" else: sep = "\n" - self.new_cf = "" for num, line in enumerate(self.raw_cf): if self.fixup and num in self.deletions: self.new_cf += "# Line removed by STARTTLS Everywhere\n# " + line @@ -104,12 +112,7 @@ class PostfixConfigGenerator: f.write(self.new_cf) f.close() - def find_postfix_cf(self): - "Search far and wide for the correct postfix configuration file" - return os.path.join(self.postfix_dir, "main.cf") - def set_domainwise_tls_policies(self): - self.policy_lines = [] all_acceptable_mxs = self.policy_config.acceptable_mxs for address_domain, properties in all_acceptable_mxs.items(): mx_list = properties.accept_mx_domains @@ -152,7 +155,6 @@ class PostfixConfigGenerator: currently supported. """ # XXX ensure we raise the right kinds of exceptions - self.postfix_cf_file = self.find_postfix_cf() def more_info(self): From fee9c862334bfd290a2b9bd1e07b93badfb66ae5 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Tue, 29 Mar 2016 14:20:33 -0700 Subject: [PATCH 02/14] Add failing test for get_all_names. --- .../TestPostfixConfigGenerator.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 letsencrypt-postfix/TestPostfixConfigGenerator.py diff --git a/letsencrypt-postfix/TestPostfixConfigGenerator.py b/letsencrypt-postfix/TestPostfixConfigGenerator.py new file mode 100644 index 000000000..98a0afb5d --- /dev/null +++ b/letsencrypt-postfix/TestPostfixConfigGenerator.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import io +import logging +import unittest + +import Config +import PostfixConfigGenerator as pcg + + +logger = logging.getLogger(__name__) +logger.addHandler(logging.StreamHandler()) + + +# Fake Postfix Configs +names_only_config = """myhostname = mail.fubard.org +mydomain = fubard.org +myorigin = fubard.org""" + + +def GetFakeOpen(fake_file_contents): + fake_file = io.StringIO() + # cast this to unicode for py2 + fake_file.write(fake_file_contents) + fake_file.seek(0) + + def FakeOpen(_): + return fake_file + + return FakeOpen + + +class TestPostfixConfigGenerator(unittest.TestCase): + + def setUp(self): + self.fopen_names_only_config = GetFakeOpen(names_only_config) + #self.config = Config.Config() + self.config = None + self.postfix_dir = 'tests/' + + def tearDown(self): + pass + + def testGetAllNames(self): + sorted_names = ('fubard.org', 'mail.fubard.org') + postfix_config_gen = pcg.PostfixConfigGenerator( + self.config, + self.postfix_dir, + fixup=True, + fopen=self.fopen_names_only_config + ) + self.assertEqual(sorted_names, postfix_config_gen.get_all_names()) + + +if __name__ == '__main__': + unittest.main() From fd1cef3fa03d026e1a0936a84985d68f67b98614 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Tue, 29 Mar 2016 14:31:27 -0700 Subject: [PATCH 03/14] Implement get_all_names. --- letsencrypt-postfix/PostfixConfigGenerator.py | 9 +++++++++ letsencrypt-postfix/TestPostfixConfigGenerator.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 34654a2d1..9863d05d2 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -171,6 +171,15 @@ class PostfixConfigGenerator: """Returns all names that may be authenticated. :rtype: `list` of `str` """ + var_names = ('myhostname', 'mydomain', 'myorigin') + names_found = set() + for num, line in enumerate(self.cf): + num, found_var, found_value = parse_line((num, line)) + if found_var in var_names: + names_found.add(found_value) + name_list = list(names_found) + name_list.sort() + return name_list def deploy_cert(self, domain, _cert_path, key_path, _chain_path, fullchain_path): """Deploy certificate. diff --git a/letsencrypt-postfix/TestPostfixConfigGenerator.py b/letsencrypt-postfix/TestPostfixConfigGenerator.py index 98a0afb5d..e1d921c6f 100644 --- a/letsencrypt-postfix/TestPostfixConfigGenerator.py +++ b/letsencrypt-postfix/TestPostfixConfigGenerator.py @@ -48,7 +48,7 @@ class TestPostfixConfigGenerator(unittest.TestCase): pass def testGetAllNames(self): - sorted_names = ('fubard.org', 'mail.fubard.org') + sorted_names = ['fubard.org', 'mail.fubard.org'] postfix_config_gen = pcg.PostfixConfigGenerator( self.config, self.postfix_dir, From 0bf2537a55db2752ee5a8f536b80e5e544128155 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Tue, 29 Mar 2016 14:32:53 -0700 Subject: [PATCH 04/14] Add initial gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 94c6f7089..cc957df18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .* *.orig +*.pyc From cc83e9ba52d9bd13dd43e72942458e95b0e6fba8 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 12:26:56 -0700 Subject: [PATCH 05/14] Wrap some lines, new style exceptions, return check for restart. --- letsencrypt-postfix/PostfixConfigGenerator.py | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 1ac9d9fc6..927b7e729 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -28,7 +28,8 @@ class PostfixConfigGenerator: self.fixup = fixup self.postfix_dir = postfix_dir self.policy_config = policy_config - self.policy_file = os.path.join(postfix_dir, "starttls_everywhere_policy") + self.policy_file = os.path.join(postfix_dir, + "starttls_everywhere_policy") self.ca_file = os.path.join(postfix_dir, "starttls_everywhere_CAfile") self.additions = [] @@ -51,7 +52,8 @@ class PostfixConfigGenerator: """ acceptable = [ideal] + also_acceptable - l = [(num,line) for num,line in enumerate(self.cf) if line.startswith(var)] + l = [(num,line) for num,line in enumerate(self.cf) + if line.startswith(var)] if not any(l): self.additions.append(var + " = " + ideal) else: @@ -62,14 +64,18 @@ class PostfixConfigGenerator: self.deletions.extend(conflicting_lines) self.additions.append(var + " = " + ideal) else: - raise ExistingConfigError, "Conflicting existing config values " + `l` + raise ExistingConfigError( + "Conflicting existing config values " + `l` + ) val = values[0][2] if val not in acceptable: if self.fixup: self.deletions.append(values[0][0]) self.additions.append(var + " = " + ideal) else: - raise ExistingConfigError, "Existing config has %s=%s"%(var,val) + raise ExistingConfigError( + "Existing config has %s=%s"%(var,val) + ) def wrangle_existing_config(self): """ @@ -96,12 +102,14 @@ class PostfixConfigGenerator: # - Client: self.ensure_cf_var("smtp_tls_mandatory_protocols", "!SSLv2, !SSLv3", []) - def maybe_add_config_lines(self): + def maybe_add_config_lines(self, fopen=open): if not self.additions: return if self.fixup: print "Deleting lines:", self.deletions - self.additions[:0]=["#","# New config lines added by STARTTLS Everywhere","#"] + self.additions[:0]=["#", + "# New config lines added by STARTTLS Everywhere", + "#"] new_cf_lines = "\n".join(self.additions) + "\n" print "Adding to %s:" % self.fn print new_cf_lines @@ -118,10 +126,10 @@ class PostfixConfigGenerator: if not os.access(self.postfix_cf_file, os.W_OK): raise Exception("Can't write to %s, please re-run as root." % self.postfix_cf_file) - with open(self.fn, "w") as f: + with fopen(self.fn, "w") as f: f.write(self.new_cf) - def set_domainwise_tls_policies(self): + def set_domainwise_tls_policies(self, fopen=open): all_acceptable_mxs = self.policy_config.acceptable_mxs for address_domain, properties in all_acceptable_mxs.items(): mx_list = properties.accept_mx_domains @@ -142,9 +150,8 @@ class PostfixConfigGenerator: print mx_policy.min_tls_version self.policy_lines.append(entry) - f = open(self.policy_file, "w") - f.write("\n".join(self.policy_lines) + "\n") - f.close() + with fopen(self.policy_file, "w") as f: + f.write("\n".join(self.policy_lines) + "\n") ### Let's Encrypt client IPlugin ### # https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L35 @@ -335,17 +342,19 @@ class PostfixConfigGenerator: """ print "Reloading postfix config..." if os.geteuid() != 0: - os.system("sudo service postfix reload") + rc = os.system("sudo service postfix reload") else: - os.system("service postfix reload") + rc = os.system("service postfix reload") + if rc != 0: + raise Exception('PluginError: cannot restart postfix') def update_CAfile(self): os.system("cat /usr/share/ca-certificates/mozilla/*.crt > " + self.ca_file) def usage(): - print ("Usage: %s starttls-everywhere.json /etc/postfix /etc/letsencrypt/live/example.com/" % - sys.argv[0]) + print ("Usage: %s starttls-everywhere.json /etc/postfix " + "/etc/letsencrypt/live/example.com/" % sys.argv[0]) sys.exit(1) From e75bafa43908d9f66f94ffae1393f18c9262f5a1 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 12:27:49 -0700 Subject: [PATCH 06/14] Add basic test for get_all_certs_keys IInstaller interface method. --- .../TestPostfixConfigGenerator.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/letsencrypt-postfix/TestPostfixConfigGenerator.py b/letsencrypt-postfix/TestPostfixConfigGenerator.py index e1d921c6f..ebcf12a4c 100644 --- a/letsencrypt-postfix/TestPostfixConfigGenerator.py +++ b/letsencrypt-postfix/TestPostfixConfigGenerator.py @@ -24,6 +24,12 @@ mydomain = fubard.org myorigin = fubard.org""" +certs_only_config = """ +smtpd_tls_cert_file = /etc/letsencrypt/live/www.fubard.org/fullchain.pem +smtpd_tls_key_file = /etc/letsencrypt/live/www.fubard.org/privkey.pem +""" + + def GetFakeOpen(fake_file_contents): fake_file = io.StringIO() # cast this to unicode for py2 @@ -40,6 +46,7 @@ class TestPostfixConfigGenerator(unittest.TestCase): def setUp(self): self.fopen_names_only_config = GetFakeOpen(names_only_config) + self.fopen_certs_only_config = GetFakeOpen(certs_only_config) #self.config = Config.Config() self.config = None self.postfix_dir = 'tests/' @@ -57,6 +64,18 @@ class TestPostfixConfigGenerator(unittest.TestCase): ) self.assertEqual(sorted_names, postfix_config_gen.get_all_names()) + def testGetAllCertAndKeys(self): + return_vals = ('/etc/letsencrypt/live/www.fubard.org/fullchain.pem', + '/etc/letsencrypt/live/www.fubard.org/privkey.pem', + None) + postfix_config_gen = pcg.PostfixConfigGenerator( + self.config, + self.postfix_dir, + fixup=True, + fopen=self.fopen_certs_only_config + ) + self.assertEqual(return_vals, postfix_config_gen.get_all_certs_keys()) + if __name__ == '__main__': unittest.main() From c6baa82ee4030bc75c8da3ea60ff22865d3a9137 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 15:14:06 -0700 Subject: [PATCH 07/14] Implement basic get_all_certs_keys, tests pass. --- letsencrypt-postfix/PostfixConfigGenerator.py | 11 +++++++++++ letsencrypt-postfix/TestPostfixConfigGenerator.py | 13 ++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 927b7e729..b5adba1a2 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -296,6 +296,17 @@ class PostfixConfigGenerator: - `path` - file path to configuration file :rtype: list """ + cert_materials = {'smtpd_tls_key_file': None, + 'smtpd_tls_cert_file': None, + } + for num, line in enumerate(self.cf): + print 'Line is: %s' % line + num, found_var, found_value = parse_line((num, line)) + if found_var in cert_materials.keys(): + cert_materials[found_var] = found_value + return [(cert_materials['smtpd_tls_cert_file'], + cert_materials['smtpd_tls_key_file'], + self.fn),] def save(self, title=None, temporary=False): """Saves all changes to the configuration files. diff --git a/letsencrypt-postfix/TestPostfixConfigGenerator.py b/letsencrypt-postfix/TestPostfixConfigGenerator.py index ebcf12a4c..57cbb59c0 100644 --- a/letsencrypt-postfix/TestPostfixConfigGenerator.py +++ b/letsencrypt-postfix/TestPostfixConfigGenerator.py @@ -24,10 +24,9 @@ mydomain = fubard.org myorigin = fubard.org""" -certs_only_config = """ -smtpd_tls_cert_file = /etc/letsencrypt/live/www.fubard.org/fullchain.pem -smtpd_tls_key_file = /etc/letsencrypt/live/www.fubard.org/privkey.pem -""" +certs_only_config = ( +"""smtpd_tls_cert_file = /etc/letsencrypt/live/www.fubard.org/fullchain.pem +smtpd_tls_key_file = /etc/letsencrypt/live/www.fubard.org/privkey.pem""") def GetFakeOpen(fake_file_contents): @@ -65,9 +64,9 @@ class TestPostfixConfigGenerator(unittest.TestCase): self.assertEqual(sorted_names, postfix_config_gen.get_all_names()) def testGetAllCertAndKeys(self): - return_vals = ('/etc/letsencrypt/live/www.fubard.org/fullchain.pem', - '/etc/letsencrypt/live/www.fubard.org/privkey.pem', - None) + return_vals = [('/etc/letsencrypt/live/www.fubard.org/fullchain.pem', + '/etc/letsencrypt/live/www.fubard.org/privkey.pem', + 'tests/main.cf'),] postfix_config_gen = pcg.PostfixConfigGenerator( self.config, self.postfix_dir, From 7edceec8ac976ea5680ab081d8884ec6e6ff2a2c Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 15:27:11 -0700 Subject: [PATCH 08/14] Add test case and fix to properly handle configs with no smtpd_tls_* vars. --- letsencrypt-postfix/PostfixConfigGenerator.py | 12 ++++++++---- letsencrypt-postfix/TestPostfixConfigGenerator.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index b5adba1a2..859d86ffe 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -300,13 +300,17 @@ class PostfixConfigGenerator: 'smtpd_tls_cert_file': None, } for num, line in enumerate(self.cf): - print 'Line is: %s' % line num, found_var, found_value = parse_line((num, line)) if found_var in cert_materials.keys(): cert_materials[found_var] = found_value - return [(cert_materials['smtpd_tls_cert_file'], - cert_materials['smtpd_tls_key_file'], - self.fn),] + + if not all(cert_materials.values()): + cert_material_tuples = [] + else: + cert_material_tuples = [(cert_materials['smtpd_tls_cert_file'], + cert_materials['smtpd_tls_key_file'], + self.fn),] + return cert_material_tuples def save(self, title=None, temporary=False): """Saves all changes to the configuration files. diff --git a/letsencrypt-postfix/TestPostfixConfigGenerator.py b/letsencrypt-postfix/TestPostfixConfigGenerator.py index 57cbb59c0..4a96aa30f 100644 --- a/letsencrypt-postfix/TestPostfixConfigGenerator.py +++ b/letsencrypt-postfix/TestPostfixConfigGenerator.py @@ -46,6 +46,8 @@ class TestPostfixConfigGenerator(unittest.TestCase): def setUp(self): self.fopen_names_only_config = GetFakeOpen(names_only_config) self.fopen_certs_only_config = GetFakeOpen(certs_only_config) + self.fopen_no_certs_only_config = self.fopen_names_only_config + #self.config = Config.Config() self.config = None self.postfix_dir = 'tests/' @@ -75,6 +77,15 @@ class TestPostfixConfigGenerator(unittest.TestCase): ) self.assertEqual(return_vals, postfix_config_gen.get_all_certs_keys()) + def testGetAllCertsAndKeys_With_None(self): + postfix_config_gen = pcg.PostfixConfigGenerator( + self.config, + self.postfix_dir, + fixup=True, + fopen=self.fopen_no_certs_only_config + ) + self.assertEqual([], postfix_config_gen.get_all_certs_keys()) + if __name__ == '__main__': unittest.main() From 4d24eb83a82095b7a740263629bbc55f7972e0f9 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 16:11:37 -0700 Subject: [PATCH 09/14] Move version fetching into get_version and implement more_info method. --- letsencrypt-postfix/PostfixConfigGenerator.py | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 859d86ffe..0163e4bff 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -24,7 +24,12 @@ class ExistingConfigError(ValueError): pass class PostfixConfigGenerator: - def __init__(self, policy_config, postfix_dir, fixup=False, fopen=open): + def __init__(self, + policy_config, + postfix_dir, + fixup=False, + fopen=open, + version=None): self.fixup = fixup self.postfix_dir = postfix_dir self.policy_config = policy_config @@ -41,6 +46,9 @@ class PostfixConfigGenerator: self.policy_lines = [] self.new_cf = "" + # Set in .prepare() unless running in a test + self.postfix_version = version + def find_postfix_cf(self): "Search far and wide for the correct postfix configuration file" return os.path.join(self.postfix_dir, "main.cf") @@ -174,13 +182,14 @@ class PostfixConfigGenerator: """ # XXX ensure we raise the right kinds of exceptions - # Parse Postfix version number (feature support, syntax changes etc.) - mail_version = subprocess.Popen(['/usr/sbin/postconf', '-d', 'mail_version'], \ - stdout=subprocess.PIPE) \ - .communicate()[0].split()[2] - maj, min, rev = mail_version.split('.') - self.postfix_version = mail_version - + if not self.postfix_version: + self.postfix_version = self.get_version() + + if self.postfix_version < (2, 11, 0): + raise Exception( + 'NotSupportedError: Postfix version is too old -- test.' + ) + # Postfix has changed support for TLS features, supported protocol versions # KEX methods, ciphers et cetera over the years. We sort out version dependend # differences here and pass them onto other configuration functions. @@ -225,7 +234,28 @@ class PostfixConfigGenerator: # - Built-in support for TLS management and DANE added, see: # http://www.postfix.org/postfix-tls.1.html - return maj, min, rev + def get_version(self): + """Return the mail version of Postfix. + + Version is returned as a tuple. (e.g. '2.11.3' is (2, 11, 3)) + + :returns: version + :rtype: tuple + + :raises .PluginError: + Unable to find Postfix version. + """ + # Parse Postfix version number (feature support, syntax changes etc.) + cmd = subprocess.Popen(['/usr/sbin/postconf', '-d', 'mail_version'], + stdout=subprocess.PIPE) + stdout, _ = cmd.communicate() + if cmd.returncode != 0: + raise Exception('PluginError: Unable to determine Postfix version.') + + # grabs version component of string like "mail_version = 2.11.3" + mail_version = stdout.split()[2] + postfix_version = tuple([int(i) for i in mail_version.split('.')]) + return postfix_version def more_info(self): """Human-readable string to help the user. @@ -233,6 +263,15 @@ class PostfixConfigGenerator: decide which plugin to use. :rtype str: """ + return ( + "Configures Postfix to try to authenticate mail servers, use " + "installed certificates and disable weak ciphers and protocols.{0}" + "Server root: {root}{0}" + "Version: {version}".format( + os.linesep, + root=self.postfix_dir, + version='.'.join([str(i) for i in self.postfix_version])) + ) ### Let's Encrypt client IInstaller ### From c43602c90808d19e78bd2ee213f007e6ee7fc9ad Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 16:30:08 -0700 Subject: [PATCH 10/14] Add simple config_test implementation. --- letsencrypt-postfix/PostfixConfigGenerator.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 0163e4bff..166206560 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -364,7 +364,6 @@ class PostfixConfigGenerator: be quickly reversed in the future (challenges) :raises .PluginError: when save is unsuccessful """ - self.maybe_add_config_lines() def rollback_checkpoints(self, rollback=1): @@ -389,6 +388,12 @@ class PostfixConfigGenerator: """Make sure the configuration is valid. :raises .MisconfigurationError: when the config is not in a usable state """ + if os.geteuid() != 0: + rc = os.system('sudo /usr/sbin/postfix check') + else: + rc = os.system('/usr/sbin/postfix check') + if rc != 0: + raise Exception('MisconfigurationError: Postfix failed self-check.') def restart(self): """Restart or refresh the server content. From 5d07b702695f08dfd0fb35a59f74c411f756c22c Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 16:40:06 -0700 Subject: [PATCH 11/14] Change over to using logging module from print statements. --- letsencrypt-postfix/PostfixConfigGenerator.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 166206560..43a8fa116 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -1,10 +1,15 @@ #!/usr/bin/env python + +import logging import sys import string import subprocess import os, os.path +logger = logging.getLogger(__name__) + + def parse_line(line_data): """ Return the (line number, left hand side, right hand side) of a stripped @@ -114,13 +119,13 @@ class PostfixConfigGenerator: if not self.additions: return if self.fixup: - print "Deleting lines:", self.deletions + logger.info('Deleting lines: {}'.format(self.deletions)) self.additions[:0]=["#", "# New config lines added by STARTTLS Everywhere", "#"] new_cf_lines = "\n".join(self.additions) + "\n" - print "Adding to %s:" % self.fn - print new_cf_lines + logger.info('Adding to {}:'.format(self.fn)) + logger.info(new_cf_lines) if self.raw_cf[-1][-1] == "\n": sep = "" else: sep = "\n" @@ -142,9 +147,12 @@ class PostfixConfigGenerator: for address_domain, properties in all_acceptable_mxs.items(): mx_list = properties.accept_mx_domains if len(mx_list) > 1: - print "Lists of multiple accept-mx-domains not yet supported." - print "Using MX %s for %s" % (mx_list[0], address_domain) - print "Ignoring: %s" % (', '.join(mx_list[1:])) + logger.warn('Lists of multiple accept-mx-domains not yet ' + 'supported.') + logger.warn('Using MX {} for {}".format(mx_list[0], + address_domain) + ) + logger.warn('Ignoring: {}'.format(', '.join(mx_list[1:]))) mx_domain = mx_list[0] mx_policy = self.policy_config.get_tls_policy(mx_domain) entry = address_domain + " encrypt" @@ -155,7 +163,9 @@ class PostfixConfigGenerator: elif mx_policy.min_tls_version.lower() == "tlsv1.2": entry += " protocols=!SSLv2:!SSLv3:!TLSv1:!TLSv1.1" else: - print mx_policy.min_tls_version + logger.warn('Unknown minimum TLS version: {} '.format( + mx_policy.min_tls_version) + ) self.policy_lines.append(entry) with fopen(self.policy_file, "w") as f: @@ -399,7 +409,7 @@ class PostfixConfigGenerator: """Restart or refresh the server content. :raises .PluginError: when server cannot be restarted """ - print "Reloading postfix config..." + logger.info('Reloading postfix config...') if os.geteuid() != 0: rc = os.system("sudo service postfix reload") else: From 887871833d407f4597066ca18c07a0c204a4e097 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 16:44:25 -0700 Subject: [PATCH 12/14] Fix typo in changing quotes. --- letsencrypt-postfix/PostfixConfigGenerator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 43a8fa116..214d98954 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -149,7 +149,7 @@ class PostfixConfigGenerator: if len(mx_list) > 1: logger.warn('Lists of multiple accept-mx-domains not yet ' 'supported.') - logger.warn('Using MX {} for {}".format(mx_list[0], + logger.warn('Using MX {} for {}'.format(mx_list[0], address_domain) ) logger.warn('Ignoring: {}'.format(', '.join(mx_list[1:]))) From af38c30c9c506cb6e40cd4b1b32103d964ab9bee Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 17:02:29 -0700 Subject: [PATCH 13/14] Fix path to postfix config variable. --- letsencrypt-postfix/PostfixConfigGenerator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 214d98954..8fcfb7e8b 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -136,9 +136,9 @@ class PostfixConfigGenerator: self.new_cf += line self.new_cf += sep + new_cf_lines - if not os.access(self.postfix_cf_file, os.W_OK): + if not os.access(self.fn, os.W_OK): raise Exception("Can't write to %s, please re-run as root." - % self.postfix_cf_file) + % self.fn) with fopen(self.fn, "w") as f: f.write(self.new_cf) From a5f23b5314c6afbac26c27e494d3d92603d777e1 Mon Sep 17 00:00:00 2001 From: Daniel Wilcox Date: Thu, 28 Apr 2016 17:10:21 -0700 Subject: [PATCH 14/14] Configure logger to be a touch louder... than silent --- letsencrypt-postfix/PostfixConfigGenerator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/letsencrypt-postfix/PostfixConfigGenerator.py b/letsencrypt-postfix/PostfixConfigGenerator.py index 8fcfb7e8b..38ef0f4c9 100755 --- a/letsencrypt-postfix/PostfixConfigGenerator.py +++ b/letsencrypt-postfix/PostfixConfigGenerator.py @@ -8,6 +8,10 @@ import os, os.path logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +log_handler = logging.StreamHandler() +log_handler.setLevel(logging.DEBUG) +logger.addHandler(log_handler) def parse_line(line_data):