[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