service.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # SPDX-FileCopyrightText: 2016-2017 Helmut Pozimski <helmut@pozimski.eu>
  2. #
  3. # SPDX-License-Identifier: GPL-2.0-only
  4. # -*- coding: utf8 -*-
  5. """ Contains the service module which manages certificates for generic
  6. services that don't need any special configuration. The configuration
  7. needs to at least contain the keys "certificate_path", "key_path",
  8. "tlsa"and "tlsa_ports".
  9. """
  10. import logging
  11. import os
  12. import subprocess
  13. import shutil
  14. from amulib import helpers
  15. import OpenSSL
  16. LOGGER = logging.getLogger("acme-updater")
  17. def run(service_name, config, acme_dir="/var/lib/acme",
  18. named_key_path="/run/named/session.key", dns_server="localhost"):
  19. """
  20. :param service_name: name of the service
  21. :type service_name: str
  22. :param config: configuration for the service
  23. :type config: dict
  24. :param acme_dir: path to the acme state dir
  25. :type acme_dir: str
  26. :param named_key_path: path to the named session.key
  27. :type named_key_path: str
  28. :param dns_server: DNS server to use to create TLSA records
  29. :type dns_server: str
  30. """
  31. certificate_path = config["certificate_path"]
  32. key_path = config["key_path"]
  33. tlsa = config["tlsa"]
  34. tlsa_ports = config["tlsa_ports"]
  35. renewal_successful = False
  36. try:
  37. with open(certificate_path, "r") as cert_file:
  38. cert_text = cert_file.read()
  39. except IOError:
  40. LOGGER.error("Error while opening the %s certificate", service_name)
  41. else:
  42. current_cert = OpenSSL.crypto.load_certificate(
  43. OpenSSL.crypto.FILETYPE_PEM, cert_text
  44. )
  45. cert_alt_names = helpers.get_subject_alt_name(current_cert)
  46. fqdn = cert_alt_names[0]
  47. acme_cert_path = os.path.join(acme_dir, "live", fqdn,
  48. "cert")
  49. acme_fullchain_path = os.path.join(acme_dir, "live", fqdn,
  50. "fullchain")
  51. if helpers.check_renewal(current_cert, acme_cert_path):
  52. try:
  53. with open(acme_cert_path, "r") as acme_cert_file:
  54. acme_cert_text = acme_cert_file.read()
  55. except IOError:
  56. LOGGER.error("Error while opening new %s "
  57. "certificate file", service_name)
  58. else:
  59. acme_cert = OpenSSL.crypto.load_certificate(
  60. OpenSSL.crypto.FILETYPE_PEM, acme_cert_text
  61. )
  62. if tlsa:
  63. for name in cert_alt_names:
  64. for port in tlsa_ports:
  65. helpers.create_tlsa_records(name, port, acme_cert,
  66. named_key_path,
  67. dns_server)
  68. newkey_path = os.path.join(acme_dir, "live",
  69. fqdn, "privkey")
  70. if certificate_path == key_path:
  71. if helpers.create_backup_copy(certificate_path):
  72. try:
  73. with open(certificate_path, "wb") as target:
  74. with open(acme_fullchain_path, "rb") as chain:
  75. with open(newkey_path, "rb") as newkey:
  76. shutil.copyfileobj(newkey,
  77. target)
  78. shutil.copyfileobj(chain, target)
  79. except IOError:
  80. LOGGER.error("Renewal of cert for %s failed, "
  81. "please clean up manually and "
  82. "check the backup files!",
  83. service_name)
  84. else:
  85. renewal_successful = True
  86. else:
  87. LOGGER.error("Renewal of cert for %s failed, "
  88. "please clean up manually and "
  89. "check the backup files!", service_name)
  90. else:
  91. if helpers.copy_file(acme_fullchain_path,
  92. certificate_path):
  93. if helpers.copy_file(newkey_path, key_path):
  94. renewal_successful = True
  95. else:
  96. LOGGER.error("Renewal of cert for %s failed, "
  97. "please clean up manually and "
  98. "check the backup files!",
  99. service_name)
  100. else:
  101. LOGGER.error("Renewal of cert for %s failed, "
  102. "please clean up manually and "
  103. "check the backup files!", service_name)
  104. if renewal_successful:
  105. LOGGER.info("Certificate for %s successfully "
  106. "renewed, restarting service.",
  107. service_name)
  108. subprocess.call(["/etc/init.d/%s" % service_name,
  109. "restart"])