The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7228

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Allows instances to access the proxy device's listen IP when using NAT mode.
From 6ec542e41c23e2446a21d91ecec5ed1c616c7edd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 12:03:47 +0100
Subject: [PATCH 1/7] lxd/device/proxy: Check for br_netfilter enabled and log
 warning if not

br_netfilter is be required in order to allow other instances to connect to 
instance proxy listen addresses in nat mode.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/proxy.go | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go
index 6b509aa38d..ca7ea7146d 100644
--- a/lxd/device/proxy.go
+++ b/lxd/device/proxy.go
@@ -12,6 +12,7 @@ import (
        "strings"
        "time"
 
+       "github.com/pkg/errors"
        "golang.org/x/sys/unix"
        liblxc "gopkg.in/lxc/go-lxc.v2"
 
@@ -19,6 +20,7 @@ import (
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/project"
+       "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/logger"
 )
@@ -312,6 +314,11 @@ func (d *proxy) setupNAT() error {
                }
        }
 
+       err = d.checkBridgeNetfilterEnabled(ipFamily)
+       if err != nil {
+               logger.Warnf("Proxy bridge netfilter not enabled: %v. Instances 
using the bridge will not be able to connect to the proxy's listen IP", err)
+       }
+
        err = d.state.Firewall.InstanceSetupProxyNAT(d.inst.Project(), 
d.inst.Name(), d.name, listenAddr, connectAddr)
        if err != nil {
                return err
@@ -320,6 +327,30 @@ func (d *proxy) setupNAT() error {
        return nil
 }
 
+// checkBridgeNetfilterEnabled checks whether the bridge netfilter feature is 
loaded and enabled.
+// If it is not an error is returned. This is needed in order for instances 
connected to a bridge to access the
+// proxy's listen IP on the LXD host, as otherwise the packets from the bridge 
do not go through the netfilter
+// NAT SNAT/MASQUERADE rules.
+func (d *proxy) checkBridgeNetfilterEnabled(ipFamily string) error {
+       sysctlName := "iptables"
+       if ipFamily == "ipv6" {
+               sysctlName = "ip6tables"
+       }
+
+       sysctlPath := fmt.Sprintf("net/bridge/bridge-nf-call-%s", sysctlName)
+       sysctlVal, err := util.SysctlGet(sysctlPath)
+       if err != nil {
+               return errors.Wrap(err, "br_netfilter not loaded")
+       }
+
+       sysctlVal = strings.TrimSpace(sysctlVal)
+       if sysctlVal != "1" {
+               return fmt.Errorf("br_netfilter sysctl 
net.bridge.bridge-nf-call-%s=%s", sysctlName, sysctlVal)
+       }
+
+       return nil
+}
+
 func (d *proxy) rewriteHostAddr(addr string) string {
        fields := strings.SplitN(addr, ":", 2)
        proto := fields[0]

From 1da3c8cc718100f8e13f216015c269269875d3fa Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 12:07:24 +0100
Subject: [PATCH 2/7] lxd/firewall/drivers/driver/xtables: Adds MASQUERADE
 hairpin proxy NAT rule

Allows instance that has proxy device to connect to its own proxy listen IP.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lxd/firewall/drivers/drivers_xtables.go 
b/lxd/firewall/drivers/drivers_xtables.go
index 8aea7b38da..2f251c8cf0 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -397,6 +397,13 @@ func (d Xtables) InstanceSetupProxyNAT(projectName string, 
instanceName string,
                if err != nil {
                        return err
                }
+
+               // instance <-> instance.
+               // Requires instance's bridge port has hairpin mode enabled 
when br_netfilter is loaded.
+               err = d.iptablesPrepend(ipVersion, comment, "nat", 
"POSTROUTING", "-p", listen.ConnType, "--source", connectHost, "--destination", 
connectHost, "--dport", connectPort, "-j", "MASQUERADE")
+               if err != nil {
+                       return err
+               }
        }
 
        revert.Success()

From d649a71598cc61971ae1b20311ae1535b7819d1e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 12:08:33 +0100
Subject: [PATCH 3/7] lxd/firewall/drivers/drivers/xtables: comments

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/firewall/drivers/drivers_xtables.go 
b/lxd/firewall/drivers/drivers_xtables.go
index 2f251c8cf0..7565bf7f65 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -377,7 +377,7 @@ func (d Xtables) InstanceSetupProxyNAT(projectName string, 
instanceName string,
                        return err
                }
 
-               // Figure out if we are using iptables or ip6tables and format 
the destination host/port as appropriate.
+               // Decide if we are using iptables/ip6tables and format the 
destination host/port as appropriate.
                ipVersion := uint(4)
                toDest := fmt.Sprintf("%s:%s", connectHost, connectPort)
                connectIP := net.ParseIP(connectHost)
@@ -386,13 +386,13 @@ func (d Xtables) InstanceSetupProxyNAT(projectName 
string, instanceName string,
                        toDest = fmt.Sprintf("[%s]:%s", connectHost, 
connectPort)
                }
 
-               // outbound <-> container.
+               // outbound <-> instance.
                err = d.iptablesPrepend(ipVersion, comment, "nat", 
"PREROUTING", "-p", listen.ConnType, "--destination", listenHost, "--dport", 
listenPort, "-j", "DNAT", "--to-destination", toDest)
                if err != nil {
                        return err
                }
 
-               // host <-> container.
+               // host <-> instance.
                err = d.iptablesPrepend(ipVersion, comment, "nat", "OUTPUT", 
"-p", listen.ConnType, "--destination", listenHost, "--dport", listenPort, 
"-j", "DNAT", "--to-destination", toDest)
                if err != nil {
                        return err
@@ -433,7 +433,7 @@ func (d Xtables) InstanceClearProxyNAT(projectName string, 
instanceName string,
 
 // generateFilterEbtablesRules returns a customised set of ebtables filter 
rules based on the device.
 func (d Xtables) generateFilterEbtablesRules(hostName string, hwAddr string, 
IPv4 net.IP, IPv6 net.IP) [][]string {
-       // MAC source filtering rules. Blocks any packet coming from instance 
with an incorrect Ethernet source MAC.
+       // MAC source filtering rules. Block any packet coming from instance 
with an incorrect Ethernet source MAC.
        // This is required for IP filtering too.
        rules := [][]string{
                {"ebtables", "-t", "filter", "-A", "INPUT", "-s", "!", hwAddr, 
"-i", hostName, "-j", "DROP"},

From d5260209e0f56b1565467351cb24a95a698be14a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 13:16:47 +0100
Subject: [PATCH 4/7] lxd/device/proxy: Sets bridge port hairpin mode on when
 br_netfilter loaded

This allows the proxy instance and the other instances on the bridge to connect 
to the proxy's listen IP.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/proxy.go | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go
index ca7ea7146d..4e25213b58 100644
--- a/lxd/device/proxy.go
+++ b/lxd/device/proxy.go
@@ -273,8 +273,9 @@ func (d *proxy) setupNAT() error {
        }
 
        var connectIP net.IP
+       var hostName string
 
-       for _, devConfig := range d.inst.ExpandedDevices() {
+       for devName, devConfig := range d.inst.ExpandedDevices() {
                if devConfig["type"] != "nic" || (devConfig["type"] == "nic" && 
devConfig.NICType() != "bridged") {
                        continue
                }
@@ -285,18 +286,22 @@ func (d *proxy) setupNAT() error {
                if ipFamily == "ipv4" && devConfig["ipv4.address"] != "" {
                        if connectHost == devConfig["ipv4.address"] || 
connectHost == "0.0.0.0" {
                                connectIP = 
net.ParseIP(devConfig["ipv4.address"])
-                               break
                        }
                } else if ipFamily == "ipv6" && devConfig["ipv6.address"] != "" 
{
                        if connectHost == devConfig["ipv6.address"] || 
connectHost == "::" {
                                connectIP = 
net.ParseIP(devConfig["ipv6.address"])
-                               break
                        }
                }
+
+               if connectIP != nil {
+                       // Get host_name of device so we can enable hairpin 
mode on bridge port.
+                       hostName = 
d.inst.ExpandedConfig()[fmt.Sprintf("volatile.%s.host_name", devName)]
+                       break // Found a match, stop searching.
+               }
        }
 
        if connectIP == nil {
-               return fmt.Errorf("Proxy connect IP cannot be used with any NIC 
static IPs")
+               return fmt.Errorf("Proxy connect IP cannot be used with any of 
the instance NICs static IPs")
        }
 
        // Override the host part of the connectAddr.Addr to the chosen connect 
IP.
@@ -317,6 +322,18 @@ func (d *proxy) setupNAT() error {
        err = d.checkBridgeNetfilterEnabled(ipFamily)
        if err != nil {
                logger.Warnf("Proxy bridge netfilter not enabled: %v. Instances 
using the bridge will not be able to connect to the proxy's listen IP", err)
+       } else {
+               if hostName == "" {
+                       return fmt.Errorf("Proxy cannot find bridge port 
host_name to enable hairpin mode")
+               }
+
+               // br_netfilter is enabled, so we need to enable hairpin mode 
on instance's bridge port otherwise
+               // the instances on the bridge will not be able to connect to 
the proxy device's listn IP and the
+               // NAT rule added by the firewall below to allow instance <-> 
instance traffic will also not work.
+               _, err = shared.RunCommand("bridge", "link", "set", "dev", 
hostName, "hairpin", "on")
+               if err != nil {
+                       return errors.Wrapf(err, "Error enabling hairpin mode 
on bridge port %q", hostName)
+               }
        }
 
        err = d.state.Firewall.InstanceSetupProxyNAT(d.inst.Project(), 
d.inst.Name(), d.name, listenAddr, connectAddr)

From 7f78e819455c75858534b288a894072f0cabadbb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 13:25:39 +0100
Subject: [PATCH 5/7] lxd/firewall/drivers/drivers/xtables: Renames toDest to
 connectDest

For consistency with connectHost and connectPort vars.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/firewall/drivers/drivers_xtables.go 
b/lxd/firewall/drivers/drivers_xtables.go
index 7565bf7f65..836dfce32e 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -379,21 +379,21 @@ func (d Xtables) InstanceSetupProxyNAT(projectName 
string, instanceName string,
 
                // Decide if we are using iptables/ip6tables and format the 
destination host/port as appropriate.
                ipVersion := uint(4)
-               toDest := fmt.Sprintf("%s:%s", connectHost, connectPort)
+               connectDest := fmt.Sprintf("%s:%s", connectHost, connectPort)
                connectIP := net.ParseIP(connectHost)
                if connectIP.To4() == nil {
                        ipVersion = 6
-                       toDest = fmt.Sprintf("[%s]:%s", connectHost, 
connectPort)
+                       connectDest = fmt.Sprintf("[%s]:%s", connectHost, 
connectPort)
                }
 
                // outbound <-> instance.
-               err = d.iptablesPrepend(ipVersion, comment, "nat", 
"PREROUTING", "-p", listen.ConnType, "--destination", listenHost, "--dport", 
listenPort, "-j", "DNAT", "--to-destination", toDest)
+               err = d.iptablesPrepend(ipVersion, comment, "nat", 
"PREROUTING", "-p", listen.ConnType, "--destination", listenHost, "--dport", 
listenPort, "-j", "DNAT", "--to-destination", connectDest)
                if err != nil {
                        return err
                }
 
                // host <-> instance.
-               err = d.iptablesPrepend(ipVersion, comment, "nat", "OUTPUT", 
"-p", listen.ConnType, "--destination", listenHost, "--dport", listenPort, 
"-j", "DNAT", "--to-destination", toDest)
+               err = d.iptablesPrepend(ipVersion, comment, "nat", "OUTPUT", 
"-p", listen.ConnType, "--destination", listenHost, "--dport", listenPort, 
"-j", "DNAT", "--to-destination", connectDest)
                if err != nil {
                        return err
                }

From 68e5f9aa1c895fc7f9247436360c54b6da5fcb02 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 13:27:02 +0100
Subject: [PATCH 6/7] lxd/firewall/drivers/drivers/nftables: Renames toDest to
 connectDest

For consistency with connectHost and connectPort vars.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/firewall/drivers/drivers_nftables.go           | 14 +++++++-------
 lxd/firewall/drivers/drivers_nftables_templates.go |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/lxd/firewall/drivers/drivers_nftables.go 
b/lxd/firewall/drivers/drivers_nftables.go
index be42a16751..916e357fea 100644
--- a/lxd/firewall/drivers/drivers_nftables.go
+++ b/lxd/firewall/drivers/drivers_nftables.go
@@ -363,19 +363,19 @@ func (d Nftables) InstanceSetupProxyNAT(projectName 
string, instanceName string,
 
                // Figure out which IP family we are using and format the 
destination host/port as appropriate.
                family := "ip"
-               toDest := fmt.Sprintf("%s:%s", connectHost, connectPort)
+               connectDest := fmt.Sprintf("%s:%s", connectHost, connectPort)
                connectIP := net.ParseIP(connectHost)
                if connectIP.To4() == nil {
                        family = "ip6"
-                       toDest = fmt.Sprintf("[%s]:%s", connectHost, 
connectPort)
+                       connectDest = fmt.Sprintf("[%s]:%s", connectHost, 
connectPort)
                }
 
                rules = append(rules, map[string]interface{}{
-                       "family":     family,
-                       "connType":   listen.ConnType,
-                       "listenHost": listenHost,
-                       "listenPort": listenPort,
-                       "toDest":     toDest,
+                       "family":      family,
+                       "connType":    listen.ConnType,
+                       "listenHost":  listenHost,
+                       "listenPort":  listenPort,
+                       "connectDest": connectDest,
                })
        }
 
diff --git a/lxd/firewall/drivers/drivers_nftables_templates.go 
b/lxd/firewall/drivers/drivers_nftables_templates.go
index 49fcf35e67..6aae6006cb 100644
--- a/lxd/firewall/drivers/drivers_nftables_templates.go
+++ b/lxd/firewall/drivers/drivers_nftables_templates.go
@@ -57,7 +57,7 @@ var nftablesNetProxyNAT = 
template.Must(template.New("nftablesNetProxyNAT").Pars
 chain prert{{.chainSeparator}}{{.deviceLabel}} {
        type nat hook prerouting priority -100; policy accept;
        {{- range .rules}}
-       {{.family}} daddr {{.listenHost}} {{.connType}} dport {{.listenPort}} 
dnat to {{.toDest}}
+       {{.family}} daddr {{.listenHost}} {{.connType}} dport {{.listenPort}} 
dnat to {{.connectDest}}
        {{- end}}
 }
 

From b6664b8cc7a65f91f425dafc66ce5d1d9e471677 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Apr 2020 14:14:08 +0100
Subject: [PATCH 7/7] lxd/firewall/drivers/drivers/nftables: Adds MASQUERADE
 hairpin proxy NAT rule

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/firewall/drivers/drivers_nftables.go           |  4 +++-
 lxd/firewall/drivers/drivers_nftables_templates.go | 11 +++++++++--
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/lxd/firewall/drivers/drivers_nftables.go 
b/lxd/firewall/drivers/drivers_nftables.go
index 916e357fea..d13aad89b3 100644
--- a/lxd/firewall/drivers/drivers_nftables.go
+++ b/lxd/firewall/drivers/drivers_nftables.go
@@ -376,6 +376,8 @@ func (d Nftables) InstanceSetupProxyNAT(projectName string, 
instanceName string,
                        "listenHost":  listenHost,
                        "listenPort":  listenPort,
                        "connectDest": connectDest,
+                       "connectHost": connectHost,
+                       "connectPort": connectPort,
                })
        }
 
@@ -399,7 +401,7 @@ func (d Nftables) InstanceSetupProxyNAT(projectName string, 
instanceName string,
 // InstanceClearProxyNAT remove DNAT rules for proxy devices.
 func (d Nftables) InstanceClearProxyNAT(projectName string, instanceName 
string, deviceName string) error {
        deviceLabel := d.instanceDeviceLabel(projectName, instanceName, 
deviceName)
-       err := d.removeChains([]string{"ip", "ip6"}, deviceLabel, "out", 
"prert")
+       err := d.removeChains([]string{"ip", "ip6"}, deviceLabel, "out", 
"prert", "pstrt")
        if err != nil {
                return errors.Wrapf(err, "Failed clearing proxy rules for 
instance device %q", deviceLabel)
        }
diff --git a/lxd/firewall/drivers/drivers_nftables_templates.go 
b/lxd/firewall/drivers/drivers_nftables_templates.go
index 6aae6006cb..31551e4bcd 100644
--- a/lxd/firewall/drivers/drivers_nftables_templates.go
+++ b/lxd/firewall/drivers/drivers_nftables_templates.go
@@ -61,10 +61,17 @@ chain prert{{.chainSeparator}}{{.deviceLabel}} {
        {{- end}}
 }
 
-chain out{{.chainSeparator}}{{.deviceLabel}}{
+chain out{{.chainSeparator}}{{.deviceLabel}} {
        type nat hook output priority -100; policy accept;
        {{- range .rules}}
-       {{.family}} daddr {{.listenHost}} {{.connType}} dport {{.listenPort}} 
dnat to {{.toDest}}
+       {{.family}} daddr {{.listenHost}} {{.connType}} dport {{.listenPort}} 
dnat to {{.connectDest}}
+       {{- end}}
+}
+
+chain pstrt{{.chainSeparator}}{{.deviceLabel}} {
+       type nat hook postrouting priority 100; policy accept;
+       {{- range .rules}}
+       {{.family}} saddr {{.connectHost}} ip daddr {{.connectHost}} 
{{.connType}} dport {{.connectPort}} masquerade
        {{- end}}
 }
 `))
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to