service.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # This file is part of acme-updater, written by Helmut Pozimski 2016-2017.
  2. #
  3. # stov is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, version 2 of the License.
  6. #
  7. # stov is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License
  13. # along with stov. If not, see <http://www.gnu.org/licenses/>.
  14. # -*- coding: utf8 -*-
  15. """ Contains the service module which manages certificates for generic
  16. services that don't need any special configuration. The configuration
  17. needs to at least contain the keys "certificate_path", "key_path",
  18. "tlsa"and "tlsa_ports".
  19. """
  20. import logging
  21. import os
  22. import subprocess
  23. import shutil
  24. from amulib import helpers
  25. import OpenSSL
  26. LOGGER = logging.getLogger("acme-updater")
  27. def run(service_name, config, acme_dir="/var/lib/acme",
  28. named_key_path="/run/named/session.key", dns_server="localhost"):
  29. """
  30. :param service_name: name of the service
  31. :type service_name: str
  32. :param config: configuration for the service
  33. :type config: dict
  34. :param acme_dir: path to the acme state dir
  35. :type acme_dir: str
  36. :param named_key_path: path to the named session.key
  37. :type named_key_path: str
  38. :param dns_server: DNS server to use to create TLSA records
  39. :type dns_server: str
  40. """
  41. certificate_path = config["certificate_path"]
  42. key_path = config["key_path"]
  43. tlsa = config["tlsa"]
  44. tlsa_ports = config["tlsa_ports"]
  45. renewal_successful = False
  46. try:
  47. with open(certificate_path, "r") as cert_file:
  48. cert_text = cert_file.read()
  49. except IOError:
  50. LOGGER.error("Error while opening the %s certificate", service_name)
  51. else:
  52. current_cert = OpenSSL.crypto.load_certificate(
  53. OpenSSL.crypto.FILETYPE_PEM, cert_text
  54. )
  55. cert_alt_names = helpers.get_subject_alt_name(current_cert)
  56. fqdn = cert_alt_names[0]
  57. acme_cert_path = os.path.join(acme_dir, "live", fqdn,
  58. "cert")
  59. acme_fullchain_path = os.path.join(acme_dir, "live", fqdn,
  60. "fullchain")
  61. if helpers.check_renewal(current_cert, acme_cert_path):
  62. try:
  63. with open(acme_cert_path, "r") as acme_cert_file:
  64. acme_cert_text = acme_cert_file.read()
  65. except IOError:
  66. LOGGER.error("Error while opening new %s "
  67. "certificate file", service_name)
  68. else:
  69. acme_cert = OpenSSL.crypto.load_certificate(
  70. OpenSSL.crypto.FILETYPE_PEM, acme_cert_text
  71. )
  72. if tlsa:
  73. for name in cert_alt_names:
  74. for port in tlsa_ports:
  75. helpers.create_tlsa_records(name, port, acme_cert,
  76. named_key_path,
  77. dns_server)
  78. newkey_path = os.path.join(acme_dir, "live",
  79. fqdn, "privkey")
  80. if certificate_path == key_path:
  81. if helpers.create_backup_copy(certificate_path):
  82. try:
  83. with open(certificate_path, "wb") as target:
  84. with open(acme_fullchain_path, "rb") as chain:
  85. with open(newkey_path, "rb") as newkey:
  86. shutil.copyfileobj(newkey,
  87. target)
  88. shutil.copyfileobj(chain, target)
  89. except IOError:
  90. LOGGER.error("Renewal of cert for %s failed, "
  91. "please clean up manually and "
  92. "check the backup files!",
  93. service_name)
  94. else:
  95. renewal_successful = True
  96. else:
  97. LOGGER.error("Renewal of cert for %s failed, "
  98. "please clean up manually and "
  99. "check the backup files!", service_name)
  100. else:
  101. if helpers.copy_file(acme_fullchain_path,
  102. certificate_path):
  103. if helpers.copy_file(newkey_path, key_path):
  104. renewal_successful = True
  105. else:
  106. LOGGER.error("Renewal of cert for %s failed, "
  107. "please clean up manually and "
  108. "check the backup files!",
  109. service_name)
  110. else:
  111. LOGGER.error("Renewal of cert for %s failed, "
  112. "please clean up manually and "
  113. "check the backup files!", service_name)
  114. if renewal_successful:
  115. LOGGER.info("Certificate for %s successfully "
  116. "renewed, restarting service.",
  117. service_name)
  118. subprocess.call(["/etc/init.d/%s" % service_name,
  119. "restart"])