From eca4c67afa8fd167066baeeacbaab070393eff6d Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 14 Aug 2017 23:53:38 -0400 Subject: [PATCH] Support multiple CloudFlare accounts --- README.md | 8 ++++++++ hook.py | 36 ++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ef0e606..dd1e4e6 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,14 @@ $ export CF_EMAIL='user@example.com' $ export CF_KEY='K9uX2HyUjeWg5AhAb' ``` +You can supply multiple account credentials by separating them with one or more spaces. Accounts will be tried in the order given, until one is found that serves the relevant domain. +Leading, trailing, and extra spaces are ignored, so you can vertically align credential pairs for easy reading: + +``` +$ export CF_EMAIL='user1@example.com user2@somewhere.com' +$ export CF_KEY=' K9uX2HyUjeWg5AhAtreb fdsfjhFdaKls45354kHJ9hsj' +``` + Optionally, you can specify the DNS servers to be used for propagation checking via the `CF_DNS_SERVERS` environment variable (props [bennettp123](https://github.com/bennettp123)): ``` diff --git a/hook.py b/hook.py index 537a20d..de41d8b 100755 --- a/hook.py +++ b/hook.py @@ -39,11 +39,11 @@ logger.setLevel(logging.INFO) try: - CF_HEADERS = { - 'X-Auth-Email': os.environ['CF_EMAIL'], - 'X-Auth-Key' : os.environ['CF_KEY'], + CF_HEADERS = [{ + 'X-Auth-Email': e, + 'X-Auth-Key' : k, 'Content-Type': 'application/json', - } + } for e,k in zip(os.environ['CF_EMAIL'].split(), os.environ['CF_KEY'].split()) ] except KeyError: logger.error(" + Unable to locate Cloudflare credentials in environment!") sys.exit(1) @@ -78,15 +78,19 @@ def _has_dns_propagated(name, token): def _get_zone_id(domain): tld = get_tld('http://' + domain) url = "https://api.cloudflare.com/client/v4/zones?name={0}".format(tld) - r = requests.get(url, headers=CF_HEADERS) - r.raise_for_status() - return r.json()['result'][0]['id'] - + for auth in CF_HEADERS: + r = requests.get(url, headers=auth) + r.raise_for_status() + r = r.json().get('result',()) + if r: + return auth, r[0]['id'] + logger.error(" + Domain {0} not found in any Cloudflare account".format(tld)) + sys.exit(1) # https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details -def _get_txt_record_id(zone_id, name, token): +def _get_txt_record_id(auth, zone_id, name, token): url = "https://api.cloudflare.com/client/v4/zones/{0}/dns_records?type=TXT&name={1}&content={2}".format(zone_id, name, token) - r = requests.get(url, headers=CF_HEADERS) + r = requests.get(url, headers=auth) r.raise_for_status() try: record_id = r.json()['result'][0]['id'] @@ -102,10 +106,10 @@ def create_txt_record(args): domain, challenge, token = args logger.debug(' + Creating TXT record: {0} => {1}'.format(domain, token)) logger.debug(' + Challenge: {0}'.format(challenge)) - zone_id = _get_zone_id(domain) + auth, zone_id = _get_zone_id(domain) name = "{0}.{1}".format('_acme-challenge', domain) - record_id = _get_txt_record_id(zone_id, name, token) + record_id = _get_txt_record_id(auth, zone_id, name, token) if record_id: logger.debug(" + TXT record exists, skipping creation.") return @@ -117,7 +121,7 @@ def create_txt_record(args): 'content': token, 'ttl': 120, } - r = requests.post(url, headers=CF_HEADERS, json=payload) + r = requests.post(url, headers=auth, json=payload) r.raise_for_status() record_id = r.json()['result']['id'] logger.debug(" + TXT record created, CFID: {0}".format(record_id)) @@ -130,13 +134,13 @@ def delete_txt_record(args): logger.info(" + http_request() error in letsencrypt.sh?") return - zone_id = _get_zone_id(domain) + auth, zone_id = _get_zone_id(domain) name = "{0}.{1}".format('_acme-challenge', domain) - record_id = _get_txt_record_id(zone_id, name, token) + record_id = _get_txt_record_id(auth, zone_id, name, token) if record_id: url = "https://api.cloudflare.com/client/v4/zones/{0}/dns_records/{1}".format(zone_id, record_id) - r = requests.delete(url, headers=CF_HEADERS) + r = requests.delete(url, headers=auth) r.raise_for_status() logger.debug(" + Deleted TXT {0}, CFID {1}".format(name, record_id)) else: