Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial experiments on switchover to environment vars #7

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```
98 changes: 86 additions & 12 deletions acme-dns-auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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)
Expand All @@ -147,7 +221,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
Expand Down