Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

PdnsApiAuthenticator.py 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import json
  4. import logging
  5. import time
  6. from certbot import errors
  7. from certbot_pdns.pdnsapi import PdnsApi
  8. logger = logging.getLogger(__name__)
  9. class PdnsApiAuthenticator:
  10. api = None
  11. zones = None
  12. axfr_time = None
  13. def find_best_matching_zone(self, domain):
  14. if domain is None or domain == "":
  15. return None
  16. for zone in self.zones:
  17. if zone['name'] == domain + ".":
  18. return zone
  19. return self.find_best_matching_zone(domain[domain.index(".") + 1:]) if "." in domain else None
  20. def find_soa(self, zone):
  21. for rrset in zone["rrsets"]:
  22. if rrset["type"] == "SOA":
  23. return rrset, rrset["records"][0]
  24. return None
  25. def flush_zone(self, zone_name):
  26. res = self.api.flush_zone_cache(zone_name)
  27. if res is None or "result" not in res or res["result"] != "Flushed cache.":
  28. raise errors.PluginError("Bad return from PDNS API when flushing cache: %s" % res)
  29. def notify_zone(self, zone_name):
  30. res = self.api.notify_zone(zone_name)
  31. if res is None or "result" not in res or res["result"] != "Notification queued":
  32. raise errors.PluginError("Bad return from PDNS API when notifying: %s" % res)
  33. def update_soa(self, zone_name):
  34. zone = self.api.get_zone(zone_name)
  35. if zone is None or "error" in zone:
  36. raise errors.PluginError("Bad return from PDNS API when getting zone %s: %s" % (zone_name, zone))
  37. rrset, soa = self.find_soa(zone)
  38. split = soa["content"].split(" ")
  39. split[2] = str(int(split[2]) + 1)
  40. soa["content"] = ' '.join(split)
  41. res = self.api.replace_record(zone_name, zone_name, rrset["type"], rrset["ttl"], soa["content"],
  42. soa["disabled"], False)
  43. if res is not None:
  44. raise errors.PluginError("Bad return from PDNS API when updating SOA: %s" % res)
  45. def prepare(self, conf_path):
  46. self.api = PdnsApi()
  47. with open(conf_path) as f:
  48. config = json.load(f)
  49. self.api.set_api_key(config["api-key"])
  50. self.api.set_base_url(config["base-url"])
  51. self.api.set_api_pass(config["api-pass"])
  52. self.api.set_http_auth_user(config["http-auth-user"])
  53. self.api.set_http_auth_pass(config["http-auth-pass"])
  54. self.axfr_time = config["axfr-time"]
  55. self.zones = self.api.list_zones()
  56. # print(self.zones)
  57. # raw_input('Press <ENTER> to continue')
  58. if self.zones is None or "error" in self.zones:
  59. raise errors.PluginError("Could not list zones %s" % self.zones)
  60. def perform_single(self, achall, response, validation):
  61. domain = achall.domain
  62. token = validation.encode()
  63. zone = self.find_best_matching_zone(domain)
  64. if zone is None:
  65. raise errors.PluginError("Could not find zone for %s" % domain)
  66. logger.debug("Found zone %s for domain %s" % (zone["name"], domain))
  67. res = self.api.replace_record(zone["name"], "_acme-challenge." + domain + ".", "TXT", 1, "\"" + token.decode('utf-8') + "\"", False, False)
  68. if res is not None:
  69. raise errors.PluginError("Bad return from PDNS API when adding record: %s" % res)
  70. return response
  71. def perform_notify(self, zone):
  72. logger.info("Notifying zone %s..." % zone["name"])
  73. self.update_soa(zone["name"])
  74. self.flush_zone(zone["name"])
  75. self.notify_zone(zone["name"])
  76. def wait_for_propagation(self, achalls):
  77. # TODO search zones authoritative servers and check for TXT record on each of them
  78. # raw_input('Press <ENTER> to continue')
  79. logger.info("Waiting %i seconds..." % self.axfr_time)
  80. time.sleep(self.axfr_time)
  81. def cleanup(self, achall):
  82. domain = achall.domain
  83. zone = self.find_best_matching_zone(domain)
  84. if zone is None:
  85. return
  86. res = self.api.delete_record(zone["name"], "_acme-challenge." + domain + ".", "TXT", 1, None, False, False)
  87. if res is not None:
  88. raise errors.PluginError("Bad return from PDNS API when deleting record: %s" % res)
  89. self.update_soa(zone["name"])
  90. self.flush_zone(zone["name"])
  91. self.notify_zone(zone["name"])