[lxc-devel] [lxd/master] Fan reverse DNS requests
tomponline on Github
lxc-bot at linuxcontainers.org
Tue Jun 4 17:04:52 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1000 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190604/9d02a6c0/attachment-0001.bin>
-------------- next part --------------
From 6ba5fe3ecc80c12a41aec3219e86622868b36703 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 4 Jun 2019 15:33:58 +0100
Subject: [PATCH 1/3] networks: Forwards fan RDNS zone to forkdns for cluster
RDNS resolution
Fixes #5798
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/networks.go | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/lxd/networks.go b/lxd/networks.go
index 5ce5bfda11..1eece2edb1 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -1535,6 +1535,7 @@ func (n *network) Start() error {
// Configure the fan
dnsClustered := false
dnsClusteredAddress := ""
+ var overlaySubnet *net.IPNet
if n.config["bridge.mode"] == "fan" {
tunName := fmt.Sprintf("%s-fan", n.name)
@@ -1551,7 +1552,7 @@ func (n *network) Start() error {
overlay = "240.0.0.0/8"
}
- _, overlaySubnet, err := net.ParseCIDR(overlay)
+ _, overlaySubnet, err = net.ParseCIDR(overlay)
if err != nil {
return err
}
@@ -1794,6 +1795,10 @@ func (n *network) Start() error {
dnsmasqCmd = append(dnsmasqCmd, []string{"-s", "__internal", "-S", "/__internal/"}...)
dnsmasqCmd = append(dnsmasqCmd, []string{"-S", fmt.Sprintf("/%s/%s#1053", dnsDomain, dnsClusteredAddress)}...)
dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option=15,%s", dnsDomain))
+
+ if overlaySubnet != nil {
+ dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--rev-server=%s,%s#1053", overlaySubnet, dnsClusteredAddress))
+ }
} else {
dnsmasqCmd = append(dnsmasqCmd, []string{"-s", dnsDomain, "-S", fmt.Sprintf("/%s/", dnsDomain)}...)
}
@@ -2101,10 +2106,13 @@ func (n *network) spawnForkDNS(listenAddress string) error {
dnsDomain = "lxd"
}
+ // Lease file to parse
+ leaseFile := shared.VarPath("networks", n.name, "dnsmasq.leases")
+
// Spawn the daemon
cmd := exec.Cmd{}
cmd.Path = n.state.OS.ExecPath
- cmd.Args = []string{n.state.OS.ExecPath, "forkdns", fmt.Sprintf("%s:1053", listenAddress), dnsDomain}
+ cmd.Args = []string{n.state.OS.ExecPath, "forkdns", fmt.Sprintf("%s:1053", listenAddress), dnsDomain, leaseFile}
cmd.Args = append(cmd.Args, addresses...)
err = cmd.Start()
From bdc87a963b2919648ca495fcc668bb71b322121e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 4 Jun 2019 17:48:26 +0100
Subject: [PATCH 2/3] dnsutil: Adds reverse dns utils from coreos dnsutil
package
Suggested-By: Christian Brauner <christian.brauner at ubuntu.com>
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/dnsutil/dnsutil.go | 83 +++++++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 shared/dnsutil/dnsutil.go
diff --git a/shared/dnsutil/dnsutil.go b/shared/dnsutil/dnsutil.go
new file mode 100644
index 0000000000..4ef4db3615
--- /dev/null
+++ b/shared/dnsutil/dnsutil.go
@@ -0,0 +1,83 @@
+// Package dnsutil copied from coredns project
+// https://github.com/coredns/coredns/blob/master/plugin/pkg/dnsutil/reverse.go
+package dnsutil
+
+import (
+ "net"
+ "strings"
+)
+
+// ExtractAddressFromReverse turns a standard PTR reverse record name
+// into an IP address. This works for ipv4 or ipv6.
+//
+// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
+// fails the empty string is returned.
+func ExtractAddressFromReverse(reverseName string) string {
+ search := ""
+
+ f := reverse
+
+ switch {
+ case strings.HasSuffix(reverseName, IP4arpa):
+ search = strings.TrimSuffix(reverseName, IP4arpa)
+ case strings.HasSuffix(reverseName, IP6arpa):
+ search = strings.TrimSuffix(reverseName, IP6arpa)
+ f = reverse6
+ default:
+ return ""
+ }
+
+ // Reverse the segments and then combine them.
+ return f(strings.Split(search, "."))
+}
+
+// IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates
+// name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4)
+// and 2 for ip6.arpa. (IPv6).
+func IsReverse(name string) int {
+ if strings.HasSuffix(name, IP4arpa) {
+ return 1
+ }
+ if strings.HasSuffix(name, IP6arpa) {
+ return 2
+ }
+ return 0
+}
+
+func reverse(slice []string) string {
+ for i := 0; i < len(slice)/2; i++ {
+ j := len(slice) - i - 1
+ slice[i], slice[j] = slice[j], slice[i]
+ }
+ ip := net.ParseIP(strings.Join(slice, ".")).To4()
+ if ip == nil {
+ return ""
+ }
+ return ip.String()
+}
+
+// reverse6 reverse the segments and combine them according to RFC3596:
+// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
+// is reversed to 2001:db8::567:89ab
+func reverse6(slice []string) string {
+ for i := 0; i < len(slice)/2; i++ {
+ j := len(slice) - i - 1
+ slice[i], slice[j] = slice[j], slice[i]
+ }
+ slice6 := []string{}
+ for i := 0; i < len(slice)/4; i++ {
+ slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
+ }
+ ip := net.ParseIP(strings.Join(slice6, ":")).To16()
+ if ip == nil {
+ return ""
+ }
+ return ip.String()
+}
+
+const (
+ // IP4arpa is the reverse tree suffix for v4 IP addresses.
+ IP4arpa = ".in-addr.arpa."
+ // IP6arpa is the reverse tree suffix for v6 IP addresses.
+ IP6arpa = ".ip6.arpa."
+)
From cd1c70d065f1a5f59d5860dd9c85e5d97d1a8069 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 4 Jun 2019 17:50:35 +0100
Subject: [PATCH 3/3] forkdns: Answers PTR requests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Updates forkdns to answer PTR requests by parsing the local dnsmasq leases file.
Suggested-By: Stéphane Graber <stgraber at ubuntu.com>
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/main_forkdns.go | 100 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 93 insertions(+), 7 deletions(-)
diff --git a/lxd/main_forkdns.go b/lxd/main_forkdns.go
index fbc3b833a1..035a347b05 100644
--- a/lxd/main_forkdns.go
+++ b/lxd/main_forkdns.go
@@ -1,9 +1,15 @@
package main
import (
+ "bufio"
+ "errors"
"fmt"
+ "os"
"strings"
+ "github.com/lxc/lxd/shared/dnsutil"
+ "github.com/lxc/lxd/shared/logger"
+ "github.com/lxc/lxd/shared/logging"
"github.com/miekg/dns"
"github.com/spf13/cobra"
)
@@ -13,8 +19,9 @@ type cmdForkDNS struct {
}
type dnsHandler struct {
- domain string
- servers []string
+ domain string
+ leaseFile string
+ servers []string
}
func (h *dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
@@ -28,6 +35,17 @@ func (h *dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
return
}
+ // Handle reverse DNS PTR type queries separately.
+ if r.Question[0].Qtype == dns.TypePTR {
+ err := h.handlePTR(w, r)
+ if err != nil {
+ logger.Errorf("PTR lookup failed: %s %v", r.Question[0].Name, err)
+ msg.SetRcode(r, dns.RcodeNameError)
+ w.WriteMsg(&msg)
+ }
+ return
+ }
+
// Rewrite the question to the internal domain
origName := r.Question[0].Name
newName := origName
@@ -66,10 +84,70 @@ func (h *dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
w.WriteMsg(&msg)
}
+// handlePTR performs a lookup in the local dnsmasq leases file for reverse DNS entries and if not
+// forwards to the other forkdns instances to check their local leases file. It does not forward
+// requests to dnsmasq itself as that would cause an infinite query loop.
+func (h *dnsHandler) handlePTR(w dns.ResponseWriter, r *dns.Msg) error {
+ msg := dns.Msg{}
+ msg.SetReply(r)
+ msg.Authoritative = true
+ hostname, err := h.getLeaseByReverseName(r.Question[0].Name)
+ if err != nil {
+ return err
+ }
+
+ if hostname == "" {
+ msg.SetRcode(r, dns.RcodeNameError)
+ w.WriteMsg(&msg)
+ return nil
+ }
+
+ msg.Answer = append(msg.Answer, &dns.PTR{
+ Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0},
+ Ptr: fmt.Sprintf("%s.lxd.", hostname),
+ })
+
+ err = w.WriteMsg(&msg)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *dnsHandler) getLeaseByReverseName(reverseName string) (string, error) {
+ ip := dnsutil.ExtractAddressFromReverse(reverseName)
+ if ip == "" {
+ return "", errors.New("failed to convert reverse name to IP")
+ }
+
+ file, err := os.Open(h.leaseFile)
+ if err != nil {
+ return "", err
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if len(fields) >= 5 {
+ address := fields[2]
+ if address == ip {
+ return fields[3], nil
+ }
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return "", err
+ }
+
+ return "", nil
+}
+
func (c *cmdForkDNS) Command() *cobra.Command {
// Main subcommand
cmd := &cobra.Command{}
- cmd.Use = "forkdns <listen address> <domain> <servers...>"
+ cmd.Use = "forkdns <listen address> <domain> <leases file> <servers...>"
cmd.Short = "Internal DNS proxy for clustering"
cmd.Long = `Description:
Spawns a tiny DNS server which forwards to all upstream servers until
@@ -83,7 +161,7 @@ func (c *cmdForkDNS) Command() *cobra.Command {
func (c *cmdForkDNS) Run(cmd *cobra.Command, args []string) error {
// Sanity checks
- if len(args) < 3 {
+ if len(args) < 4 {
cmd.Help()
if len(args) == 0 {
@@ -93,17 +171,25 @@ func (c *cmdForkDNS) Run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Missing required arguments")
}
+ log, err := logging.GetLogger("lxd-forkdns", "", false, false, eventsHandler{})
+ if err != nil {
+ return err
+ }
+ logger.Log = log
+ logger.Info("Started")
+
srv := &dns.Server{
Addr: args[0],
Net: "udp",
}
srv.Handler = &dnsHandler{
- domain: args[1],
- servers: args[2:],
+ domain: args[1],
+ leaseFile: args[2],
+ servers: args[3:],
}
- err := srv.ListenAndServe()
+ err = srv.ListenAndServe()
if err != nil {
return fmt.Errorf("Failed to set udp listener: %v\n", err)
}
More information about the lxc-devel
mailing list