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

195 lines
7.4 KiB
Markdown

## 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](https://en.wikipedia.org/wiki/Wildcard_certificate)
certificate (e.g. `*.52-0-56-137.sslip.io`) from a certificate authority (e.g.
Let's Encrypt) using the [DNS-01
challenge](https://letsencrypt.org/docs/challenge-types/#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](https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment)
protocol client; I use [acme.sh](https://github.com/acmesh-official/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](https://www.cloudflare.com/), [AWS Route
53](https://aws.amazon.com/route53/), my perennial favorite
[easyDNS](https://easydns.com/), etc.). If not using a public DNS service, you
need to run your own DNS server (e.g.
[acme-dns](https://github.com/joohoi/acme-dns), the venerable
[BIND](https://en.wikipedia.org/wiki/BIND), the opinionated
[djbdns](https://cr.yp.to/djbdns.html), or my personal
[wildcard-dns-http-server](https://github.com/cunnie/sslip.io/tree/main/src/wildcard-dns-http-server),
etc.). You can use any ACME client
([acme.sh](https://github.com/acmesh-official/acme.sh),
[Certbot](https://certbot.eff.org/), 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:
```bash
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](https://letsencrypt.org/docs/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
```