From bad88961e93bf81668ea3eafe58ff2be64d71342 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 11 Oct 2018 20:52:23 +0300 Subject: [PATCH 1/5] WIP -- single binary -- works, replies to DNS, but need to check what got broken --- config.go | 2 +- control.go | 149 +------------------------------ coredns.go | 127 ++++++++++++++++++++++++++ coredns_plugin/coredns_plugin.go | 5 ++ coredns_plugin/reload.go | 39 ++++++++ openapi.yaml | 27 ------ 6 files changed, 174 insertions(+), 175 deletions(-) create mode 100644 coredns.go create mode 100644 coredns_plugin/reload.go diff --git a/config.go b/config.go index 3a370f4f..9218526b 100644 --- a/config.go +++ b/config.go @@ -166,7 +166,7 @@ func writeAllConfigs() error { return nil } -const coreDNSConfigTemplate = `. { +const coreDNSConfigTemplate = `.:{{.Port}} { {{if .ProtectionEnabled}}dnsfilter {{if .FilteringEnabled}}{{.FilterFile}}{{end}} { {{if .SafeBrowsingEnabled}}safebrowsing{{end}} {{if .ParentalEnabled}}parental {{.ParentalSensitivity}}{{end}} diff --git a/control.go b/control.go index c468379d..d6b1eee2 100644 --- a/control.go +++ b/control.go @@ -9,14 +9,13 @@ import ( "net" "net/http" "os" - "os/exec" "path/filepath" "regexp" "strconv" "strings" - "syscall" "time" + coredns_plugin "github.com/AdguardTeam/AdguardDNS/coredns_plugin" "github.com/AdguardTeam/AdguardDNS/dnsfilter" "github.com/miekg/dns" "gopkg.in/asaskevich/govalidator.v4" @@ -24,8 +23,6 @@ import ( const updatePeriod = time.Minute * 30 -var coreDNSCommand *exec.Cmd - var filterTitle = regexp.MustCompile(`^! Title: +(.*)$`) // cached version.json to avoid hammering github.io for each page reload @@ -43,26 +40,7 @@ var client = &http.Client{ // coredns run control // ------------------- func tellCoreDNSToReload() { - // not running -- cheap check - if coreDNSCommand == nil && coreDNSCommand.Process == nil { - return - } - // not running -- more expensive check - if !isRunning() { - return - } - - pid := coreDNSCommand.Process.Pid - process, err := os.FindProcess(pid) - if err != nil { - log.Printf("os.FindProcess(%d) returned err: %v\n", pid, err) - return - } - err = process.Signal(syscall.SIGUSR1) - if err != nil { - log.Printf("process.Signal on pid %d returned: %v\n", pid, err) - return - } + coredns_plugin.Reload <- true } func writeAllConfigsAndReloadCoreDNS() error { @@ -95,126 +73,6 @@ func returnOK(w http.ResponseWriter, r *http.Request) { } } -func isRunning() bool { - if coreDNSCommand != nil && coreDNSCommand.Process != nil { - pid := coreDNSCommand.Process.Pid - process, err := os.FindProcess(pid) - if err != nil { - log.Printf("os.FindProcess(%d) returned err: %v\n", pid, err) - } else { - err := process.Signal(syscall.Signal(0)) - if err != nil { - log.Printf("process.Signal on pid %d returned: %v\n", pid, err) - } - if err == nil { - return true - } - } - } - return false -} - -func startDNSServer() error { - if isRunning() { - return fmt.Errorf("Unable to start coreDNS: Already running") - } - err := writeCoreDNSConfig() - if err != nil { - errortext := fmt.Errorf("Unable to write coredns config: %s", err) - log.Println(errortext) - return errortext - } - err = writeFilterFile() - if err != nil { - errortext := fmt.Errorf("Couldn't write filter file: %s", err) - log.Println(errortext) - return errortext - } - binarypath := filepath.Join(config.ourBinaryDir, config.CoreDNS.binaryFile) - configpath := filepath.Join(config.ourBinaryDir, config.CoreDNS.coreFile) - coreDNSCommand = exec.Command(binarypath, "-conf", configpath, "-dns.port", fmt.Sprintf("%d", config.CoreDNS.Port)) - coreDNSCommand.Stdout = os.Stdout - coreDNSCommand.Stderr = os.Stderr - err = coreDNSCommand.Start() - if err != nil { - errortext := fmt.Errorf("Unable to start coreDNS: %s", err) - log.Println(errortext) - return errortext - } - log.Printf("coredns PID: %v\n", coreDNSCommand.Process.Pid) - go childwaiter() - return nil -} - -func handleStart(w http.ResponseWriter, r *http.Request) { - if isRunning() { - http.Error(w, fmt.Sprintf("Unable to start coreDNS: Already running"), 400) - return - } - err := startDNSServer() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - _, err = fmt.Fprintf(w, "OK, PID %d\n", coreDNSCommand.Process.Pid) - if err != nil { - log.Printf("Couldn't write body in %s(): %s", _Func(), err) - return - } -} - -func childwaiter() { - err := coreDNSCommand.Wait() - log.Printf("coredns unexpectedly died: %s\n", err) - coreDNSCommand.Process.Release() - log.Printf("restarting coredns") - err = startDNSServer() - if err != nil { - log.Printf("Couldn't restart DNS server: %s\n", err) - return - } -} - -func handleStop(w http.ResponseWriter, r *http.Request) { - if coreDNSCommand == nil || coreDNSCommand.Process == nil { - http.Error(w, fmt.Sprintf("Unable to start coreDNS: Not running"), 400) - return - } - if isRunning() { - http.Error(w, fmt.Sprintf("Unable to start coreDNS: Not running"), 400) - return - } - cmd := coreDNSCommand - // TODO: send SIGTERM first, then SIGKILL - err := cmd.Process.Kill() - if err != nil { - errortext := fmt.Sprintf("Unable to stop coreDNS:\nGot error %T\n%v\n%s", err, err, err) - log.Println(errortext) - http.Error(w, errortext, 500) - return - } - exitstatus := cmd.Wait() - - err = cmd.Process.Release() - if err != nil { - errortext := fmt.Sprintf("Unable to release process resources: %s", err) - log.Println(errortext) - http.Error(w, errortext, 500) - return - } - _, err = fmt.Fprintf(w, "OK\n%s\n", exitstatus) - if err != nil { - log.Printf("Couldn't write body in %s(): %s", _Func(), err) - return - } -} - -func handleRestart(w http.ResponseWriter, r *http.Request) { - handleStop(w, r) - handleStart(w, r) -} - func handleStatus(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ "dns_address": config.BindHost, @@ -1246,9 +1104,6 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { } func registerControlHandlers() { - http.HandleFunc("/control/start", optionalAuth(ensurePOST(handleStart))) - http.HandleFunc("/control/stop", optionalAuth(ensurePOST(handleStop))) - http.HandleFunc("/control/restart", optionalAuth(ensurePOST(handleRestart))) http.HandleFunc("/control/status", optionalAuth(ensureGET(handleStatus))) http.HandleFunc("/control/enable_protection", optionalAuth(ensurePOST(handleProtectionEnable))) http.HandleFunc("/control/disable_protection", optionalAuth(ensurePOST(handleProtectionDisable))) diff --git a/coredns.go b/coredns.go new file mode 100644 index 00000000..2c1f292d --- /dev/null +++ b/coredns.go @@ -0,0 +1,127 @@ +package main + +import ( + "fmt" + "log" + "sync" + + // Include all plugins. + _ "github.com/AdguardTeam/AdguardDNS/coredns_plugin" + _ "github.com/coredns/coredns/plugin/auto" + _ "github.com/coredns/coredns/plugin/autopath" + _ "github.com/coredns/coredns/plugin/bind" + _ "github.com/coredns/coredns/plugin/cache" + _ "github.com/coredns/coredns/plugin/chaos" + _ "github.com/coredns/coredns/plugin/debug" + _ "github.com/coredns/coredns/plugin/dnssec" + _ "github.com/coredns/coredns/plugin/dnstap" + _ "github.com/coredns/coredns/plugin/erratic" + _ "github.com/coredns/coredns/plugin/errors" + _ "github.com/coredns/coredns/plugin/file" + _ "github.com/coredns/coredns/plugin/forward" + _ "github.com/coredns/coredns/plugin/health" + _ "github.com/coredns/coredns/plugin/hosts" + _ "github.com/coredns/coredns/plugin/loadbalance" + _ "github.com/coredns/coredns/plugin/log" + _ "github.com/coredns/coredns/plugin/loop" + _ "github.com/coredns/coredns/plugin/metadata" + _ "github.com/coredns/coredns/plugin/metrics" + _ "github.com/coredns/coredns/plugin/nsid" + _ "github.com/coredns/coredns/plugin/pprof" + _ "github.com/coredns/coredns/plugin/proxy" + _ "github.com/coredns/coredns/plugin/reload" + _ "github.com/coredns/coredns/plugin/rewrite" + _ "github.com/coredns/coredns/plugin/root" + _ "github.com/coredns/coredns/plugin/secondary" + _ "github.com/coredns/coredns/plugin/template" + _ "github.com/coredns/coredns/plugin/tls" + _ "github.com/coredns/coredns/plugin/whoami" + _ "github.com/mholt/caddy/onevent" + + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/coremain" +) + +// Directives are registered in the order they should be +// executed. +// +// Ordering is VERY important. Every plugin will +// feel the effects of all other plugin below +// (after) them during a request, but they must not +// care what plugin above them are doing. + +var directives = []string{ + "metadata", + "tls", + "reload", + "nsid", + "root", + "bind", + "debug", + "health", + "pprof", + "prometheus", + "errors", + "log", + "dnsfilter", + "dnstap", + "chaos", + "loadbalance", + "cache", + "rewrite", + "dnssec", + "autopath", + "template", + "hosts", + "file", + "auto", + "secondary", + "loop", + "forward", + "proxy", + "erratic", + "whoami", + "on", +} + +func init() { + dnsserver.Directives = directives +} + +var ( + isCoreDNSRunningLock sync.Mutex + isCoreDNSRunning = false +) + +func isRunning() bool { + isCoreDNSRunningLock.Lock() + value := isCoreDNSRunning + isCoreDNSRunningLock.Unlock() + return value +} + +func startDNSServer() error { + isCoreDNSRunningLock.Lock() + if isCoreDNSRunning { + isCoreDNSRunningLock.Unlock() + return fmt.Errorf("Unable to start coreDNS: Already running") + } + isCoreDNSRunning = true + isCoreDNSRunningLock.Unlock() + + err := writeCoreDNSConfig() + if err != nil { + errortext := fmt.Errorf("Unable to write coredns config: %s", err) + log.Println(errortext) + return errortext + } + err = writeFilterFile() + if err != nil { + errortext := fmt.Errorf("Couldn't write filter file: %s", err) + log.Println(errortext) + return errortext + } + + go coremain.Run() + return nil +} diff --git a/coredns_plugin/coredns_plugin.go b/coredns_plugin/coredns_plugin.go index cb372a22..225a9a78 100644 --- a/coredns_plugin/coredns_plugin.go +++ b/coredns_plugin/coredns_plugin.go @@ -185,6 +185,10 @@ func setupPlugin(c *caddy.Controller) (*plug, error) { }) } + onceHook.Do(func() { + caddy.RegisterEventHook("dnsfilter-reload", hook) + }) + p.upstream, err = upstream.New(nil) if err != nil { return nil, err @@ -573,4 +577,5 @@ func (p *plug) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( // Name returns name of the plugin as seen in Corefile and plugin.cfg func (p *plug) Name() string { return "dnsfilter" } +var onceHook sync.Once var onceQueryLog sync.Once diff --git a/coredns_plugin/reload.go b/coredns_plugin/reload.go new file mode 100644 index 00000000..823ee594 --- /dev/null +++ b/coredns_plugin/reload.go @@ -0,0 +1,39 @@ +package dnsfilter + +import ( + "log" + + "github.com/mholt/caddy" +) + +var Reload = make(chan bool) + +func hook(event caddy.EventName, info interface{}) error { + if event != caddy.InstanceStartupEvent { + return nil + } + + // this should be an instance. ok to panic if not + instance := info.(*caddy.Instance) + + go func() { + trace("Will wait for Reload channel") + + for range Reload { + trace("Got message on Reload, restarting coredns") + corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType()) + if err != nil { + continue + } + _, err = instance.Restart(corefile) + if err != nil { + log.Printf("Corefile changed but reload failed: %s", err) + continue + } + // hook will be called again from new instance + return + } + }() + + return nil +} diff --git a/openapi.yaml b/openapi.yaml index e20d59d3..0b11f3fe 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -25,33 +25,6 @@ tags: name: safesearch description: 'Enforce family-friendly results in search engines' paths: - /start: - post: - tags: - - global - operationId: start - summary: 'Start DNS server' - responses: - 200: - description: OK - /stop: - post: - tags: - - global - operationId: stop - summary: 'Stop DNS server' - responses: - 200: - description: OK - /restart: - post: - tags: - - global - operationId: restart - summary: 'Restart DNS server' - responses: - 200: - description: OK /status: get: tags: From 7ff89baf45c4fbaa9fd25f8fe88bd842ec9dfc75 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 12 Oct 2018 03:54:22 +0300 Subject: [PATCH 2/5] Update makefile to build only one binary instead of two --- Makefile | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 1c335fd2..e8820bae 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ STATIC = build/static/index.html .PHONY: all build clean all: build -build: AdguardDNS coredns +build: AdguardDNS client/node_modules: client/package.json client/package-lock.json npm --prefix client install @@ -19,25 +19,16 @@ client/node_modules: client/package.json client/package-lock.json $(STATIC): $(JSFILES) client/node_modules npm --prefix client run build-prod -AdguardDNS: $(STATIC) *.go +AdguardDNS: $(STATIC) *.go coredns_plugin/*.go dnsfilter/*.go mkdir -p $(GOPATH)/src/github.com/AdguardTeam if [ ! -h $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS ]; then rm -rf $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS && ln -fs ../../../../.. $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS; fi GOPATH=$(GOPATH) go get -v -d . GOPATH=$(GOPATH) GOOS=$(NATIVE_GOOS) GOARCH=$(NATIVE_GOARCH) go get -v github.com/gobuffalo/packr/... mkdir -p $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS/build/static ## work around packr bug - GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) packr build -ldflags="-X main.VersionString=$(GIT_VERSION)" -o AdguardDNS - -coredns: coredns_plugin/*.go dnsfilter/*.go - GOPATH=$(GOPATH) go get -v -d github.com/coredns/coredns - cd $(GOPATH)/src/github.com/prometheus/client_golang && git checkout -q v0.8.0 - cd $(GOPATH)/src/github.com/coredns/coredns && perl -p -i.bak -e 's/^(trace|route53|federation|kubernetes|etcd):.*//' plugin.cfg - cd $(GOPATH)/src/github.com/coredns/coredns && grep -q '^dnsfilter:' plugin.cfg || perl -p -i.bak -e 's|^log:log|log:log\ndnsfilter:github.com/AdguardTeam/AdguardDNS/coredns_plugin|' plugin.cfg - grep '^dnsfilter:' $(GOPATH)/src/github.com/coredns/coredns/plugin.cfg ## used to check that plugin.cfg was successfully edited by sed + cd $(GOPATH)/src/github.com/prometheus/client_golang && git reset --hard v0.8.0 perl -0777 -p -i.bak -e 's/pprofOnce.Do\(func\(\) {(.*)}\)/\1/ms' $(GOPATH)/src/github.com/coredns/coredns/plugin/pprof/setup.go perl -0777 -p -i.bak -e 's/c.OnShutdown/c.OnRestart/' $(GOPATH)/src/github.com/coredns/coredns/plugin/pprof/setup.go - cd $(GOPATH)/src/github.com/coredns/coredns && GOPATH=$(GOPATH) GOOS=$(NATIVE_GOOS) GOARCH=$(NATIVE_GOARCH) go generate - cd $(GOPATH)/src/github.com/coredns/coredns && GOPATH=$(GOPATH) go get -v -d . - cd $(GOPATH)/src/github.com/coredns/coredns && GOPATH=$(GOPATH) go build -o $(mkfile_dir)/coredns + GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) packr build -ldflags="-X main.VersionString=$(GIT_VERSION)" -o AdguardDNS clean: $(MAKE) cleanfast @@ -45,4 +36,4 @@ clean: rm -rf client/node_modules cleanfast: - rm -f coredns AdguardDNS + rm -f AdguardDNS From ac6e0add317ff6baeccc102e1159859bea364e78 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 12 Oct 2018 04:05:21 +0300 Subject: [PATCH 3/5] single binary -- coredns also tries to parse arguments, it kills itself on unknown flags --- app.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app.go b/app.go index 608f1590..c3ae4b07 100644 --- a/app.go +++ b/app.go @@ -109,6 +109,11 @@ func main() { } } + // eat all args so that coredns can start happily + if len(os.Args) > 1 { + os.Args = os.Args[:1] + } + err := writeConfig() if err != nil { log.Fatal(err) From 5192e95a0d177d6b918578fcfcfc12d9e9d4b967 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 12 Oct 2018 04:05:39 +0300 Subject: [PATCH 4/5] coredns plugin -- remove debug logging --- coredns_plugin/reload.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/coredns_plugin/reload.go b/coredns_plugin/reload.go index 823ee594..880a3acc 100644 --- a/coredns_plugin/reload.go +++ b/coredns_plugin/reload.go @@ -17,10 +17,7 @@ func hook(event caddy.EventName, info interface{}) error { instance := info.(*caddy.Instance) go func() { - trace("Will wait for Reload channel") - for range Reload { - trace("Got message on Reload, restarting coredns") corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType()) if err != nil { continue From 880ad362a849cc53729128b614a4d4cc1d696750 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 12 Oct 2018 16:43:17 +0300 Subject: [PATCH 5/5] Makefile -- use variable for target binary --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index e8820bae..d2cb0b11 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,12 @@ GOPATH := $(mkfile_dir)/build/gopath JSFILES = $(shell find client -path client/node_modules -prune -o -type f -name '*.js') STATIC = build/static/index.html +TARGET=AdguardDNS + .PHONY: all build clean all: build -build: AdguardDNS +build: $(TARGET) client/node_modules: client/package.json client/package-lock.json npm --prefix client install @@ -19,7 +21,7 @@ client/node_modules: client/package.json client/package-lock.json $(STATIC): $(JSFILES) client/node_modules npm --prefix client run build-prod -AdguardDNS: $(STATIC) *.go coredns_plugin/*.go dnsfilter/*.go +$(TARGET): $(STATIC) *.go coredns_plugin/*.go dnsfilter/*.go mkdir -p $(GOPATH)/src/github.com/AdguardTeam if [ ! -h $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS ]; then rm -rf $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS && ln -fs ../../../../.. $(GOPATH)/src/github.com/AdguardTeam/AdguardDNS; fi GOPATH=$(GOPATH) go get -v -d . @@ -28,7 +30,7 @@ AdguardDNS: $(STATIC) *.go coredns_plugin/*.go dnsfilter/*.go cd $(GOPATH)/src/github.com/prometheus/client_golang && git reset --hard v0.8.0 perl -0777 -p -i.bak -e 's/pprofOnce.Do\(func\(\) {(.*)}\)/\1/ms' $(GOPATH)/src/github.com/coredns/coredns/plugin/pprof/setup.go perl -0777 -p -i.bak -e 's/c.OnShutdown/c.OnRestart/' $(GOPATH)/src/github.com/coredns/coredns/plugin/pprof/setup.go - GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) packr build -ldflags="-X main.VersionString=$(GIT_VERSION)" -o AdguardDNS + GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) packr build -ldflags="-X main.VersionString=$(GIT_VERSION)" -o $(TARGET) clean: $(MAKE) cleanfast @@ -36,4 +38,4 @@ clean: rm -rf client/node_modules cleanfast: - rm -f AdguardDNS + rm -f $(TARGET)