From 2715fc1a994f8552bd5529d0f1f2111b94db5274 Mon Sep 17 00:00:00 2001 From: Niels Basjes Date: Sun, 18 Mar 2018 11:12:21 +0100 Subject: [PATCH 1/2] Added . to CNAME instructions to avoid DNS config mistakes --- acme-dns-auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme-dns-auth.py b/acme-dns-auth.py index 10d3385..2913db0 100755 --- a/acme-dns-auth.py +++ b/acme-dns-auth.py @@ -147,7 +147,7 @@ def fetch(self, key): # Display the notification for the user to update the main zone msg = "Please add the following CNAME record to your main DNS zone:\n{}" - cname = "{} CNAME {}".format(VALIDATION_DOMAIN, account["fulldomain"]) + cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"]) print(msg.format(cname)) # Update the TXT record in acme-dns instance From 401f14327b028282c3165556aceff94cb82c9088 Mon Sep 17 00:00:00 2001 From: jonathan vanasco Date: Thu, 12 Apr 2018 13:22:39 -0400 Subject: [PATCH 2/2] initial work/experiments on switchover to environment vars adds --setup and --version commands --- README.md | 49 ++++++++++++++++++++---- acme-dns-auth.py | 96 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 126 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index a9b7ffc..56f0b58 100644 --- a/README.md +++ b/README.md @@ -18,25 +18,46 @@ $ curl -o /etc/letsencrypt/acme-dns-auth.py https://raw.githubusercontent.com/j $ chmod 0700 /etc/letsencrypt/acme-dns-auth.py ``` -4) Configure the variables in the beginning of the hook script file to point to your acme-dns instance. The only value that you must change is the `ACMEDNS_URL`, other values are optional. +4) Configure `acme-dns-auth` to point to your acme-dns instance by setting environment variables. The only values that you must set are `ACMEDNSAUTH_URL` (which points to your `acme-dns` instance) and `ACMEDNSAUTH_ENV_VERSION` (which is used to ensure compatibility across upgrades). + +The easiest way to do this is by generating setup template using this command: + +```` +$ python /etc/letsencrypt/acme-dns-auth.py --setup ``` -### EDIT THESE: Configuration values ### +That will print out a template that you can customize + +``` +# ---------- CUSTOMIZE THE BELOW ---------- + +# required settings +# # URL to acme-dns instance -ACMEDNS_URL = "https://auth.acme-dns.io" +export ACMEDNSAUTH_URL="https://acme-dns.example.com" +# used to maintain compatibility across future versions +export ACMEDNSAUTH_ENV_VERSION="1" + +# optional settings +# # Path for acme-dns credential storage -STORAGE_PATH = "/etc/letsencrypt/acmedns.json" +export ACMEDNSAUTH_STORAGE_PATH="/etc/letsencrypt/acmedns.json" # Whitelist for address ranges to allow the updates from -# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"] -ALLOW_FROM = [] +# this must be a list encoded as a json string +# Example: `export ACMEDNSAUTH_ALLOW_FROM='["192.168.10.0/24", "::1/128"]'` +export ACMEDNSAUTH_ALLOW_FROM='[]' # Force re-registration. Overwrites the already existing acme-dns accounts. -FORCE_REGISTER = False +export ACMEDNSAUTH_FORCE_REGISTER="False" + +# ---------- ---------- ``` ## Usage On initial run: ``` +$ export ACMEDNSAUTH_URL="https://acme-dns.example.com" +$ export ACMEDNSAUTH_ENV_VERSION="1" $ certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py \ --preferred-challenges dns --debug-challenges \ -d example.org -d \*.example.org @@ -45,4 +66,16 @@ Note that the `--debug-challenges` is mandatory here to pause the Certbot execut After adding the prompted CNAME records to your zone(s), wait for a bit for the changes to propagate over the main DNS zone name servers. This takes anywhere from few seconds up to a few minutes, depending on the DNS service provider software and configuration. Hit enter to continue as prompted to ask Let's Encrypt to validate the records. -After the initial run, Certbot is able to automatically renew your certificates using the stored per-domain acme-dns credentials. +After the initial run, Certbot is able to automatically renew your certificates using the stored per-domain acme-dns credentials. You will still need to `export` the environment variables before running Certbot. + +Whenever you update the script, you should ensure the script is compatible with your existing environment configuration. You can do check this by invoking the script with a `--version` argument. + +```` +$ python /etc/letsencrypt/acme-dns-auth.py --version +``` + +If the version has changed, the best way to update your variables is to generate a new template by invoking the script again with `--setup`, and copy/alter your existing setup as needed + +```` +$ python /etc/letsencrypt/acme-dns-auth.py --setup +``` diff --git a/acme-dns-auth.py b/acme-dns-auth.py index 2913db0..7e429c4 100755 --- a/acme-dns-auth.py +++ b/acme-dns-auth.py @@ -4,27 +4,29 @@ import requests import sys -### EDIT THESE: Configuration values ### + +### you will likely prefer to use environment variables ### +### however you can edit these if you prefer ### # URL to acme-dns instance -ACMEDNS_URL = "https://auth.acme-dns.io" +ACMEDNS_URL = os.environ.get("ACMEDNSAUTH_URL", None) +# used to maintain compatibility across future versions +ENV_VERSION = os.environ.get("ACMEDNSAUTH_ENV_VERSION", None) # Path for acme-dns credential storage -STORAGE_PATH = "/etc/letsencrypt/acmedns.json" +STORAGE_PATH = os.environ.get("ACMEDNSAUTH_STORAGE_PATH", + "/etc/letsencrypt/acmedns.json") # Whitelist for address ranges to allow the updates from # Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"] -ALLOW_FROM = [] +# if customized on the commandline, this must be a list encoded as a json string +# Example: `export ACMEDNSAUTH_ALLOW_FROM='["192.168.10.0/24", "::1/128"]'` +ALLOW_FROM = os.environ.get("ACMEDNSAUTH_ALLOW_FROM", []) # Force re-registration. Overwrites the already existing acme-dns accounts. -FORCE_REGISTER = False +FORCE_REGISTER = os.environ.get("ACMEDNSAUTH_FORCE_REGISTER", False) + ### DO NOT EDIT BELOW THIS POINT ### ### HERE BE DRAGONS ### -DOMAIN = os.environ["CERTBOT_DOMAIN"] -if DOMAIN.startswith("*."): - DOMAIN = DOMAIN[2:] -VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN -VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"] - class AcmeDnsClient(object): """ @@ -132,7 +134,79 @@ def fetch(self, key): except KeyError: return None + +def template_new(env_version): + templated = """ +# ---------- CUSTOMIZE THE BELOW ---------- + +# required settings +# +# URL to acme-dns instance +export ACMEDNSAUTH_URL="https://acme-dns.example.com" +# used to maintain compatibility across future versions +export ACMEDNSAUTH_ENV_VERSION="%(env_version)s" + +# optional settings +# +# Path for acme-dns credential storage +export ACMEDNSAUTH_STORAGE_PATH="/etc/letsencrypt/acmedns.json" +# Whitelist for address ranges to allow the updates from +# this must be a list encoded as a json string +# Example: `export ACMEDNSAUTH_ALLOW_FROM='["192.168.10.0/24", "::1/128"]'` +export ACMEDNSAUTH_ALLOW_FROM='[]' +# Force re-registration. Overwrites the already existing acme-dns accounts. +export ACMEDNSAUTH_FORCE_REGISTER="False" + +# ---------- ---------- +""" % {'env_version': env_version, } + print(templated) + + if __name__ == "__main__": + + # this may be used in the future to handle compatibility concerns + ENV_VERSION__CURRENT = 1 + ENV_VERSION__MINMAX = (1, 1) + + if len(sys.argv) == 2: + if sys.argv[1] == '--version': + print("The current ENV_VERSION/ACMEDNSAUTH_ENV_VERSION is: %s" % ENV_VERSION__CURRENT) + print("This script is compatible with versions: %s-%s" % ENV_VERSION__MINMAX) + sys.exit(1) + if sys.argv[1] == '--setup': + template_new(ENV_VERSION__CURRENT) + sys.exit(1) + + # validation/coercion : BEGIN + if not ACMEDNS_URL: + raise ValueError("`ACMEDNS_URL` or the environment variable " + "`ACMEDNSAUTH_URL` must be set") + if ENV_VERSION is None: + raise ValueError("`ENV_VERSION` or the environment variable " + "`ACMEDNSAUTH_ENV_VERSION` must be set. " + "The current version is %s" % ENV_VERSION__CURRENT) + ENV_VERSION = int(ENV_VERSION) + if not isinstance(ALLOW_FROM, list): + try: + ALLOW_FROM = json.loads(ALLOW_FROM) + if not isinstance(ALLOW_FROM, list): + raise ValueError() + except: + raise ValueError("ALLOW_FROM must be a list") + if not isinstance(FORCE_REGISTER, bool): + if FORCE_REGISTER.lower() in ('true', '1'): + FORCE_REGISTER = True + else: + FORCE_REGISTER = False + # validation/coercion : END + + # resume original script + DOMAIN = os.environ["CERTBOT_DOMAIN"] + if DOMAIN.startswith("*."): + DOMAIN = DOMAIN[2:] + VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN + VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"] + # Init client = AcmeDnsClient(ACMEDNS_URL) storage = Storage(STORAGE_PATH)