Files
nip/docs/wildcard.md
Brian Cunnie 50e6d71ee4 ns-gce is dead! Long live ns-ovh-sg!
I'm worried the traffic to my GCP server will cost me a hundred dollars
in bandwidth fees. It has a volume similar to my late AWS server which,
in its last month, racked up ~$130 in bandwidth fees!

I'm also trying to balance the servers more geographically: instead of
having two servers in the US and none in Asia, I'll have one server in
the US and one in Asia (Singapore).

The OVH server in Asia is expensive — $60/month instead of $20/month for
the OVH server in Warsaw. Also there's a monthly bandwidth cap in
Singapore in addition to the 300 Mbps cap.

I went with a dedicated server, similar to the one in Warsaw, but I took
the opportunity to upgrade it (same price):

- ns-ovh:    KS-4: Intel Xeon-E3 1230 v6
- ns-ovh-sg: KS-5: Intel Xeon-E3 1270 v6

I'm hoping that by adding this server to Singapore, the traffic to the
ns-ovh, the Warsaw server, will lessen, and I won't get thos "Anti-DDoS
protection enabled for IP address 51.75.53.19" emails every few days.

Current Queries per second:

- 4,087 ns-gce
- 1,131 ns-hetzner
- 7,183 ns-ovh
2025-04-27 06:30:43 -07:00

7.4 KiB

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 example.com. You have a few DNS entries set up like foo.example.com, and then you have xip.example.com which is an NS record to ns-ovh.sslip.io. 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 *.xip.example.com, simply go through the regular Let's Encrypt DNS-01 challenge process.

Let's Encrypt will query your name servers for the TXT record _acme-challenge.xip.example.com, 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 (ns-ovh.sslip.io and so on).

Using the sslip.io domain

You can procure a wildcard certificate (e.g. *.52-0-56-137.sslip.io) 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 sslip.io subdomain For example, if the DNS server's IP address is 52.187.42.158, the DNS server would need to be authoritative for the domain 52-187-42-158.sslip.io. Pro-tip: it only needs to be authoritative for the _acme-challenge subdomain, e.g. _acme-challenge.52-187-42-158.sslip.io; 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 _acme-challenge.52-187-42-158.sslip.io txt
    ...
    _acme-challenge.52-187-42-158.sslip.io	604800	IN	TXT	"I love my dog"
    ...
    
  • An ACME v2 protocol client; I use acme.sh. 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: https://internal-ip.external-ip.sslip.io (e.g. https://www-192-168-0-10.52-187-42-158.sslip.io). 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 sslip.io 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 (acme.sh, Certbot, etc.), but you must configure it to request a wildcard certificate for *.external-ip.sslip.io, which requires configuring the DNS-01 challenge to use DNS server chosen.

Example

In the following example, we create a webserver on Google Cloud Platform (GCP) to acquire a wildcard certificate. We use the ACME client acme.sh 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" \
  sslip
 # get the IP, e.g. 35.199.174.9
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. 35-199-174-9.sslip.io
export FQDN=${NAT_IP//./-}.sslip.io
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 0.0.0.0/0 \
 # ssh onto the VM
gcloud compute ssh sslip -- -A
 # install docker
sudo apt update && sudo apt upgrade -y && sudo apt install -y docker.io 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 0.0.0.0:53: bind: address already in use."
 # thanks https://askubuntu.com/questions/907246/how-to-disable-systemd-resolved-in-ubuntu
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
echo nameserver 8.8.8.8 | 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 does.not.matter.example.com @localhost
 # You should see `"Set this TXT record ..."`
export ACMEDNS_UPDATE_URL="http://localhost/update"
docker run --rm -it \
  -v $PWD/tls:/acme.sh \
  -e ACMEDNS_UPDATE_URL \
  --net=host \
  neilpang/acme.sh \
    --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/.

Clean-up:

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                           \
 cunnie/wildcard-dns-http-server

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 acme.sh'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:/acme.sh \
  -e ACMEDNS_UPDATE_URL \
  --net=host \
  neilpang/acme.sh \
    --issue \
    --staging \
    --debug \
    -d *.$FQDN \
    --dns dns_acmedns