From 0614f2b059cdbf5031e1bc6766b6e46827d1d40f Mon Sep 17 00:00:00 2001 From: Brian Cunnie Date: Sat, 9 Jan 2021 10:32:24 -0800 Subject: [PATCH] Custom DNS Server returns only TXT records MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This small DNS server only returns one type of record, a TXT record, meant to be a token assigned by a certificate authority (e.g. Let's Encrypt) to verify domain ownership. The TXT record will be updateable by an API endpoint on the webserver (same executable as the DNS server), but I haven't yet written that portion. Drive-by: in our _other_ (main) sslip.io DNS server, I changed `break` → `continue` in the main loop. Had we gotten a malformed UDP packet, we would have exited, but now we continue to the next packet. Exiting is not that big a deal—`monit` would have restarted the server—but moving on to the next packet is a more robust approach. [#6] --- bosh-release/src/sslip.io-dns-server/main.go | 2 +- .../src/wildcard-dns-http-server/go.mod | 5 ++ .../src/wildcard-dns-http-server/go.sum | 6 ++ .../src/wildcard-dns-http-server/main.go | 90 +++++++++++++++++++ docs/wildcard.md | 4 +- 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 bosh-release/src/wildcard-dns-http-server/go.mod create mode 100644 bosh-release/src/wildcard-dns-http-server/go.sum create mode 100644 bosh-release/src/wildcard-dns-http-server/main.go diff --git a/bosh-release/src/sslip.io-dns-server/main.go b/bosh-release/src/sslip.io-dns-server/main.go index 16573d5..3d27bd4 100644 --- a/bosh-release/src/sslip.io-dns-server/main.go +++ b/bosh-release/src/sslip.io-dns-server/main.go @@ -19,7 +19,7 @@ func main() { _, addr, err := conn.ReadFromUDP(query) if err != nil { log.Println(err.Error()) - break + continue } go func() { diff --git a/bosh-release/src/wildcard-dns-http-server/go.mod b/bosh-release/src/wildcard-dns-http-server/go.mod new file mode 100644 index 0000000..5871dbc --- /dev/null +++ b/bosh-release/src/wildcard-dns-http-server/go.mod @@ -0,0 +1,5 @@ +module github.com/cunnie/sslip.io/bosh-release/src/wildcard-dns-http-server + +go 1.15 + +require golang.org/x/net v0.0.0-20201224014010-6772e930b67b diff --git a/bosh-release/src/wildcard-dns-http-server/go.sum b/bosh-release/src/wildcard-dns-http-server/go.sum new file mode 100644 index 0000000..b8f64b7 --- /dev/null +++ b/bosh-release/src/wildcard-dns-http-server/go.sum @@ -0,0 +1,6 @@ +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/bosh-release/src/wildcard-dns-http-server/main.go b/bosh-release/src/wildcard-dns-http-server/main.go new file mode 100644 index 0000000..12b14ca --- /dev/null +++ b/bosh-release/src/wildcard-dns-http-server/main.go @@ -0,0 +1,90 @@ +package main + +import ( + "log" + "net" + "sync" + + "golang.org/x/net/dns/dnsmessage" +) + +var txt = `Set this TXT record: curl -X POST http://localhost/update -d '{"txt":"Certificate Authority's validation token"}'` + +func main() { + conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53}) + if err != nil { + log.Fatal(err.Error()) + } + + var group sync.WaitGroup + group.Add(1) + go dnsServer(conn, &group) + group.Add(1) + go httpServer(&group) + group.Wait() +} + +func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) { + var query dnsmessage.Message + + defer group.Done() + log.Println("I'm firing up the DNS server.") + queryRaw := make([]byte, 512) + for { + _, addr, err := conn.ReadFromUDP(queryRaw) + if err != nil { + log.Println(err.Error()) + continue + } + err = query.Unpack(queryRaw) + if err != nil { + log.Println(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("I expected one question but got %d.\n", len(query.Questions)) + continue + } + // We only return answers to TXT records, nothing else + if query.Questions[0].Type != dnsmessage.TypeTXT { + log.Println("I expected a question for a TypeTXT record but got a question for a " + query.Questions[0].Type.String() + " record.") + continue + } + reply := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: query.ID, + Response: true, + Authoritative: true, + RecursionDesired: query.RecursionDesired, + }, + Questions: query.Questions, + Answers: []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: query.Questions[0].Name, + Type: dnsmessage.TypeTXT, + Class: dnsmessage.ClassINET, + }, + Body: &dnsmessage.TXTResource{TXT: []string{txt}}, + }, + }, + } + replyRaw, err := reply.Pack() + if err != nil { + log.Println(err.Error()) + continue + } + _, err = conn.WriteToUDP(replyRaw, addr) + if err != nil { + log.Println(err.Error()) + continue + } + log.Printf("%v.%d %s → \"%s\"\n", addr.IP, addr.Port, query.Questions[0].Type.String(), txt) + } +} + +func httpServer(group *sync.WaitGroup) { + defer group.Done() + log.Println("I'm firing up the HTTP server.") +} diff --git a/docs/wildcard.md b/docs/wildcard.md index 62ca2d9..82e8fd4 100644 --- a/docs/wildcard.md +++ b/docs/wildcard.md @@ -91,8 +91,6 @@ docker run --rm -it \ Clean-up: ``` -gcloud compute firewall-rules delete sslip-io-allow-dns-http-ssh -gcloud compute firewall-rules delete sslip-io-allow-dns-http gcloud compute firewall-rules delete sslip-io-allow-dns -gcloud compute instances delete +gcloud compute instances delete sslip ```