diff --git a/README.md b/README.md index 74ed0cb..09543b7 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ $ go install github.com/Control-D-Inc/ctrld/cmd/ctrld@latest or ``` -$ docker build -t controldns/ctrld . +$ docker build -t controldns/ctrld . -f docker/Dockerfile $ docker run -d --name=ctrld -p 53:53/tcp -p 53:53/udp controldns/ctrld --cd=RESOLVER_ID_GOES_HERE -vv ``` @@ -188,8 +188,8 @@ See [Configuration Docs](docs/config.md). [listener] [listener.0] - ip = "127.0.0.1" - port = 53 + ip = "" + port = 0 restricted = false [network] @@ -220,6 +220,8 @@ See [Configuration Docs](docs/config.md). ``` +`ctrld` will pick a working config for `listener.0` then writing the default config to disk for the first run. + ## Advanced Configuration The above is the most basic example, which will work out of the box. If you're looking to do advanced configurations using policies, see [Configuration Docs](docs/config.md) for complete documentation of the config file. diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index 8f54d1f..1b94902 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -882,6 +882,7 @@ func readConfigFile(writeDefaultConfig bool) bool { if err := v.Unmarshal(&cfg); err != nil { mainLog.Load().Fatal().Msgf("failed to unmarshal default config: %v", err) } + _ = updateListenerConfig() if err := writeConfigFile(); err != nil { mainLog.Load().Fatal().Msgf("failed to write default config file: %v", err) } else { diff --git a/cmd/cli/dns_proxy.go b/cmd/cli/dns_proxy.go index 23ae03e..98ccafc 100644 --- a/cmd/cli/dns_proxy.go +++ b/cmd/cli/dns_proxy.go @@ -5,10 +5,8 @@ import ( "crypto/rand" "encoding/hex" "fmt" - "io" "net" "net/netip" - "os" "runtime" "strconv" "strings" @@ -16,10 +14,8 @@ import ( "time" "github.com/miekg/dns" - "go4.org/mem" "golang.org/x/sync/errgroup" "tailscale.com/net/interfaces" - "tailscale.com/util/lineread" "github.com/Control-D-Inc/ctrld" "github.com/Control-D-Inc/ctrld/internal/dnscache" @@ -121,7 +117,8 @@ func (p *prog) serveDNS(listenerNum string) error { }) } g.Go(func() error { - s, errCh := runDNSServer(dnsListenAddress(listenerConfig), proto, handler) + addr := net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port)) + s, errCh := runDNSServer(addr, proto, handler) defer s.Shutdown() select { case err := <-errCh: @@ -422,17 +419,6 @@ func needLocalIPv6Listener() bool { return ctrldnet.SupportsIPv6ListenLocal() && runtime.GOOS == "windows" } -func dnsListenAddress(lc *ctrld.ListenerConfig) string { - // If we are inside container and the listener loopback address, change - // the address to something like 0.0.0.0:53, so user can expose the port to outside. - if inContainer() { - if ip := net.ParseIP(lc.IP); ip != nil && ip.IsLoopback() { - return net.JoinHostPort("0.0.0.0", strconv.Itoa(lc.Port)) - } - } - return net.JoinHostPort(lc.IP, strconv.Itoa(lc.Port)) -} - func macFromMsg(msg *dns.Msg) string { if opt := msg.IsEdns0(); opt != nil { for _, s := range opt.Option { @@ -498,41 +484,6 @@ func runDNSServer(addr, network string, handler dns.Handler) (*dns.Server, <-cha return s, errCh } -// inContainer reports whether we're running in a container. -// -// Copied from https://github.com/tailscale/tailscale/blob/v1.42.0/hostinfo/hostinfo.go#L260 -// with modification for ctrld usage. -func inContainer() bool { - if runtime.GOOS != "linux" { - return false - } - - var ret bool - if _, err := os.Stat("/.dockerenv"); err == nil { - return true - } - if _, err := os.Stat("/run/.containerenv"); err == nil { - // See https://github.com/cri-o/cri-o/issues/5461 - return true - } - lineread.File("/proc/1/cgroup", func(line []byte) error { - if mem.Contains(mem.B(line), mem.S("/docker/")) || - mem.Contains(mem.B(line), mem.S("/lxc/")) { - ret = true - return io.EOF // arbitrary non-nil error to stop loop - } - return nil - }) - lineread.File("/proc/mounts", func(line []byte) error { - if mem.Contains(mem.B(line), mem.S("lxcfs /proc/cpuinfo fuse.lxcfs")) { - ret = true - return io.EOF - } - return nil - }) - return ret -} - func (p *prog) getClientInfo(ip, mac string) *ctrld.ClientInfo { ci := &ctrld.ClientInfo{} if mac != "" { diff --git a/config.go b/config.go index 3d9e222..50fb76b 100644 --- a/config.go +++ b/config.go @@ -78,8 +78,8 @@ func SetConfigNameWithPath(v *viper.Viper, name, configPath string) { func InitConfig(v *viper.Viper, name string) { v.SetDefault("listener", map[string]*ListenerConfig{ "0": { - IP: "127.0.0.1", - Port: 53, + IP: "", + Port: 0, }, }) v.SetDefault("network", map[string]*NetworkConfig{ diff --git a/Dockerfile b/docker/Dockerfile similarity index 95% rename from Dockerfile rename to docker/Dockerfile index f328790..68d4d6d 100644 --- a/Dockerfile +++ b/docker/Dockerfile @@ -8,7 +8,7 @@ # - Non-cgo ctrld binary. # # CI_COMMIT_TAG is used to set the version of ctrld binary. -FROM golang:bullseye as base +FROM golang:1.20-bullseye as base WORKDIR /app diff --git a/docker/Dockerfile.debug b/docker/Dockerfile.debug new file mode 100644 index 0000000..e7ce172 --- /dev/null +++ b/docker/Dockerfile.debug @@ -0,0 +1,32 @@ +# Using Debian bullseye for building regular image. +# Using scratch image for minimal image size. +# The final image has: +# +# - Timezone info file. +# - CA certs file. +# - /etc/{passwd,group} file. +# - Non-cgo ctrld binary. +# +# CI_COMMIT_TAG is used to set the version of ctrld binary. +FROM golang:1.20-bullseye as base + +WORKDIR /app + +RUN apt-get update && apt-get install -y upx-ucl + +COPY . . + +ARG tag=master +ENV CI_COMMIT_TAG=$tag +RUN CTRLD_NO_QF=yes CGO_ENABLED=0 ./scripts/build.sh + +FROM alpine + +COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=base /etc/passwd /etc/passwd +COPY --from=base /etc/group /etc/group + +COPY --from=base /app/ctrld-linux-*-nocgo ctrld + +ENTRYPOINT ["./ctrld", "run"]