Browse Source

implement and document the postfix module

Helmut Pozimski 7 years ago
parent
commit
13f2ab6e04
4 changed files with 112 additions and 0 deletions
  1. 10 0
      README.md
  2. 7 0
      amulib/main.py
  3. 89 0
      amulib/postfix.py
  4. 6 0
      example/config.json

+ 10 - 0
README.md

@@ -49,6 +49,16 @@ This module accepts the following configuration parameters:
 * tlsa_exclude: domains that should not receive a TLSA record
 
 The module will parse all vhosts, determine if they use a Let's encrypt certificate and manage it if this is the case and the vhost isn't excluded.
+
+### Postfix
+
+This module accepts the following configuration parameters:
+
+* certificate_path: path of the certificate file (public key and chain)
+* key_path: path of the private key file
+* tlsa: whether to write tlsa records for the domain
+* tlsa_ports: ports that should receive a TLSA record
+
 ## INSTALLATION
 
 Use the setup.py to perform the installation, this requires the setuptools module.

+ 7 - 0
amulib/main.py

@@ -26,6 +26,7 @@ import sys
 
 from amulib.helpers import get_log_level
 from amulib import apache
+from amulib import postfix
 
 
 def main():
@@ -64,3 +65,9 @@ def main():
                        config["named_key_path"])
         else:
             apache.run()
+    if args.postfix:
+        if config:
+            postfix.run(config["postifx"], config["acme_dir"],
+                        config["named_key_path"])
+        else:
+            postfix.run()

+ 89 - 0
amulib/postfix.py

@@ -0,0 +1,89 @@
+#   This file is part of acme-updater, written by Helmut Pozimski 2016-2017.
+#
+#   stov 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, version 2 of the License.
+#
+#   stov 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 stov.  If not, see <http://www.gnu.org/licenses/>.
+
+
+# -*- coding: utf8 -*-
+
+""" Contains the postfix module which manages certificates for the postfix
+mail server.
+"""
+
+import logging
+import socket
+import os
+import subprocess
+
+from amulib import helpers
+import OpenSSL
+
+LOGGER = logging.getLogger("acme_tlsa_mail")
+
+
+def run(config=None, acme_dir="/var/lib/acme",
+        named_key_path="/run/named/session.key"):
+    hostname = socket.gethostname()
+    fqdn = socket.getfqdn()
+    if config:
+        certificate_path = config["certificate_path"]
+        key_path = config["key_path"]
+        tlsa = config["tlsa"]
+        tlsa_ports = config["tlsa_ports"]
+    else:
+        certificate_path = "/etc/postfix/%s.crt" % hostname
+        key_path = "/etc/postfix/%s.key" % hostname
+        tlsa = True
+        tlsa_ports = [25, 465, 587]
+    try:
+        with open(certificate_path, "r") as cert_file:
+            cert_text = cert_file.read()
+    except IOError:
+        LOGGER.error("Error while opening the postfix certificate")
+    else:
+        current_cert = OpenSSL.crypto.load_certificate(
+            OpenSSL.crypto.FILETYPE_PEM, cert_text
+        )
+        acme_cert_path = os.path.join(acme_dir, "live", fqdn,
+                                      "cert")
+        acme_fullchain_path = os.path.join(acme_dir, "live", fqdn,
+                                           "fullchain")
+        if helpers.check_renewal(current_cert, acme_cert_path):
+            try:
+                with open(acme_cert_path, "r") as acme_cert_file:
+                    acme_cert_text = acme_cert_file.read()
+            except IOError:
+                LOGGER.error("Error while opening new postfix "
+                             "certificate file")
+            else:
+                acme_cert = OpenSSL.crypto.load_certificate(
+                    OpenSSL.crypto.FILETYPE_PEM, acme_cert_text
+                )
+                if tlsa:
+                    for port in tlsa_ports:
+                        helpers.create_tlsa_records(fqdn, port, acme_cert,
+                                                    named_key_path)
+                if helpers.copy_file(acme_fullchain_path, certificate_path):
+                    newkey_path = os.path.join(acme_dir, "live",
+                                               fqdn, "privkey")
+                    if helpers.copy_file(newkey_path, key_path):
+                        LOGGER.info("Certificate for postfix successfully "
+                                    "renewed, restarting service.")
+                        subprocess.call(["/etc/init.d/postfix", "restart"])
+                    else:
+                        LOGGER.error("Renewal of cert for postfix failed, "
+                                     "please clean up manually and "
+                                     "check the backup files!")
+                else:
+                    LOGGER.error("Renewal of cert for postfix failed, "
+                                 "please clean up manually and "
+                                 "check the backup files!")

+ 6 - 0
example/config.json

@@ -7,5 +7,11 @@
     "tlsa": false,
     "exclude_vhosts": [],
     "tlsa_exclude": []
+  },
+  "postfix": {
+    "certificate_path": "/etc/postfix/localhost.crt",
+    "key_path": "/etc/postfix/localhost.key",
+    "tlsa": true,
+    "tlsa_ports": [25, 465, 587]
   }
 }