From 16944e6adf151e076580c0956674a19fe3536ee1 Mon Sep 17 00:00:00 2001
From: Brian Cunnie
Date: Tue, 12 Aug 2025 19:38:50 -0700
Subject: [PATCH] Nuked: procuring a wildcard certificate
The documentation on how to procure a wildcard certificate had gotten
overly-complicated and stale, the Docker image, old, and the code, even
older.
Perhaps more importantly I couldn't bring myself to care whether people
could procure a wildcard certificate.
Signed-off-by: Brian Cunnie
---
README.md | 4 +-
docs/wildcard.md | 194 -------------------
k8s/Dockerfile-wildcard-dns-http-server | 51 -----
k8s/document_root_sslip.io/index.html | 3 -
src/.gitignore | 1 -
src/wildcard-dns-http-server/go.mod | 5 -
src/wildcard-dns-http-server/go.sum | 28 ---
src/wildcard-dns-http-server/main.go | 236 ------------------------
8 files changed, 1 insertion(+), 521 deletions(-)
delete mode 100644 docs/wildcard.md
delete mode 100644 k8s/Dockerfile-wildcard-dns-http-server
delete mode 100644 src/.gitignore
delete mode 100644 src/wildcard-dns-http-server/go.mod
delete mode 100644 src/wildcard-dns-http-server/go.sum
delete mode 100644 src/wildcard-dns-http-server/main.go
diff --git a/README.md b/README.md
index 908c8ed..4821f73 100644
--- a/README.md
+++ b/README.md
@@ -127,9 +127,7 @@ as ARM64 (AWS Graviton, Apple M1/M2).
`go run main.go -nameservers ns1.example.com,ns2.example.com`). If you're
running your own nameservers, you probably want to set this. Don't forget to
set address records for the new name servers with the `-addresses` flag (see
- below). Exception: `_acme-challenge` records are handled differently to
- accommodate the procurement of Let's Encrypt wildcard certificates; you can
- read more about that procedure [here](docs/wildcard.md)
+ below).
- `-addresses` overrides the default A/AAAA (IPv4/IPv6) address records. For
example, here's how we set the IPv4 record & IPv6 record for our nameserver
(in the `-nameservers` example above), ns1.example.com: `-addresses
diff --git a/docs/wildcard.md b/docs/wildcard.md
deleted file mode 100644
index bd0a591..0000000
--- a/docs/wildcard.md
+++ /dev/null
@@ -1,194 +0,0 @@
-## 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.
-). 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
-```
diff --git a/k8s/Dockerfile-wildcard-dns-http-server b/k8s/Dockerfile-wildcard-dns-http-server
deleted file mode 100644
index 1242915..0000000
--- a/k8s/Dockerfile-wildcard-dns-http-server
+++ /dev/null
@@ -1,51 +0,0 @@
-# cunnie/wildcard-dns-http-server: sslip.io wildcard DNS/HTTP server Dockerfile
-
-# This DNS/HTTP server enables the procurement of wildcard certs for sslip.io
-# subdomains. It's meant to be run on the server whose IP address is the
-# subdomain. e.g. if the subdomain was '207-44-147-10.sslip.io', then this
-# should be run on the server whose IP address is 207.44.147.10, and this will
-# procure a wildcard cert for *.207-44-147-10.sslip.io
-
-# This won't work for private addresses such as 10.0.1.10 or 192.168.0.1.
-
-# Dockerfile of a (Golang-based) DNS/HTTP server.
-
-# - the DNS server only responds to TXT queries, and always responds to TXT queries,
-# and always responds with the same TXT record
-# - the HTTP server allows you to update the TXT record by POST'ing to the /update
-# endpoint with a JSON body of `{"txt":"the-new-TXT-record"}`. The endpoint
-# is compatible with acme-dns.
-# - acme.sh can be configured to update the DNS TXT record via HTTPS.
-
-# To build:
-
-# DOCKER_BUILD_DIR=$PWD
-# pushd ../src/wildcard-dns-http-server/
-# GOOS=linux GOARCH=amd64 go build -o $DOCKER_BUILD_DIR/wildcard-dns-http-server
-# popd
-# docker build . -f Dockerfile-wildcard-dns-http-server -t cunnie/wildcard-dns-http-server
-
-# Typical start command:
-
-# docker run -it --rm -p 53:53/udp -p 80:80 cunnie/wildcard-dns-http-server
-
-# To test from host:
-
-# dig +short txt 127-0-0-1.example.com @localhost
-# "Set this TXT record: curl -X POST http://localhost/update -d '{\"txt\":\"Certificate Authority's validation token\"}'"
-# curl -X POST http://localhost/update -d '{"txt":"new-TXT-record"}'
-# dig +short txt any-domain-you-want @localhost
-# "new-TXT-record"
-
-FROM alpine AS sslip.io
-
-LABEL org.opencontainers.image.authors="Brian Cunnie "
-
-COPY wildcard-dns-http-server /usr/sbin/wildcard-dns-http-server
-
-ENTRYPOINT ["/usr/sbin/wildcard-dns-http-server"]
-
-# DNS listens on port 53 UDP
-# The `EXPOSE` directive doesn't do much in our case. We use it for documentation.
-EXPOSE 53/udp
-EXPOSE 80/tcp
diff --git a/k8s/document_root_sslip.io/index.html b/k8s/document_root_sslip.io/index.html
index 7bf3626..78c46a3 100644
--- a/k8s/document_root_sslip.io/index.html
+++ b/k8s/document_root_sslip.io/index.html
@@ -172,9 +172,6 @@ dig @localhost 127-0-0-1.nip.io +short # returns "127.0.0.1"
https://www.52.0.56.137.xip.example.com/.
- if you're interested in acquiring a wildcard certificate for your nip.io domain, e.g.
- "*.52-0-56-137.nip.io", the procedure is described here.
Experimental Features
Experimental features can change; don't depend on them.
Determining Your External IP Address via DNS Lookup
diff --git a/src/.gitignore b/src/.gitignore
deleted file mode 100644
index 9f11b75..0000000
--- a/src/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.idea/
diff --git a/src/wildcard-dns-http-server/go.mod b/src/wildcard-dns-http-server/go.mod
deleted file mode 100644
index 011d7c2..0000000
--- a/src/wildcard-dns-http-server/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module github.com/cunnie/sslip.io/src/wildcard-dns-http-server
-
-go 1.15
-
-require golang.org/x/net v0.7.0
diff --git a/src/wildcard-dns-http-server/go.sum b/src/wildcard-dns-http-server/go.sum
deleted file mode 100644
index 4b6f231..0000000
--- a/src/wildcard-dns-http-server/go.sum
+++ /dev/null
@@ -1,28 +0,0 @@
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/src/wildcard-dns-http-server/main.go b/src/wildcard-dns-http-server/main.go
deleted file mode 100644
index 4e26ae2..0000000
--- a/src/wildcard-dns-http-server/main.go
+++ /dev/null
@@ -1,236 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "os"
- "runtime"
- "strings"
- "sync"
- "syscall"
-
- "golang.org/x/net/dns/dnsmessage"
-)
-
-var txts = []string{`Set this TXT record: curl -X POST http://localhost/update -d '{"txt":"Certificate Authority validation token"}'`}
-
-// Txt is for parsing the JSON POST to set the DNS TXT record
-type Txt struct {
- Txt string `json:"txt"`
-}
-
-func main() {
- var wg sync.WaitGroup
- log.Println("DNS: starting up.")
- conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
- switch {
- case err == nil:
- log.Println(`DNS: Successfully bound to all interfaces, port 53.`)
- wg.Add(1)
- go dnsServer(conn, &wg)
- case isErrorPermissionsError(err):
- log.Println("DNS: Try invoking me with `sudo` because I don't have permission to bind to port 53.")
- log.Fatal("DNS: " + err.Error())
- case isErrorAddressAlreadyInUse(err):
- log.Println(`DNS: I couldn't bind to "0.0.0.0:53" (INADDR_ANY, all interfaces), so I'll try to bind to each address individually.`)
- ipCIDRs := listLocalIPCIDRs()
- var boundIPsPorts, unboundIPs []string
- for _, ipCIDR := range ipCIDRs {
- ip, _, err := net.ParseCIDR(ipCIDR)
- if err != nil {
- log.Printf(`DNS: I couldn't parse the local interface "%s".`, ipCIDR)
- continue
- }
- conn, err = net.ListenUDP("udp", &net.UDPAddr{
- IP: ip,
- Port: 53,
- Zone: "",
- })
- if err != nil {
- unboundIPs = append(unboundIPs, ip.String())
- } else {
- wg.Add(1)
- boundIPsPorts = append(boundIPsPorts, conn.LocalAddr().String())
- go dnsServer(conn, &wg)
- }
- }
- if len(boundIPsPorts) > 0 {
- log.Printf(`DNS: I bound to the following: "%s"`, strings.Join(boundIPsPorts, `", "`))
- }
- if len(unboundIPs) > 0 {
- log.Printf(`DNS: I couldn't bind to the following IPs: "%s"`, strings.Join(unboundIPs, `", "`))
- }
- default:
- log.Fatal("DNS: " + err.Error())
- }
- wg.Add(1)
- go httpServer(&wg)
- wg.Wait()
-}
-
-func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) {
- var query dnsmessage.Message
-
- defer group.Done()
- queryRaw := make([]byte, 512)
- for {
- _, addr, err := conn.ReadFromUDP(queryRaw)
- if err != nil {
- log.Println("DNS: " + err.Error())
- continue
- }
- err = query.Unpack(queryRaw)
- if err != nil {
- log.Println("DNS: " + err.Error())
- continue
- }
- // Technically, there can be multiple questions in a DNS message; practically, there's only one
- if len(query.Questions) != 1 {
- log.Printf("DNS: I expected one question but got %d.\n", len(query.Questions))
- continue
- }
- // We only return answers to TXT queries, nothing else
- if query.Questions[0].Type != dnsmessage.TypeTXT {
- log.Println("DNS: I expected a question for a TypeTXT record but got a question for a " + query.Questions[0].Type.String() + " record.")
- continue
- }
- var txtAnswers = []dnsmessage.Resource{}
- for _, txt := range txts {
- txtAnswers = append(txtAnswers, dnsmessage.Resource{
- Header: dnsmessage.ResourceHeader{
- Name: query.Questions[0].Name,
- Type: dnsmessage.TypeTXT,
- Class: dnsmessage.ClassINET,
- TTL: 60,
- },
- Body: &dnsmessage.TXTResource{TXT: []string{txt}},
- })
- }
- reply := dnsmessage.Message{
- Header: dnsmessage.Header{
- ID: query.ID,
- Response: true,
- Authoritative: true,
- RecursionDesired: query.RecursionDesired,
- },
- Questions: query.Questions,
- Answers: txtAnswers,
- }
- replyRaw, err := reply.Pack()
- if err != nil {
- log.Println("DNS: " + err.Error())
- continue
- }
- _, err = conn.WriteToUDP(replyRaw, addr)
- if err != nil {
- log.Println("DNS: " + err.Error())
- continue
- }
- log.Printf("DNS: %v.%d %s → \"%v\"\n", addr.IP, addr.Port, query.Questions[0].Type.String(), txts)
- }
-}
-
-func httpServer(group *sync.WaitGroup) {
- defer group.Done()
- log.Println("HTTP: starting up.")
- http.HandleFunc("/", usageHandler)
- http.HandleFunc("/update", updateTxtHandler)
- log.Fatal("HTTP: " + http.ListenAndServe(":80", nil).Error())
-}
-
-func usageHandler(w http.ResponseWriter, r *http.Request) {
- _, err := fmt.Fprintln(w, `Set the TXT record: curl -X POST http://localhost/update -d '{"txt":"Certificate Authority's validation token"}'`)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- log.Println("HTTP: " + err.Error())
- }
- log.Printf("HTTP: wrong path (%s) with method (%s).\n", r.URL.Path, r.Method)
-}
-
-func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
- var err error
- if r.Method != http.MethodPost {
- err = errors.New("/update requires POST method, not " + r.Method + " method")
- http.Error(w, err.Error(), http.StatusBadRequest)
- log.Println("HTTP: " + err.Error())
- return
- }
- var body []byte
- if body, err = ioutil.ReadAll(r.Body); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- log.Println("HTTP: " + err.Error())
- return
- }
- var updateTxt Txt
- if err := json.Unmarshal(body, &updateTxt); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- log.Println("HTTP: " + err.Error())
- return
- }
- if body, err = json.Marshal(updateTxt); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- log.Println("HTTP: " + err.Error())
- return
- }
- if _, err = fmt.Fprintf(w, string(body)); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- log.Println("HTTP: " + err.Error())
- return
- }
- log.Println("HTTP: Creating new TXT record \"" + updateTxt.Txt + "\".")
- // this is the money shot, where we create a new DNS TXT record to what was in the POST request
- txts = append(txts, updateTxt.Txt)
-}
-func listLocalIPCIDRs() []string {
- var ifaces []net.Interface
- var cidrStrings []string
- var err error
- if ifaces, err = net.Interfaces(); err != nil {
- panic(err)
- }
- for _, iface := range ifaces {
- var cidrs []net.Addr
- if cidrs, err = iface.Addrs(); err != nil {
- panic(err)
- }
- for _, cidr := range cidrs {
- cidrStrings = append(cidrStrings, cidr.String())
- }
- }
- return cidrStrings
-}
-
-// Thanks https://stackoverflow.com/a/52152912/2510873
-func isErrorAddressAlreadyInUse(err error) bool {
- var eOsSyscall *os.SyscallError
- if !errors.As(err, &eOsSyscall) {
- return false
- }
- var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
- if !errors.As(eOsSyscall, &errErrno) {
- return false
- }
- if errErrno == syscall.EADDRINUSE {
- return true
- }
- const WSAEADDRINUSE = 10048
- if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
- return true
- }
- return false
-}
-
-func isErrorPermissionsError(err error) bool {
- var eOsSyscall *os.SyscallError
- if errors.As(err, &eOsSyscall) {
- if os.IsPermission(eOsSyscall) {
- return true
- }
- }
- return false
-}