2018-06-04 22:32:16 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
2018-06-06 19:38:20 +00:00
|
|
|
|
|
|
|
"""API client to core-networks"""
|
|
|
|
|
|
|
|
import requests
|
|
|
|
import os
|
|
|
|
import json
|
|
|
|
import urlparse
|
2018-06-07 20:03:27 +00:00
|
|
|
import configparser
|
2018-06-09 21:54:08 +00:00
|
|
|
import sys
|
|
|
|
import logging
|
2018-06-11 21:15:23 +00:00
|
|
|
import tldextract
|
2018-06-19 20:22:19 +00:00
|
|
|
import codecs
|
2018-06-06 19:38:20 +00:00
|
|
|
|
2018-06-20 21:40:23 +00:00
|
|
|
from time import sleep
|
|
|
|
|
2018-06-06 19:38:20 +00:00
|
|
|
|
2018-06-09 21:54:08 +00:00
|
|
|
def setup_logger():
|
|
|
|
# seup logging
|
|
|
|
logger = logging.getLogger("certbot_dns_corenetworks")
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
2018-06-11 21:24:13 +00:00
|
|
|
# silence logging from requests module
|
|
|
|
logging.getLogger('requests').setLevel(logging.CRITICAL)
|
|
|
|
|
2018-06-09 21:54:08 +00:00
|
|
|
# create console handler
|
|
|
|
ch = logging.StreamHandler()
|
|
|
|
ch.setLevel(logging.INFO)
|
|
|
|
|
2018-06-23 17:38:05 +00:00
|
|
|
formatter = logging.Formatter(
|
|
|
|
'%(asctime)s - %(name)s - %(levelname)s: %(message)s', datefmt='%Y-%m-%d,%H:%M:%S')
|
2018-06-09 21:54:08 +00:00
|
|
|
ch.setFormatter(formatter)
|
|
|
|
# add handler to logger
|
|
|
|
logger.addHandler(ch)
|
|
|
|
logger.propagate = False
|
|
|
|
return logger
|
2018-06-06 19:38:20 +00:00
|
|
|
|
|
|
|
|
2018-06-23 17:38:05 +00:00
|
|
|
def set_log_level(config):
|
|
|
|
try:
|
|
|
|
loglevel = str(config.get('LOG', 'LEVEL')).upper()
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
if loglevel == 'INFO':
|
|
|
|
level = logging.INFO
|
|
|
|
elif loglevel == 'ERROR':
|
|
|
|
level = logging.ERROR
|
|
|
|
elif loglevel == 'DEBUG':
|
|
|
|
level = logging.DEBUG
|
|
|
|
else:
|
|
|
|
level = logging.INFO
|
|
|
|
|
|
|
|
logger = logging.getLogger('certbot_dns_corenetworks')
|
|
|
|
logger.setLevel(level)
|
|
|
|
for handler in logger.handlers:
|
|
|
|
handler.setLevel(level)
|
|
|
|
|
|
|
|
|
2018-06-10 00:26:08 +00:00
|
|
|
def load_config(logger):
|
2018-06-19 20:22:19 +00:00
|
|
|
config_path = os.path.join(os.path.expanduser("~"), ".certbot_dns_corenetworks", "config.ini")
|
|
|
|
|
|
|
|
if os.path.isfile(config_path):
|
2018-06-09 20:46:31 +00:00
|
|
|
config = configparser.ConfigParser()
|
|
|
|
config.read(config_path)
|
2018-06-19 20:22:19 +00:00
|
|
|
else:
|
|
|
|
logger.error("Configfile " + config_path + " not present.")
|
|
|
|
sys.exit(1)
|
2018-06-09 20:46:31 +00:00
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
return config
|
2018-06-10 00:26:08 +00:00
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
|
2018-06-19 21:26:31 +00:00
|
|
|
def write_backup(filename, name, value, logger):
|
2018-06-19 20:22:19 +00:00
|
|
|
data = {}
|
|
|
|
data["name"] = name
|
|
|
|
data["data"] = value
|
2018-06-07 20:03:27 +00:00
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
with codecs.open(filename, 'w', encoding='utf8') as f:
|
|
|
|
f.write(json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False))
|
2018-06-10 00:26:08 +00:00
|
|
|
|
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
def get_auth_token(config, logger):
|
2018-06-10 00:26:08 +00:00
|
|
|
data = {}
|
2018-06-19 20:22:19 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
data["login"] = config.get('API', 'USER')
|
|
|
|
data["password"] = config.get('API', 'PASSWORD')
|
|
|
|
json_data = json.dumps(data)
|
|
|
|
|
|
|
|
host = config.get('API', 'HOST')
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
2018-06-10 00:26:08 +00:00
|
|
|
|
|
|
|
url = urlparse.urljoin(host, os.path.join("auth", "token"))
|
|
|
|
|
|
|
|
try:
|
|
|
|
r = requests.post(url, data=json_data)
|
|
|
|
r.raise_for_status()
|
2018-06-19 20:22:19 +00:00
|
|
|
except requests.exceptions.HTTPError as e:
|
2018-06-10 00:26:08 +00:00
|
|
|
logger.error(e)
|
2018-06-11 20:39:56 +00:00
|
|
|
sys.exit(1)
|
2018-06-10 00:26:08 +00:00
|
|
|
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
def get_zone(config, logger):
|
|
|
|
try:
|
|
|
|
host = config.get('API', 'HOST')
|
|
|
|
zone = config.get('DNS', 'ZONE')
|
|
|
|
token = config.get('API', 'TOKEN')
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-10 00:26:08 +00:00
|
|
|
url = urlparse.urljoin(host, os.path.join("dnszones", zone))
|
|
|
|
headers = {"Authorization": "Bearer %s" % (token)}
|
|
|
|
|
|
|
|
try:
|
|
|
|
r = requests.get(url, headers=headers)
|
|
|
|
r.raise_for_status()
|
|
|
|
except requests.exceptions.HTTPError, e:
|
|
|
|
logger.error(e)
|
2018-06-11 20:39:56 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
def set_record(config, logger):
|
2018-06-19 21:26:31 +00:00
|
|
|
filename = '/tmp/CERTBOT_%s.json' % (os.environ['CERTBOT_DOMAIN'])
|
|
|
|
if os.path.isfile(filename):
|
|
|
|
logger.error("File '%s' exists. There seems to be an existing DNS record." % (filename))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-19 20:22:19 +00:00
|
|
|
try:
|
|
|
|
host = config.get('API', 'HOST')
|
|
|
|
zone = config.get('DNS', 'ZONE')
|
|
|
|
token = config.get('API', 'TOKEN')
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-11 20:39:56 +00:00
|
|
|
url = urlparse.urljoin(host, os.path.join("dnszones", zone, "records/"))
|
|
|
|
headers = {"Authorization": "Bearer %s" % (token)}
|
|
|
|
|
|
|
|
try:
|
2018-06-11 21:15:23 +00:00
|
|
|
custom_cache_extract = tldextract.TLDExtract(
|
|
|
|
cache_file=os.path.join(os.path.expanduser("~"), "tld_set"))
|
2018-06-11 21:24:13 +00:00
|
|
|
subdomain = custom_cache_extract(os.environ['CERTBOT_DOMAIN']).subdomain
|
2018-06-19 21:26:31 +00:00
|
|
|
record_name = "_acme-challenge.%s" % (os.environ['CERTBOT_DOMAIN'])
|
2018-06-11 21:24:13 +00:00
|
|
|
if subdomain:
|
|
|
|
certbot_domain = "_acme-challenge.%s" % (subdomain)
|
|
|
|
else:
|
|
|
|
certbot_domain = "_acme-challenge"
|
2018-06-11 20:39:56 +00:00
|
|
|
certbot_validation = os.environ['CERTBOT_VALIDATION']
|
|
|
|
except KeyError, e:
|
|
|
|
logger.error("Environment Variable %s not set" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
data = {}
|
|
|
|
data["name"] = certbot_domain
|
|
|
|
data["ttl"] = 120
|
|
|
|
data["type"] = "TXT"
|
|
|
|
data["data"] = certbot_validation
|
|
|
|
json_data = json.dumps(data)
|
|
|
|
|
|
|
|
try:
|
|
|
|
r = requests.post(url, headers=headers, data=json_data)
|
|
|
|
r.raise_for_status()
|
|
|
|
except requests.exceptions.HTTPError, e:
|
|
|
|
logger.error(e)
|
|
|
|
sys.exit(1)
|
2018-06-10 00:26:08 +00:00
|
|
|
|
2018-06-19 21:26:31 +00:00
|
|
|
write_backup(filename, certbot_domain, certbot_validation, logger)
|
|
|
|
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
|
|
def dns_commit(config, logger):
|
|
|
|
try:
|
|
|
|
host = config.get('API', 'HOST')
|
|
|
|
zone = config.get('DNS', 'ZONE')
|
|
|
|
token = config.get('API', 'TOKEN')
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
url = urlparse.urljoin(host, os.path.join("dnszones", zone, "records", "commit"))
|
|
|
|
headers = {"Authorization": "Bearer %s" % (token)}
|
|
|
|
|
|
|
|
try:
|
|
|
|
r = requests.post(url, headers=headers)
|
|
|
|
r.raise_for_status()
|
|
|
|
except requests.exceptions.HTTPError, e:
|
|
|
|
logger.error(e)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-10 00:26:08 +00:00
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
"""Main logic entrypoint"""
|
|
|
|
|
|
|
|
logger = setup_logger()
|
|
|
|
config = load_config(logger)
|
2018-06-23 17:38:05 +00:00
|
|
|
set_log_level(config)
|
2018-06-10 00:26:08 +00:00
|
|
|
|
|
|
|
# get auth token
|
|
|
|
logger.info("Try to get auth token")
|
2018-06-19 20:22:19 +00:00
|
|
|
r_token = get_auth_token(config, logger)
|
|
|
|
config.set('API', 'TOKEN', r_token.json()["token"])
|
2018-06-10 00:26:08 +00:00
|
|
|
logger.info("Authentication successful")
|
|
|
|
|
|
|
|
# verify dns zone
|
2018-06-19 20:22:19 +00:00
|
|
|
r_zone = get_zone(config, logger)
|
|
|
|
|
|
|
|
try:
|
|
|
|
zone_name = config.get('DNS', 'ZONE')
|
|
|
|
except (configparser.NoSectionError, configparser.NoOptionError) as e:
|
|
|
|
logger.error("Config error: %s" % (e))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-10 00:26:08 +00:00
|
|
|
zone_active = r_zone.json()["active"]
|
|
|
|
|
|
|
|
if zone_active:
|
|
|
|
logger.info("Zone '%s' verified (active)" % (zone_name))
|
|
|
|
else:
|
2018-06-11 20:39:56 +00:00
|
|
|
logger.error("Zone '%s' verified (not active)" % (zone_name))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# add txt record
|
2018-06-19 20:22:19 +00:00
|
|
|
logger.info("Try to set TXT record for dns challenge")
|
|
|
|
z_record = set_record(config, logger)
|
2018-06-19 21:26:31 +00:00
|
|
|
# commit changes
|
|
|
|
z_commit = dns_commit(config, logger)
|
2018-06-20 21:33:17 +00:00
|
|
|
logger.info("Set TXT record successful")
|
2018-06-19 21:26:31 +00:00
|
|
|
|
2018-06-20 21:40:23 +00:00
|
|
|
logger.info("Sleep to propagate over changes to DNS")
|
|
|
|
sleep(30)
|
|
|
|
|
2018-06-06 19:38:20 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|