Skip to content

Latest commit



194 lines (166 loc) · 7.36 KB

File metadata and controls

194 lines (166 loc) · 7.36 KB

Procuring a Wildcard Certificate

Using a White Label Domain

Let's say you have a domain that is hosted on Amazon Route53, lets call it You have a few DNS entries set up like, and then you have which is an NS record to So you are able to use both regular DNS records that are hardcoded, and then when you need to use sslip you simply use your xip subdomain.

To get a wildcard certificate for *, simply go through the regular Let's Encrypt DNS-01 challenge process.

Let's Encrypt will query your name servers for the TXT record, then your DNS server will respond with the TXT record that should have been created on Route53 as part of the challenge, otherwise it'll return the delegated nameservers ( and so on).

Using the domain

You can procure a wildcard certificate (e.g. * from a certificate authority (e.g. Let's Encrypt) using the DNS-01 challenge.

You'll need the following:

  • An internet-accessible DNS server that's authoritative for its subdomain For example, if the DNS server's IP address is, the DNS server would need to be authoritative for the domain Pro-tip: it only needs to be authoritative for the _acme-challenge subdomain, e.g.; furthermore, it only needs to return TXT records.

    How to test that your DNS server is working properly (assuming you've set a TXT record, "I love my dog"):

    dig txt
    ...	604800	IN	TXT	"I love my dog"
  • An ACME v2 protocol client; I use The ACME client must be able to update the TXT records of your DNS server.

Using the Wildcard Certificate

Once you've procured the wildcard certificate, you can install it on your internal webservers for URLS of the following format: (e.g. Note that the internal-ip portion of the URL must be dash-separated, not dot-separated, for the wildcard certificate to work properly.

Tech note: wildcard certificates can be used for development for machines behind a firewall using non-routable IP addresses (10/8, 172.16/12, 192.168/16) by taking advantage of the manner which parses hostnames with embedded IP addresses: left-to-right. The internal IP address is parsed first and returned as the IP address of the hostname.

How Do I Set Up an External DNS Server?

The external IP might be from your local network (forward port 53 at your router), or from a cloud provider (GCP, AWS, etc.). It might even be from a public DNS service (e.g. Cloudflare, AWS Route 53, my perennial favorite easyDNS, etc.). If not using a public DNS service, you need to run your own DNS server (e.g. acme-dns, the venerable BIND, the opinionated djbdns, or my personal wildcard-dns-http-server, etc.). You can use any ACME client (, Certbot, etc.), but you must configure it to request a wildcard certificate for *, which requires configuring the DNS-01 challenge to use DNS server chosen.


In the following example, we create a webserver on Google Cloud Platform (GCP) to acquire a wildcard certificate. We use the ACME client and the DNS server wildcard-dns-http-server:

gcloud auth login
 # set your project; mine is "blabbertabber"
gcloud config set project blabbertabber
 # create your VM
gcloud compute instances create \
  --image-project "ubuntu-os-cloud" \
  --image-family "ubuntu-2004-lts" \
  --machine-type f1-micro \
  --boot-disk-size 40 \
  --boot-disk-type pd-ssd \
  --zone "us-west1-a" \
 # get the IP, e.g.
export NAT_IP=$(gcloud compute instances list --filter="name=('sslip')" --format=json | \
  jq -r '.[0].networkInterfaces[0].accessConfigs[0].natIP')
echo $NAT_IP
 # get the fully-qualified domain name, e.g.
export FQDN=${NAT_IP//./-}
echo $FQDN
 # set IP & FQDN on the VM because we'll need them later
gcloud compute ssh --command="echo export FQDN=$FQDN IP=$IP >> ~/.bashrc" --zone=us-west1-a sslip
 # create the rules to allow DNS (and ICMP/ping) inbound
gcloud compute firewall-rules create sslip-io-allow-dns \
  --allow udp:53,icmp \
  --network=default \
  --source-ranges \
 # ssh onto the VM
gcloud compute ssh sslip -- -A
 # install docker
sudo apt update && sudo apt upgrade -y && sudo apt install -y jq
 # add us to the docker group
sudo addgroup $USER docker
newgrp docker
 # Create the necessary directories
mkdir -p tls/
 # disable systemd-resolved to fix "Error starting userland proxy: listen tcp bind: address already in use."
 # thanks
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
echo nameserver | sudo tee /etc/resolv.conf
 # Let's start it up:
docker run -it --rm --name wildcard \
 -p 53:53/udp                       \
 -p 80:80                           \
 cunnie/wildcard-dns-http-server &
dig +short TXT @localhost
 # You should see `"Set this TXT record ..."`
export ACMEDNS_UPDATE_URL="http://localhost/update"
docker run --rm -it \
  -v $PWD/tls:/ \
  --net=host \
  neilpang/ \
    --issue \
    --debug \
    -d $FQDN \
    -d *.$FQDN \
    --dns dns_acmedns
ls tls/$FQDN  # you'll see the new cert, key, certificate
openssl x509 -in tls/$FQDN/$FQDN.cer -noout -text # read the cert info

Save the cert, key, certificate, intermediate ca, fullchain cert. They are in tls/$FQDN/.


gcloud compute firewall-rules delete sslip-io-allow-dns
gcloud compute instances delete sslip

Troubleshooting / Debugging

Run the server in one window so you can see the output, and then ssh into another window and watch the log output in realtime.

gcloud compute ssh sslip -- -A
docker run -it --rm --name wildcard \
 -p 53:53/udp                       \
 -p 80:80                           \

Notes about the logging output: any line that has the string "TypeTXT →" is output from the DNS server; everything else is output from the HTTP server which is used to create TXT records which the DNS server serves.

Use's --staging flag to make sure it works (so you don't run into Let's Encrypt's rate limits with failed attempts).

docker run --rm -it \
  -v $PWD/tls:/ \
  --net=host \
  neilpang/ \
    --issue \
    --staging \
    --debug \
    -d *.$FQDN \
    --dns dns_acmedns