Skip to content

Commit

Permalink
Update config to be ordered JSON instead of json5 (canonically)
Browse files Browse the repository at this point in the history
This includes a fallback so that existing configs do not break.

This also refactors a lot of data handling to be more sane (less shared objects).
  • Loading branch information
tianon committed Mar 9, 2024
1 parent cf22bdc commit 2dc31ac
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 44 deletions.
100 changes: 56 additions & 44 deletions cmd/rawdns/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package main
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"os/signal"
"runtime"
"slices"
"strings"

"github.com/docker-library/meta-scripts/om"
"github.com/miekg/dns"
"github.com/titanous/json5"
)

type Config map[string]DomainConfig // { "docker.": { ... }, ".": { ... } }
// config like { "docker.": { ... }, ".": { ... } }

type DomainConfig struct {
Type string `json:"type"` // "containers", "forwarding", "static"
Expand All @@ -29,6 +32,8 @@ type DomainConfig struct {
TLSCACert string `json:"tlscacert"`
TLSCert string `json:"tlscert"`
TLSKey string `json:"tlskey"`
// parsed/loaded
tlsConfig *tls.Config

// IP address strategy
SwarmNode bool `json:"swarmnode"`
Expand Down Expand Up @@ -57,8 +62,6 @@ type DomainConfigSrv struct {
Target string `json:"target"`
}

var config Config

func main() {
log.Printf("rawdns v%s (%s on %s/%s; %s)\n", VERSION, runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.Compiler)

Expand All @@ -71,76 +74,85 @@ func main() {
if err != nil {
log.Fatalf("error: unable to read config file %s: %v\n", configFile, err)
}
err = json5.Unmarshal(configData, &config)
if err != nil {
log.Fatalf("error: unable to process config file data from %s: %v\n", configFile, err)

config := om.OrderedMap[DomainConfig]{}
if err := json.Unmarshal(configData, &config); err != nil {
// if it isn't stock JSON, it might be json5; we'll try that (for backwards compatibility)
var configMap map[string]DomainConfig
if err5 := json5.Unmarshal(configData, &configMap); err5 != nil {
log.Fatalf("error: unable to process config file data from %s: %v\n", configFile, err)
}
domains := make([]string, 0, len(configMap))
for domain := range configMap {
domains = append(domains, domain)
}
slices.Sort(domains)
for _, domain := range domains {
config.Set(domain, configMap[domain])
}
}

for domain := range config {
if config[domain].Randomize == nil {
domainConfig := config[domain]
for _, domain := range config.Keys() {
domain := domain // https://github.com/golang/go/discussions/56010
domainConfig := config.Get(domain)

if domainConfig.Randomize == nil {
randomize := true
domainConfig.Randomize = &randomize
config[domain] = domainConfig
}

switch config[domain].Type {
switch domainConfig.Type {
case "containers":
// TODO there must be a better way to pass "domain" along without an anonymous function AND copied variable
var tlsConfig *tls.Config
if config[domain].TLSCert != "" && config[domain].TLSKey != "" {
if domainConfig.TLSCert != "" && domainConfig.TLSKey != "" {
var err error
tlsConfig, err = loadTLSConfig(config[domain].TLSCACert, config[domain].TLSCert, config[domain].TLSKey, config[domain].TLSVerify)
domainConfig.tlsConfig, err = loadTLSConfig(domainConfig.TLSCACert, domainConfig.TLSCert, domainConfig.TLSKey, domainConfig.TLSVerify)
if err != nil {
log.Fatalf("error: Unable to load tls config for %s: %s\n", domain, err)
}
}

dCopy := domain
dns.HandleFunc(dCopy, func(w dns.ResponseWriter, r *dns.Msg) {
handleDockerRequest(dCopy, tlsConfig, w, r)
dns.HandleFunc(domain, func(w dns.ResponseWriter, r *dns.Msg) {
handleDockerRequest(domain, domainConfig, w, r)
})

case "forwarding":
// TODO there must be a better way to pass "domain" along without an anonymous function AND copied variable
nameservers := config[domain].Nameservers
randomize := *config[domain].Randomize
dns.HandleFunc(domain, func(w dns.ResponseWriter, r *dns.Msg) {
handleForwarding(nameservers, randomize, w, r)
handleForwarding(domainConfig.Nameservers, *domainConfig.Randomize, w, r)
})
case "static":
cCopy := config[domain]

cCopy.addrs = make([]net.IP, len(cCopy.Addrs))
for i, addr := range cCopy.Addrs {
cCopy.addrs[i] = net.ParseIP(addr)
case "static":
domainConfig.addrs = make([]net.IP, len(domainConfig.Addrs))
for i, addr := range domainConfig.Addrs {
domainConfig.addrs[i] = net.ParseIP(addr)
}

cCopy.cnames = make([]string, len(cCopy.Cnames))
for i, cname := range cCopy.Cnames {
cCopy.cnames[i] = dns.Fqdn(cname)
domainConfig.cnames = make([]string, len(domainConfig.Cnames))
for i, cname := range domainConfig.Cnames {
domainConfig.cnames[i] = dns.Fqdn(cname)
}

cCopy.ptrs = make([]string, len(cCopy.Ptrs))
for i, ptr := range cCopy.Ptrs {
cCopy.ptrs[i] = dns.Fqdn(ptr)
domainConfig.ptrs = make([]string, len(domainConfig.Ptrs))
for i, ptr := range domainConfig.Ptrs {
domainConfig.ptrs[i] = dns.Fqdn(ptr)
}

cCopy.txts = make([][]string, len(cCopy.Txts))
for i, txts := range cCopy.Txts {
cCopy.txts[i] = make([]string, len(txts))
domainConfig.txts = make([][]string, len(domainConfig.Txts))
for i, txts := range domainConfig.Txts {
domainConfig.txts[i] = make([]string, len(txts))
for j, txt := range txts {
cCopy.txts[i][j] = strings.Replace(txt, `\`, `\\`, -1)
domainConfig.txts[i][j] = strings.Replace(txt, `\`, `\\`, -1)
}
}

dns.HandleFunc(domain, func(w dns.ResponseWriter, r *dns.Msg) {
handleStaticRequest(cCopy, w, r)
handleStaticRequest(domainConfig, w, r)
})

default:
log.Printf("error: unknown domain type on %s: %q\n", domain, config[domain].Type)
continue
log.Fatalf("error: unknown domain type on %s: %q\n", domain, domainConfig.Type)
}
log.Printf("listening on domain: %s\n", domain)

log.Printf("listening on domain [% -10s]: %s\n", domainConfig.Type, domain)
}

go serve("tcp", ":53")
Expand Down Expand Up @@ -197,7 +209,7 @@ func dnsAppend(q dns.Question, m *dns.Msg, rr dns.RR) {
}
}

func handleDockerRequest(domain string, tlsConfig *tls.Config, w dns.ResponseWriter, r *dns.Msg) {
func handleDockerRequest(domain string, domainConfig DomainConfig, w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
defer w.WriteMsg(m)
Expand All @@ -211,15 +223,15 @@ func handleDockerRequest(domain string, tlsConfig *tls.Config, w dns.ResponseWri
}
containerName := name[:len(name)-len(domainSuffix)]

ips, err := dockerGetIpList(config[domain].Socket, containerName, tlsConfig, config[domain].SwarmNode)
ips, err := dockerGetIpList(domainConfig.Socket, containerName, domainConfig.tlsConfig, domainConfig.SwarmNode)
if err != nil && strings.Contains(containerName, ".") {
// we have something like "db.app", so let's try looking up a "app/db" container (linking!)
parts := strings.Split(containerName, ".")
var linkedContainerName string
for i := range parts {
linkedContainerName += "/" + parts[len(parts)-i-1]
}
ips, err = dockerGetIpList(config[domain].Socket, linkedContainerName, tlsConfig, config[domain].SwarmNode)
ips, err = dockerGetIpList(domainConfig.Socket, linkedContainerName, domainConfig.tlsConfig, domainConfig.SwarmNode)
}
if err != nil {
m.SetRcode(r, dns.RcodeNameError)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/tianon/rawdns
go 1.21

require (
github.com/docker-library/meta-scripts v0.0.0-20240308032004-96ed6a9b2bad
github.com/miekg/dns v1.1.58
github.com/titanous/json5 v1.0.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/docker-library/meta-scripts v0.0.0-20240308032004-96ed6a9b2bad h1:47H9uVRqdgq6lLd2XMVRg+K3KuYKmGF6jkzDVlPLZy8=
github.com/docker-library/meta-scripts v0.0.0-20240308032004-96ed6a9b2bad/go.mod h1:Lxb/5IT8JwRt7wtvC5uRWDgiIyBKxl7J4Y4OytutTlE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
Expand Down

0 comments on commit 2dc31ac

Please sign in to comment.