Acme-DNS

Page content

Web

A simplified DNS server with a RESTful HTTP API to provide a simple way to automate ACME DNS challenges. Sounds promising, right ? Let’s give try ;)

Setup

fireup a new OpenBSD VM

  • let’s do it in London.
  • ip: 100.10.20.30

patch, update, add go

doas su -
syspatch
pkg_add -Vu
pkg_add go

clone repo and build acme-dns

cd /root
git clone https://github.com/joohoi/acme-dns
cd acme-dns
export GOPATH=/tmp/acme-dns
go build
cp acme-dns /usr/local/sbin/

Create Selfsign Cert

the RESTful API need’s a Cert. Let’s use a selfsigned Cert for this demonstration.

cd /etc/ssl
openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 356

Prepare Folders

mkdir -p /etc/acme-dns /var/lib/acme-dns

Build Config File

VM IP: 100.10.20.30 FQDN: london.your.domain

SUBDOMAIN Name: play.london.your.domain

cat << 'EOF' > /etc/acme-dns/config.cfg
[general]
# DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53
# In this case acme-dns will error out and you will need to define the listening interface

# for example: listen = "127.0.0.1:53"
listen = "0.0.0.0:53"

# protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6"
protocol = "both"

# domain name to serve the requests off of
domain = "play.london.your.domain"

# zone name server
nsname = "london.your.domain"

# admin email address, where @ is substituted with .
nsadmin = "[email protected]"

# predefined records served in addition to the TXT
records = [
    # domain pointing to the public IP of your acme-dns server 
    "play.london.your.domain. A 100.10.20.30",

    # specify that auth.example.org will resolve any *.auth.example.org records
    "play.london.your.domain. NS play.london.your.domain.",
]

# debug messages from CORS etc
debug = false

[database]
# Database engine to use, sqlite3 or postgres
engine = "sqlite3"
connection = "/var/lib/acme-dns/acme-dns.db"

[api]
# listen ip eg. 127.0.0.1
ip = "0.0.0.0"

# disable registration endpoint
disable_registration = false

# listen port, eg. 443 for default HTTPS
port = "443"

# possible values: "letsencrypt", "letsencryptstaging", "cert", "none"
tls = "cert"

# only used if tls = "cert"
tls_cert_privkey = "/etc/ssl/key.pem"
tls_cert_fullchain = "/etc/ssl/cert.pem"

# only used if tls = "letsencrypt"
acme_cache_dir = "api-certs"

# optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert
notification_email = ""

# CORS AllowOrigins, wildcards can be used
corsorigins = [
    "*"
]

# use HTTP header to get the client ip
use_header = false

# header name to pull the ip address / list of ip addresses from
header_name = "X-Forwarded-For"

[logconfig]
# logging level: "error", "warning", "info" or "debug"
loglevel = "debug"

# possible values: stdout, TODO file & integrations
logtype = "stdout"
logformat = "text"
EOF

allow FW Port 53 TCP/UDP

pass in log quick proto udp from any to (self) proto 53

run acme-dns

[email protected]# acme-dns                                                                                         
INFO[0000] Using config file                             file=/etc/acme-dns/config.cfg
INFO[0000] Connected to database                        
DEBU[0000] Adding new record to domain                   domain=london.your.domain. recordtype=A
DEBU[0000] Adding new record to domain                   domain=london.your.domain. recordtype=NS
DEBU[0000] Adding new record to domain                   domain=london.your.domain. recordtype=SOA
INFO[0000] Listening HTTPS                               host="0.0.0.0:443"
INFO[0000] Listening DNS                                 addr="0.0.0.0:53" proto=udp
INFO[0000] Listening DNS 

Update DNS Zone

add the following Lines to your DNS Server

london.puffy.work A 100.10.20.30
play NS london.puffy.work

on some Clients

Register Subdomain

# Set Nameserver
ns="london.your.domain"
curl -s -k -X POST "https://${ns}/register" |tee .register.json |jq
user=$(cat .register.json |jq -r '.username')
key=$(cat .register.json |jq -r '.password')
sub=$(cat .register.json |jq -r '.subdomain')

Set Token

curl -k -X POST -H "X-Api-User: ${user}" -H "X-Api-Key: ${key}" -d '{"subdomain": "'${sub}'", "txt": "___validation_token_received_from_the_ca___"}' https://${ns}/update

Verify Token

dig +short -t txt @${ns} ${sub}.${ns}

sha256: f5f89ee30a52096efca247bccf49a40d12f813e99f1143c5ab286aebcbde167b