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 -}