Flag -delegates for delegated domains

Meant for obtaining wildcard certs from Let's Encrypt using the DNS-01
challenge.

- introduce a variant of `blocklist.txt` to be used for testing
  (`blocklist-test.txt`) because the blocklist has grown so large it
  clutters the test output
- more rigorous about lowercasing hostnames when matching against
  customized records. This needs to be extendend when we parse _any_
  arguments

TODOs:

- remove the wildcard DNS servers
- update instructions
This commit is contained in:
Brian Cunnie
2024-06-08 16:13:29 -07:00
parent d52d97f478
commit 8a08e49034
7 changed files with 220 additions and 22 deletions

10
etc/blocklist-test.txt Normal file
View File

@@ -0,0 +1,10 @@
# TESTING ONLY: List of "Forbidden" (blocked) names & CIDRs
# This is a shortened variant meant to be used for testing (`ginkgo`) because
# the legitimate one has grown so long it clutters the test output
raiffeisen # https://www.rbinternational.com/en/homepage.html
43-134-66-67 # Netflix, https://nf-43-134-66-67.sslip.io/sg
43.134.66.67/24 # Netflix
2601:646:100:69f7:cafe:bebe:cafe:bebe/112 # personal (Comcast) IPv6 range for testing blocklist

View File

@@ -18,7 +18,7 @@ var _ = Describe("flags", func() {
var flags []string var flags []string
JustBeforeEach(func() { JustBeforeEach(func() {
flags = append(flags, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist.txt") flags = append(flags, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist-test.txt")
serverCmd = exec.Command(serverPath, flags...) serverCmd = exec.Command(serverPath, flags...)
serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter) serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
@@ -160,4 +160,77 @@ var _ = Describe("flags", func() {
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`fc00--\.sslip\.io\. \? fc00::`)) Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`fc00--\.sslip\.io\. \? fc00::`))
}) })
}) })
When("-delegates is set", func() {
BeforeEach(func() {
flags = []string{"-delegates=" +
"_acme-challenge.127-0-0-1.IP.io=ns.nono.io," +
"2600--.IP.IO=ns-1.nono.com," +
"_acme-challenge.73-189-219-4.ip.IO=ns-2.nono.com," +
"a.b.C=d.E.f"}
})
When("the arguments are missing", func() {
BeforeEach(func() {
flags = []string{"-delegates="}
})
It("should give an informative message", func() {
Expect(string(serverSession.Err.Contents())).Should(MatchRegexp(`-delegates: arguments should be in the format "delegatedDomain=nameserver", not ""`))
})
})
When("the arguments are mangled", func() {
BeforeEach(func() {
flags = []string{"-delegates=blahblah"}
})
It("should give an informative message", func() {
Expect(string(serverSession.Err.Contents())).Should(MatchRegexp(`-delegates: arguments should be in the format "delegatedDomain=nameserver", not "blahblah"`))
})
})
When("only some of the arguments are mangled", func() {
BeforeEach(func() {
flags = []string{"-delegates=a.b=c.d,blahblah"}
})
It("adds the correct ones, gives an informative message for the mangled ones", func() {
Expect(string(serverSession.Err.Contents())).Should(MatchRegexp(`Adding delegated NS record "a.b.=c.d."`))
Expect(string(serverSession.Err.Contents())).Should(MatchRegexp(`-delegates: arguments should be in the format "delegatedDomain=nameserver", not "blahblah"`))
})
})
When("looking up a delegated domain", func() {
It("should return a non-authoritative NS record pointing to the nameserver", func() {
digArgs := "@localhost _acme-challenge.127-0-0-1.IP.io -p " + strconv.Itoa(port)
digCmd := exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err := Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred())
Eventually(digSession).Should(Say(`flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0`))
Eventually(digSession).Should(Say(`;; AUTHORITY SECTION:`))
Eventually(digSession).Should(Say(`_acme-challenge.127-0-0-1.IP.io. 604800 IN NS ns.nono.io.\n`))
Eventually(digSession, 1).Should(Exit(0))
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`_acme-challenge\.127-0-0-1\.IP\.io\. \? nil, NS ns\.nono\.io\.`))
})
})
When("looking up the subdomain of a delegated domain", func() {
It("should return a non-authoritative NS record pointing to the nameserver", func() {
digArgs := "@localhost subdomain.2600--.IP.IO -p " + strconv.Itoa(port)
digCmd := exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err := Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred())
Eventually(digSession).Should(Say(`flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0`))
Eventually(digSession).Should(Say(`;; AUTHORITY SECTION:`))
Eventually(digSession).Should(Say(`subdomain.2600--.IP.IO. 604800 IN NS ns-1.nono.com.\n`))
Eventually(digSession, 1).Should(Exit(0))
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`subdomain\.2600--\.IP\.IO\. \? nil, NS ns-1\.nono\.com\.`))
})
})
When("looking up a delegated domain that wouldn't have resolved to an IP address", func() {
It("it delegates", func() {
digArgs := "@localhost a.b.c -p " + strconv.Itoa(port)
digCmd := exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err := Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred())
Eventually(digSession).Should(Say(`flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0`))
Eventually(digSession).Should(Say(`;; AUTHORITY SECTION:`))
Eventually(digSession).Should(Say(`a.b.c. 604800 IN NS d.e.f.`))
Eventually(digSession, 1).Should(Exit(0))
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`a\.b\.c\. \? nil, NS d\.e\.f\.`))
})
})
})
}) })

View File

@@ -20,7 +20,7 @@ var _ = Describe("speed", func() {
var flags []string var flags []string
JustBeforeEach(func() { JustBeforeEach(func() {
flags = append(flags, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist.txt") flags = append(flags, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist-test.txt")
serverCmd = exec.Command(serverPath, flags...) serverCmd = exec.Command(serverPath, flags...)
serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter) serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())

View File

@@ -26,7 +26,7 @@ var serverPath, _ = Build("main.go")
var _ = BeforeSuite(func() { var _ = BeforeSuite(func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
serverCmd = exec.Command(serverPath, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist.txt") serverCmd = exec.Command(serverPath, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist-test.txt")
serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter) serverSession, err = Start(serverCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
// takes 0.455s to start up on macOS Big Sur 3.7 GHz Quad Core 22-nm Xeon E5-1620v2 processor (2013 Mac Pro) // takes 0.455s to start up on macOS Big Sur 3.7 GHz Quad Core 22-nm Xeon E5-1620v2 processor (2013 Mac Pro)
@@ -414,7 +414,7 @@ var _ = Describe("sslip.io-dns-server", func() {
When("it can't bind to any UDP port", func() { When("it can't bind to any UDP port", func() {
It("prints an error message and exits", func() { It("prints an error message and exits", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist.txt") secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(port), "-blocklistURL", "file://etc/blocklist-test.txt")
secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter) secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(secondServerSession.Err, 10).Should(Say("I couldn't bind via UDP to any IPs")) Eventually(secondServerSession.Err, 10).Should(Say("I couldn't bind via UDP to any IPs"))
@@ -436,7 +436,7 @@ var _ = Describe("sslip.io-dns-server", func() {
}) })
It("prints an error message and continues running", func() { It("prints an error message and continues running", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist.txt") secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist-test.txt")
secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter) secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`)) Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`))
@@ -456,7 +456,7 @@ var _ = Describe("sslip.io-dns-server", func() {
}) })
It("prints an informative message and binds to the addresses it can", func() { It("prints an informative message and binds to the addresses it can", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist.txt") secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist-test.txt")
secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter) secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`)) Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`))
@@ -479,7 +479,7 @@ var _ = Describe("sslip.io-dns-server", func() {
}) })
It("prints an informative message and binds to the addresses it can", func() { It("prints an informative message and binds to the addresses it can", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist.txt") secondServerCmd := exec.Command(serverPath, "-port", strconv.Itoa(newPort), "-blocklistURL", "file://etc/blocklist-test.txt")
secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter) secondServerSession, err := Start(secondServerCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`)) Eventually(secondServerSession.Err, 10).Should(Say(` version \d+\.\d+\.\d+ starting`))

View File

@@ -33,6 +33,10 @@ func main() {
"ns-gce.sslip.io=104.155.144.4,"+ "ns-gce.sslip.io=104.155.144.4,"+
"ns-gce.sslip.io=2600:1900:4000:4d12::", "ns-gce.sslip.io=2600:1900:4000:4d12::",
"comma-separated list of hosts and corresponding IPv4 and/or IPv6 address(es). If you're running your own sslip.io nameservers, add their hostnames and addresses here. If unsure, add to the list rather than replace") "comma-separated list of hosts and corresponding IPv4 and/or IPv6 address(es). If you're running your own sslip.io nameservers, add their hostnames and addresses here. If unsure, add to the list rather than replace")
var delegates = flag.String("delegates", "", "comma-separated list of domains you own "+
"and nameservers you control to which to delegate, often used to acquire wildcard certificates from "+
"Let's Encrypt via DNS challenge. Example: "+
`-delegates=_acme-challenge.73-189-219-4.xip.nono.io=ns-437.awsdns-54.com.,_acme-challenge.73-189-219-4.xip.nono.io=ns-1097.awsdns-09.org."`)
var bindPort = flag.Int("port", 53, "port the DNS server should bind to") var bindPort = flag.Int("port", 53, "port the DNS server should bind to")
var quiet = flag.Bool("quiet", false, "suppresses logging of each DNS response. Use this to avoid Google Cloud charging you $30/month to retain the logs of your GKE-based sslip.io server") var quiet = flag.Bool("quiet", false, "suppresses logging of each DNS response. Use this to avoid Google Cloud charging you $30/month to retain the logs of your GKE-based sslip.io server")
var public = flag.Bool("public", true, "allows resolution of public IP addresses. If false, only resolves private IPs including localhost (127/8, ::1), link-local (169.254/16, fe80::/10), CG-NAT (100.64/12), private (10/8, 172.16/12, 192.168/16, fc/7). Set to false if you don't want miscreants impersonating you via public IPs. If unsure, set to false") var public = flag.Bool("public", true, "allows resolution of public IP addresses. If false, only resolves private IPs including localhost (127/8, ::1), link-local (169.254/16, fe80::/10), CG-NAT (100.64/12), private (10/8, 172.16/12, 192.168/16, fc/7). Set to false if you don't want miscreants impersonating you via public IPs. If unsure, set to false")
@@ -41,7 +45,7 @@ func main() {
log.Printf("blocklist URL: %s, name servers: %s, bind port: %d, quiet: %t", log.Printf("blocklist URL: %s, name servers: %s, bind port: %d, quiet: %t",
*blocklistURL, *nameservers, *bindPort, *quiet) *blocklistURL, *nameservers, *bindPort, *quiet)
x, logmessages := xip.NewXip(*blocklistURL, strings.Split(*nameservers, ","), strings.Split(*addresses, ",")) x, logmessages := xip.NewXip(*blocklistURL, strings.Split(*nameservers, ","), strings.Split(*addresses, ","), strings.Split(*delegates, ","))
x.Public = *public x.Public = *public
for _, logmessage := range logmessages { for _, logmessage := range logmessages {
log.Println(logmessage) log.Println(logmessage)

View File

@@ -64,6 +64,7 @@ type DomainCustomization struct {
AAAA []dnsmessage.AAAAResource AAAA []dnsmessage.AAAAResource
CNAME dnsmessage.CNAMEResource CNAME dnsmessage.CNAMEResource
MX []dnsmessage.MXResource MX []dnsmessage.MXResource
NS []dnsmessage.NSResource
TXT func(*Xip, net.IP) ([]dnsmessage.TXTResource, error) TXT func(*Xip, net.IP) ([]dnsmessage.TXTResource, error)
// Unlike the other record types, TXT is a function in order to enable more complex behavior // Unlike the other record types, TXT is a function in order to enable more complex behavior
// e.g. IP address of the query's source // e.g. IP address of the query's source
@@ -167,7 +168,7 @@ type Response struct {
} }
// NewXip follows convention for constructors: https://go.dev/doc/effective_go#allocation_new // NewXip follows convention for constructors: https://go.dev/doc/effective_go#allocation_new
func NewXip(blocklistURL string, nameservers []string, addresses []string) (x *Xip, logmessages []string) { func NewXip(blocklistURL string, nameservers []string, addresses []string, delegates []string) (x *Xip, logmessages []string) {
x = &Xip{Metrics: Metrics{Start: time.Now()}} x = &Xip{Metrics: Metrics{Start: time.Now()}}
// Download the blocklist // Download the blocklist
@@ -213,7 +214,7 @@ func NewXip(blocklistURL string, nameservers []string, addresses []string) (x *X
if host[len(host)-1] != '.' { if host[len(host)-1] != '.' {
host += "." host += "."
} }
if ip == nil { // bad IP address if ip == nil { // bad IP delegate
logmessages = append(logmessages, fmt.Sprintf(`-addresses: "%s" is not assigned a valid IP "%s"`, hostAddr, ip.String())) logmessages = append(logmessages, fmt.Sprintf(`-addresses: "%s" is not assigned a valid IP "%s"`, hostAddr, ip.String()))
continue continue
} }
@@ -247,6 +248,38 @@ func NewXip(blocklistURL string, nameservers []string, addresses []string) (x *X
// print out the added records in a manner similar to the way they're set on the cmdline // print out the added records in a manner similar to the way they're set on the cmdline
logmessages = append(logmessages, fmt.Sprintf(`Adding record "%s=%s"`, host, ip)) logmessages = append(logmessages, fmt.Sprintf(`Adding record "%s=%s"`, host, ip))
} }
// Parse and set the nameservers of our delegated domains
for _, delegate := range delegates {
delegatedDomainAndNameserver := strings.Split(strings.ToLower(delegate), "=")
if len(delegatedDomainAndNameserver) != 2 {
logmessages = append(logmessages, fmt.Sprintf(`-delegates: arguments should be in the format "delegatedDomain=nameserver", not "%s"`, delegate))
continue
}
delegatedDomain := delegatedDomainAndNameserver[0]
nameServer := delegatedDomainAndNameserver[1]
// all domains & nameservers must be absolute (end in ".")
if delegatedDomain[len(delegatedDomain)-1] != '.' {
delegatedDomain += "."
}
if nameServer[len(nameServer)-1] != '.' {
nameServer += "."
}
// nameservers must be DNS-compliant
nsName, err := dnsmessage.NewName(nameServer)
if err != nil {
logmessages = append(logmessages, fmt.Sprintf(`-nameservers: ignoring invalid nameserver "%s"`, nameServer))
continue
}
var domainEntry = DomainCustomization{}
if _, ok := Customizations[delegatedDomain]; ok {
domainEntry = Customizations[delegatedDomain]
}
domainEntry.NS = append(domainEntry.NS, dnsmessage.NSResource{NS: nsName})
Customizations[delegatedDomain] = domainEntry
// print out the added records in a manner similar to the way they're set on the cmdline
logmessages = append(logmessages, fmt.Sprintf(`Adding delegated NS record "%s=%s"`, delegatedDomain, nsName.String()))
}
// We want to make sure that our DNS server isn't used in a DNS amplification attack. // We want to make sure that our DNS server isn't used in a DNS amplification attack.
// The endpoint we're worried about is metrics.status.sslip.io, whose reply is // The endpoint we're worried about is metrics.status.sslip.io, whose reply is
@@ -360,11 +393,19 @@ func (x *Xip) processQuestion(q dnsmessage.Question, srcAddr net.IP) (response R
RCode: dnsmessage.RCodeSuccess, // assume success, may be replaced later RCode: dnsmessage.RCodeSuccess, // assume success, may be replaced later
}, },
} }
if IsDelegated(q.Name.String()) {
// if xip.pivotal.io has been delegated to ns-437.awsdns-54.com.
// and a query comes in for 127-0-0-1.cloudfoundry.xip.pivotal.io
// then don't resolve the A record; instead, return the delegated
// NS record, ns-437.awsdns-54.com.
response.Header.Authoritative = false
return x.NSResponse(q.Name, response, logMessage)
}
if IsAcmeChallenge(q.Name.String()) && !x.blocklist(q.Name.String()) { if IsAcmeChallenge(q.Name.String()) && !x.blocklist(q.Name.String()) {
// thanks, @NormanR // thanks, @NormanR
// delegate everything to its stripped (remove "_acme-challenge.") address, e.g. // delegate everything to its stripped (remove "_acme-challenge.") address, e.g.
// dig _acme-challenge.127-0-0-1.sslip.io mx → NS 127-0-0-1.sslip.io // dig _acme-challenge.127-0-0-1.sslip.io mx → NS 127-0-0-1.sslip.io
response.Header.Authoritative = false // we're delegating, so we're not authoritative response.Header.Authoritative = false
return x.NSResponse(q.Name, response, logMessage) return x.NSResponse(q.Name, response, logMessage)
} }
switch q.Type { switch q.Type {
@@ -598,8 +639,6 @@ func (x *Xip) processQuestion(q dnsmessage.Question, srcAddr net.IP) (response R
} }
// NSResponse sets the Answers/Authorities depending upon whether we're delegating or authoritative // NSResponse sets the Answers/Authorities depending upon whether we're delegating or authoritative
// (whether it's an "_acme-challenge." domain or not). Either way, it supplies the Additionals
// (IP addresses of the nameservers).
func (x *Xip) NSResponse(name dnsmessage.Name, response Response, logMessage string) (Response, string, error) { func (x *Xip) NSResponse(name dnsmessage.Name, response Response, logMessage string) (Response, string, error) {
nameServers := x.NSResources(name.String()) nameServers := x.NSResources(name.String())
var logMessages []string var logMessages []string
@@ -753,9 +792,10 @@ func MXResources(fqdnString string) []dnsmessage.MXResource {
} }
func IsAcmeChallenge(fqdnString string) bool { func IsAcmeChallenge(fqdnString string) bool {
if dns01ChallengeRE.MatchString(fqdnString) { fqdnStringLowerCased := strings.ToLower(fqdnString)
ipv4s := NameToA(fqdnString, true) if dns01ChallengeRE.MatchString(fqdnStringLowerCased) {
ipv6s := NameToAAAA(fqdnString, true) ipv4s := NameToA(fqdnStringLowerCased, true)
ipv6s := NameToAAAA(fqdnStringLowerCased, true)
if len(ipv4s) > 0 || len(ipv6s) > 0 { if len(ipv4s) > 0 || len(ipv6s) > 0 {
return true return true
} }
@@ -763,15 +803,40 @@ func IsAcmeChallenge(fqdnString string) bool {
return false return false
} }
func IsDelegated(fqdnString string) bool {
fqdnStringLowerCased := strings.ToLower(fqdnString)
for domain := range Customizations {
if Customizations[domain].NS == nil { // no nameserver? then it can't be delegated
continue
}
// the "." prevents "where.com" from being mistakenly recognized as a subdomain of "here.com"
if strings.HasSuffix(fqdnStringLowerCased, "."+domain) || fqdnStringLowerCased == domain {
return true
}
}
return false
}
func (x *Xip) NSResources(fqdnString string) []dnsmessage.NSResource { func (x *Xip) NSResources(fqdnString string) []dnsmessage.NSResource {
if x.blocklist(fqdnString) { fqdnStringLowerCased := strings.ToLower(fqdnString)
if x.blocklist(fqdnStringLowerCased) {
x.Metrics.AnsweredQueries++ x.Metrics.AnsweredQueries++
x.Metrics.AnsweredBlockedQueries++ x.Metrics.AnsweredBlockedQueries++
return x.NameServers return x.NameServers
} }
if IsAcmeChallenge(fqdnString) { // Is this a delegated domain? Let's return the delegated nameservers
for domain := range Customizations {
if Customizations[domain].NS == nil { // no nameserver? then it can't be delegated
continue
}
// the "." prevents "where.com" from being mistakenly recognized as a subdomain of "here.com"
if strings.HasSuffix(fqdnStringLowerCased, "."+domain) || fqdnStringLowerCased == domain {
return Customizations[domain].NS
}
}
if IsAcmeChallenge(fqdnStringLowerCased) {
x.Metrics.AnsweredNSDNS01ChallengeQueries++ x.Metrics.AnsweredNSDNS01ChallengeQueries++
strippedFqdn := dns01ChallengeRE.ReplaceAllString(fqdnString, "") strippedFqdn := dns01ChallengeRE.ReplaceAllString(fqdnStringLowerCased, "")
ns, _ := dnsmessage.NewName(strippedFqdn) ns, _ := dnsmessage.NewName(strippedFqdn)
return []dnsmessage.NSResource{{NS: ns}} return []dnsmessage.NSResource{{NS: ns}}
} }

View File

@@ -79,7 +79,7 @@ var _ = Describe("Xip", func() {
Describe("NSResources()", func() { Describe("NSResources()", func() {
When("we use the default nameservers", func() { When("we use the default nameservers", func() {
var x, _ = xip.NewXip("file:///", []string{"ns-aws.sslip.io.", "ns-azure.sslip.io.", "ns-gce.sslip.io."}, []string{}) var x, _ = xip.NewXip("file:///", []string{"ns-aws.sslip.io.", "ns-azure.sslip.io.", "ns-gce.sslip.io."}, []string{}, []string{})
It("returns the name servers", func() { It("returns the name servers", func() {
randomDomain := testhelper.Random8ByteString() + ".com." randomDomain := testhelper.Random8ByteString() + ".com."
ns := x.NSResources(randomDomain) ns := x.NSResources(randomDomain)
@@ -94,7 +94,7 @@ var _ = Describe("Xip", func() {
randomDomain := "192.168.0.1." + testhelper.Random8ByteString() + ".com." randomDomain := "192.168.0.1." + testhelper.Random8ByteString() + ".com."
ns := x.NSResources("_acme-challenge." + randomDomain) ns := x.NSResources("_acme-challenge." + randomDomain)
Expect(len(ns)).To(Equal(1)) Expect(len(ns)).To(Equal(1))
Expect(ns[0].NS.String()).To(Equal(randomDomain)) Expect(ns[0].NS.String()).To(Equal(strings.ToLower(randomDomain)))
aResources := xip.NameToA(randomDomain, true) aResources := xip.NameToA(randomDomain, true)
Expect(len(aResources)).To(Equal(1)) Expect(len(aResources)).To(Equal(1))
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
@@ -109,9 +109,25 @@ var _ = Describe("Xip", func() {
}) })
}) })
}) })
When("we delegate domains to other nameservers", func() {
When(`we don't use the "=" in the arguments`, func() {
It("returns an informative log message", func() {
var _, logs = xip.NewXip("file://etc/blocklist-test.txt", []string{"ns-aws.sslip.io.", "ns-azure.sslip.io.", "ns-gce.sslip.io."}, []string{}, []string{"noEquals"})
Expect(strings.Join(logs, "")).To(MatchRegexp(`"-delegates: arguments should be in the format "delegatedDomain=nameserver", not "noEquals"`))
})
})
When(`there's no "." at the end of the delegated domain or nameserver`, func() {
It(`helpfully adds the "."`, func() {
var x, logs = xip.NewXip("file://etc/blocklist-test.txt", []string{"ns-aws.sslip.io.", "ns-azure.sslip.io.", "ns-gce.sslip.io."}, []string{}, []string{"a=b"})
Expect(strings.Join(logs, "")).To(MatchRegexp(`Adding delegated NS record "a\.=b\."`))
ns := x.NSResources("a.")
Expect(len(ns)).To(Equal(1))
})
})
})
}) })
When("we override the default nameservers", func() { When("we override the default nameservers", func() {
var x, _ = xip.NewXip("file:///", []string{"mickey", "minn.ie.", "goo.fy"}, []string{}) var x, _ = xip.NewXip("file:///", []string{"mickey", "minn.ie.", "goo.fy"}, []string{}, []string{})
It("returns the configured servers", func() { It("returns the configured servers", func() {
randomDomain := testhelper.Random8ByteString() + ".com." randomDomain := testhelper.Random8ByteString() + ".com."
ns := x.NSResources(randomDomain) ns := x.NSResources(randomDomain)
@@ -287,6 +303,36 @@ var _ = Describe("Xip", func() {
}) })
}) })
}) })
Describe("IsDelegated()", func() {
var nsName dnsmessage.Name
nsName, err = dnsmessage.NewName("1.com")
Expect(err).ToNot(HaveOccurred())
xip.Customizations["a.com"] = xip.DomainCustomization{NS: []dnsmessage.NSResource{dnsmessage.NSResource{NS: nsName}}}
xip.Customizations["b.com"] = xip.DomainCustomization{}
When("the domain is delegated", func() {
When("the fqdn exactly matches the domain", func() {
It("returns true", func() {
Expect(xip.IsDelegated("A.com")).To(BeTrue())
})
})
When("the fqdn is a subdomain of the domain", func() {
It("returns true", func() {
Expect(xip.IsDelegated("b.a.COM")).To(BeTrue())
})
})
When("the fqdn doesn't match the domain", func() {
It("returns false", func() {
Expect(xip.IsDelegated("Aa.com")).To(BeFalse())
})
})
})
When("the domain is customized but not delegated", func() {
It("returns false", func() {
Expect(xip.IsDelegated("b.COM")).To(BeFalse())
})
})
})
Describe("NameToAAAA()", func() { Describe("NameToAAAA()", func() {
DescribeTable("when it succeeds", DescribeTable("when it succeeds",