- Notifications
You must be signed in to change notification settings - Fork0
Naive, self-contained dns-01 gateway service
License
Sp1l/ACME-dns01-gateway
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Intentially naive "pip-less" web-service shim to add/remove_acme-challenge
TXT DNS resource records to your DNS service. Minimalrequirements, all part of a standard Python install.
Built to accompany Apache'smod_md
and run in a FreeBSD jail (or yourcontainer flavour-du-jour). Thus it uses "setup" and "teardown" for the action.There's no reason this couldn't be made to work with any other client viaa curl script.
Uses a "bring-your-own" DNS modification python script. An exampleimplementation script (no pip requirements) can be found in this repo'sproviders/openprovider.py
file.
I don't like to have credentials to my complete DNS zone in my web-server jail.This setup separates the account credentials and will only add/remove_acme-challenge
records.
This setup also allows creating a single service/server that can manage thedns-01 challenge creation and removal for many web-servers.
An attacker adding a spurious_acme-challenge
notification, that's bad enoughbut they'll have to do very fancy stuff to Man-in-the-Middle the connections toyour webserver, especially so if you're domain is DNSSEC protected.
An attacker with full control over your DNS records? That'spants-on-firebad! They'll have no issue MitM-ing all your traffic.
- Clone this repo.
- Create DNS API implementation
- Create configuration
- Run
acmegw_server.py
, it will sit there and listen.
Usedaemon
on FreeBSD to run it in background from anrc.d
script.(Asystemd
unit on Linux?)
The webservice and the DNS API can be configured using (in decreasingprecedence):
- Command-line arguments (long and short): unless long option listed in thetable below, it is the lower-case of the variable.
- Environment variables: only upper-case, verbatim (NOTE: watch out withquoting)
- A
.env
file: formatvariable = value
, variable will be converted toupper-case. Last entry in the file takes precedence.
Variable | arg | env | .env | Default | Description |
---|---|---|---|---|---|
DOTENV | -e | ✓ | ✗ | ./.env | Path to .env file |
LISTEN | -l | ✓ | ✓ | *:8000 | IP:port, port or IP to listen on |
LISTEN_PORT | ✗ | ✓ | ✓ | 8000 | port to listen on |
LISTEN_IP | ✗ | ✓ | ✓ | * | IP-address to listen on |
DNSAPI_USERNAME | ✗ | ✓ | ✓ | Username for DNS API | |
DNSAPI_PASSWORD | ✗ | ✓ | ✓ | Password DNS API | |
DNSAPI_MODULE | -m | ✓ | ✓ | Error | Python module to load for DNSAPI |
DNSAPI_CLASS | -c | ✓ | ✓ | Error | Class inDNSAPI_MODULE to use as DNSAPI |
DNSAPI_DOMAINS 1 | ✗ | ✓ | ✓ | Error | List of (Sub-)Domains manageable via API |
ALL_PROXY 2 | ✗ | ✗ | ✓ | None | Forward proxy to use for providers |
SSL_CERT | ✗ | ✓ | ✓ | cert.pem | Path to SSL certificate file |
SSL_KEY | ✗ | ✓ | ✓ | key.pem | Path to SSL key file |
BASIC_AUTH 3 | ✗ | ✓ | ✓ | None | Enable Basic authentication on API |
ALLOWED_HOSTS 1,3 | ✗ | ✓ | ✓ | None | List of remote IP's/networks allowed to use API |
ALLOWED_PROXIES 1,4 | ✗ | ✓ | ✓ | None | List of remote IP's/networks allowed to set XFF header |
PROXY_XFF 3 | ✗ | ✓ | ✓ | X-Forwarded-For | XFF header to use |
Note1: Lists are comma-separated
Note2: Uses standard proxy environment variables (ALL_PROXY
,HTTPS_PROXY
). If these are not set in environment, useALL_PROXY
from.env
asHTTP_PROXY
andHTTPS_PROXY
value.
Note3: One ofBASIC_AUTH
orALLOWED_HOSTS
must be set, both maybe set. See "API Authentication"
Note4: Only relevant ifALLOWED_HOSTS
is set.
- Lines staring with "#", with optional leading whitespace, will be ignored.
- Trailing comments arenot ignored!
VAR = value # comment
will assignvalue # comment
toVAR
. - Whitespace around the "=" sign will be removed, as will leading and trailingwhitespace of the variable and the value.
- Either single- (') or double- (") quotes work.
- Variables are case-insensitive (values arenot!).
TheBASIC_AUTH
parameter andALLOWED_HOSTS
interact following setting ofBASIC_AUTH
:
- Not configured, empty, "none" or "disabled": Basic authentication disabled.
- "sufficient": Either a matching username and password, or a matching host is sufficient for auth (if configured).
- "required": A matching username and password is required, in addition to matching host (if configured).
MDChallengeDns01Version2MDChallengeDns01 /path/to/wrapper.sh
Example/path/to/wrapper.sh
. Make sure it is executable!
!/bin/shprintf'{"argument": "%s", "domain_name": "%s", "challenge_content": "%s"}' \"$1""$2""$3" \| curl http://dns01gw.example.org:8017/ -X POST --data @-
NOTE: Working with JSON in shell scripts is a real pain with quoting andespecially with spaces. This part of the reason this is implemented in Python.
Payload must be valid json and conform to themod_md
naming of the arguments.
{"argument":"setup|add|teardown|remove","domain_name":"fully.qualified.example.com","challenge_content":"abcdef123456790" }
Nothing more, nothing less.
The server will dynamically import your DNS API script as specified by theDNSAPI_MODULE
andDNSAPI_CLASS
configuration.
Your `DNSAPI_CLASSmust implement the following method:
classMyDNSProvider():defacme_challenge(self,action:str,fqdn:str,token:str):"""Add or remove the _acme-challenge dns-01 validation token Args: action (str): 'setup' or 'teardown' fqdn (str): The Fully Qualified Domain Name, includes `_acme-challenge` prefix token (str): Content for the `_acme-challenge` TXT RR """
Check out theproviders\openprovider.py
example.
Python examples for various DNS providers can be found in e.g.certbot's repo, in thecertbot-dns-*
directories.Shell-script examples can be found in e.g.acme.sh's repodnsapi dir.
About
Naive, self-contained dns-01 gateway service