mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-05 23:56:50 +08:00
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:
10
etc/blocklist-test.txt
Normal file
10
etc/blocklist-test.txt
Normal 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
|
||||||
|
|
@@ -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\.`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@@ -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())
|
||||||
|
@@ -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`))
|
||||||
|
6
main.go
6
main.go
@@ -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)
|
||||||
|
87
xip/xip.go
87
xip/xip.go
@@ -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}}
|
||||||
}
|
}
|
||||||
|
@@ -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",
|
||||||
|
Reference in New Issue
Block a user