Skip to content

Commit

Permalink
update recursion code
Browse files Browse the repository at this point in the history
Signed-off-by: Ahmet Alp Balkan <[email protected]>
  • Loading branch information
ahmetb committed Oct 7, 2020
1 parent 7db84bd commit 1e1fc92
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 26 deletions.
2 changes: 1 addition & 1 deletion example/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ RUN apk add --no-cache curl bind-tools
COPY --from=build /app /app

# This is how you integrate with runsd:
ADD https://github.com/ahmetb/runsd/releases/download/v0.0.0-rc.4/runsd /runsd
ADD https://github.com/ahmetb/runsd/releases/download/v0.0.0-rc.5/runsd /runsd
RUN chmod +x /runsd
ENTRYPOINT ["/runsd", "-v=5", "--", "/app"]
24 changes: 24 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package main

import (
"fmt"
"io"
"log"
"net/http"
"os"
Expand All @@ -26,6 +27,8 @@ import (
func main() {
http.HandleFunc("/", home)
http.HandleFunc("/curl", curl)
http.HandleFunc("/dig", dig)
http.HandleFunc("/resolv.conf", resolvconf)
port := "8080"
if v := os.Getenv("PORT"); v != "" {
port = v
Expand Down Expand Up @@ -108,3 +111,24 @@ func curl(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "curl failed: %v\n", err)
}
}

func dig(w http.ResponseWriter, r *http.Request) {
domain := r.URL.Query().Get("domain")
if domain == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "?domain=[...] is missing")
return
}
fmt.Fprintf(w, "$ dig +search A %s\n\n", domain)
cmd := exec.Command("dig", "+search", "A", domain)
cmd.Stdout = w
cmd.Stderr = w
if err := cmd.Run(); err != nil {
fmt.Fprintf(w, "dig failed: %v\n", err)
}
}

func resolvconf(w http.ResponseWriter, r *http.Request) {
f, _ := os.Open("/etc/resolv.conf")
io.Copy(w, f)
}
2 changes: 1 addition & 1 deletion runsd/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func identityToken(audience string) (string, error) {
}

func identityTokenFromMetadata(audience string) (string, error) {
return queryMetadata("http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=" + audience)
return queryMetadata("http://metadata.google.internal./computeMetadata/v1/instance/service-accounts/default/identity?audience=" + audience)
}
46 changes: 30 additions & 16 deletions runsd/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,51 @@ func (d *dnsHijack) handler() dns.Handler {
mux := dns.NewServeMux()
mux.HandleFunc(d.domain, d.handleLocal)
mux.HandleFunc(".", d.recurse)
klog.V(1).Infof("dnsmux=%#v", mux)
return mux
}

func loggingHandler(d dns.HandlerFunc) dns.HandlerFunc {
return func(w dns.ResponseWriter, r *dns.Msg) {
for i, q := range r.Question {
klog.V(5).Infof("[dns] > Q%d: type=%v name=%v", i, dns.TypeToString[q.Qtype], q.Name)
}
d(w, r)
}
}

func (d *dnsHijack) newServer(addr string) *dns.Server {
return &dns.Server{
Addr: addr,
Net: "udp",
Handler: d.handler(),
Handler: loggingHandler(d.handler().ServeDNS),
}
}

func (d *dnsHijack) handleLocal(w dns.ResponseWriter, msg *dns.Msg) {
for _, q := range msg.Question {
dots := strings.Count(q.Name, ".")
klog.V(5).Infof("[dns] type=%v name=%v dots=%d", dns.TypeToString[q.Qtype], q.Name, dots)
if q.Qtype != dns.TypeA && q.Qtype != dns.TypeAAAA {
klog.V(4).Infof("unsupported dns msg type: %s, defer", dns.TypeToString[q.Qtype])
d.recurse(w, msg)
klog.V(4).Infof("[dns] < unsupported dns msg type: %s, defer", dns.TypeToString[q.Qtype])
d.recurse(w, msg) // TODO probably should not do this since original resolver won’t know about local domains
return
}

if dots != d.dots {
klog.V(4).Infof("[dns] type=%v name=%v is too short or long (need ndots=%d; got=%d), nxdomain", dns.TypeToString[q.Qtype], q.Name, d.dots, dots)
klog.V(4).Infof("[dns] < type=%v name=%v is too short or long (need ndots=%d; got=%d), nxdomain", dns.TypeToString[q.Qtype], q.Name, d.dots, dots)
nxdomain(w, msg)
return
}

parts := strings.SplitN(strings.TrimSuffix(q.Name, "."+d.domain+"."), ".", 2)
parts := strings.SplitN(strings.TrimSuffix(q.Name, "."+d.domain), ".", 2)
if len(parts) < 2 {
klog.V(4).Infof("[dns] < name=%q not enough segments to parse", q.Name)
return
}
region := parts[1]
_, ok := cloudRunRegionCodes[region]
if !ok {
klog.V(4).Infof("[dns] unknown region=%q from name=%q, nxdomain", region, q.Name)
klog.V(4).Infof("[dns] < unknown region=%q from name=%q, nxdomain", region, q.Name)
nxdomain(w, msg)
return
}
Expand All @@ -74,7 +87,7 @@ func (d *dnsHijack) handleLocal(w dns.ResponseWriter, msg *dns.Msg) {
r.SetReply(msg)
r.Authoritative = true
for _, q := range msg.Question {
klog.V(5).Infof("[dns] MATCH type=%v name=%v", dns.TypeToString[q.Qtype], q.Name)
klog.V(5).Infof("[dns] < MATCH type=%v name=%v", dns.TypeToString[q.Qtype], q.Name)
switch q.Qtype {
case dns.TypeA:
r.Answer = append(r.Answer, &dns.A{
Expand Down Expand Up @@ -105,20 +118,21 @@ func (d *dnsHijack) handleLocal(w dns.ResponseWriter, msg *dns.Msg) {

// recurse proxies the message to the backend nameserver.
func (d *dnsHijack) recurse(w dns.ResponseWriter, msg *dns.Msg) {
klog.V(5).Infof("[dns] recursing type=%s name=%v", dns.TypeToString[msg.Question[0].Qtype], msg.Question[0].Name)
r, err := dns.Exchange(msg, net.JoinHostPort(d.nameserver, "53"))
klog.V(5).Infof("[dns] >> recursing type=%s name=%v", dns.TypeToString[msg.Question[0].Qtype], msg.Question[0].Name)
r, rtt, err := new(dns.Client).Exchange(msg, net.JoinHostPort(d.nameserver, "53"))
if err != nil {
klog.V(4).Infof("WARNING: recursive dns fail: %v, servfail", err)
klog.V(4).Infof("[dns] << WARNING: recursive dns fail: %v, servfail", err)
servfail(w, msg)
return
}
r.SetReply(msg)
r.RecursionAvailable = true
w.WriteMsg(r)
klog.V(5).Infof("[dns] recursed type=%s name=%v resp_code=%d",
klog.V(5).Infof(r.String())
klog.V(5).Infof("[dns] << recursed type=%s name=%v rcode=%s answers=%d rtt=%v",
dns.TypeToString[msg.Question[0].Qtype],
msg.Question[0].Name,
r.Rcode)
dns.RcodeToString[r.Rcode], len(r.Answer), rtt)

// r.SetReply(msg) // TODO(ahmetb): not sure why but removing this actually preserves the response hdrs and other sections well
w.WriteMsg(r)
}

// nxdomain sends an authoritative NXDOMAIN (domain not found) reply
Expand Down
16 changes: 8 additions & 8 deletions runsd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (

const (
resolvConf = "/etc/resolv.conf"
defaultInternalDomain = "run.internal"
defaultInternalDomain = "run.internal."
defaultNdots = 4
defaultDnsPort = "53"
defaultHTTPProxyPort = "80"
Expand Down Expand Up @@ -62,7 +62,7 @@ func main() {
defer klog.Flush()
flag.StringVar(&flResolvConf, "resolv_conf_file", resolvConf, "[debug-only] path to resolv.conf(5) file to read/write")
flag.StringVar(&flInternalDomain, "domain", defaultInternalDomain, "internal zone (without a trailing dot)")
flag.IntVar(&flNdots, "ndots", defaultNdots, "ndots setting for resolv conf (e.g. for -domain=a.b this should be 4)")
flag.IntVar(&flNdots, "ndots", defaultNdots, "ndots setting for resolv conf (e.g. for -domain=a.b. this should be 4)")
flag.StringVar(&flNameserver, "nameserver", "", "override used nameserver (default: from -resolv_conf_file)")
flag.StringVar(&flRegion, "gcp_region", "", "[debug-only] override GCP region (do not infer from metadata svc)")
flag.BoolVar(&flSkipDNSServer, "skip_dns_hijack", false, "[debug-only] do not start a DNS server for service discovery")
Expand Down Expand Up @@ -101,7 +101,7 @@ func main() {
} else {
klog.Exitf("no nameservers in %s and no nameserver is specified as option", flResolvConf)
}
klog.V(3).Infof("using backend nameserver in process: %s", useNameserver)
klog.V(3).Infof("original nameserver: %s, ndots=%d", useNameserver, flNdots)

// do not hijack dns for this process
net.DefaultResolver = resolver(net.JoinHostPort(useNameserver, "53"))
Expand Down Expand Up @@ -167,11 +167,11 @@ func main() {

klog.V(4).Infof("hijacking resolv.conf file=%s", flResolvConf)
searchDomains := append(cloudRunZones(region, flInternalDomain), rc.Search...)
if err := configureResolvConf(flResolvConf, []string{
ipv4Loopback.String(), // to resolve local domains
net.IPv6loopback.String(), // to resolve local domains
//useNameserver, // TODO: probably not necessary to resolve external domains since we return NS records?
}, searchDomains, flNdots); err != nil {
resolvers := []string{ipv4Loopback.String()}
if ipv6OK {
resolvers = append(resolvers, net.IPv6loopback.String())
}
if err := configureResolvConf(flResolvConf, resolvers, searchDomains, flNdots); err != nil {
klog.Fatal(err)
}
klog.V(1).Info("dns hijack setup complete")
Expand Down

0 comments on commit 1e1fc92

Please sign in to comment.