package integration import ( "fmt" "net" "os" "github.com/miekg/dns" "github.com/traefik/traefik/v2/pkg/log" ) type handler struct { traefikIP string } // ServeDNS a fake DNS server // Simplified version of the Challenge Test Server from Boulder // https://github.com/letsencrypt/boulder/blob/a6597b9f120207eff192c3e4107a7e49972a0250/test/challtestsrv/dnsone.go#L40 func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { logger := log.WithoutContext() m := new(dns.Msg) m.SetReply(r) m.Compress = false for _, q := range r.Question { logger.Infof("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype]) switch q.Qtype { case dns.TypeA: record := new(dns.A) record.Hdr = dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0, } record.A = net.ParseIP(s.traefikIP) m.Answer = append(m.Answer, record) case dns.TypeCAA: addCAARecord := true var value string switch q.Name { case "bad-caa-reserved.com.": value = "sad-hacker-ca.invalid" case "good-caa-reserved.com.": value = "happy-hacker-ca.invalid" case "accounturi.good-caa-reserved.com.": uri := os.Getenv("ACCOUNT_URI") value = fmt.Sprintf("happy-hacker-ca.invalid; accounturi=%s", uri) case "recheck.good-caa-reserved.com.": // Allow issuance when we're running in the past // (under FAKECLOCK), otherwise deny issuance. if os.Getenv("FAKECLOCK") != "" { value = "happy-hacker-ca.invalid" } else { value = "sad-hacker-ca.invalid" } case "dns-01-only.good-caa-reserved.com.": value = "happy-hacker-ca.invalid; validationmethods=dns-01" case "http-01-only.good-caa-reserved.com.": value = "happy-hacker-ca.invalid; validationmethods=http-01" case "dns-01-or-http-01.good-caa-reserved.com.": value = "happy-hacker-ca.invalid; validationmethods=dns-01,http-01" default: addCAARecord = false } if addCAARecord { record := new(dns.CAA) record.Hdr = dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0, } record.Tag = "issue" record.Value = value m.Answer = append(m.Answer, record) } } } auth := new(dns.SOA) auth.Hdr = dns.RR_Header{Name: "boulder.invalid.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0} auth.Ns = "ns.boulder.invalid." auth.Mbox = "master.boulder.invalid." auth.Serial = 1 auth.Refresh = 1 auth.Retry = 1 auth.Expire = 1 auth.Minttl = 1 m.Ns = append(m.Ns, auth) if err := w.WriteMsg(m); err != nil { logger.Fatalf("Failed to write message %v", err) } } func startFakeDNSServer(traefikIP string) *dns.Server { srv := &dns.Server{ Addr: ":5053", Net: "udp", Handler: &handler{traefikIP}, } go func() { log.WithoutContext().Infof("Start a fake DNS server.") if err := srv.ListenAndServe(); err != nil { log.WithoutContext().Fatalf("Failed to set udp listener %v", err) } }() return srv }