[lxc-devel] [lxd/master] lxd/proxy: Add support for PROXY header

stgraber on Github lxc-bot at linuxcontainers.org
Wed Jul 18 21:46:43 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 370 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180718/195111d1/attachment.bin>
-------------- next part --------------
From 24c5be8d5b67a1e5fc5c2085dccd2dfdf5ebdb76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 18 Jul 2018 17:46:08 -0400
Subject: [PATCH] lxd/proxy: Add support for PROXY header
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #4786

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 doc/api-extensions.md     |  3 +++
 doc/containers.md         |  1 +
 lxd/container.go          |  6 ++++++
 lxd/container_lxc.go      |  4 +++-
 lxd/main_forkproxy.go     | 33 ++++++++++++++++++++++++++++++---
 lxd/proxy_device_utils.go |  2 ++
 shared/version/api.go     |  1 +
 7 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 96b8f0b67..cc4f65543 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -551,3 +551,6 @@ This adds a new core.debug\_address config option to start a debugging HTTP serv
 
 That server currently includes a pprof API and replaces the old
 cpu-profile, memory-profile and print-goroutines debug options.
+
+## proxy\_protocol
+Adds a proxy\_protocol key to the proxy device which controls the use of the HAProxy PROXY protocol header.
diff --git a/doc/containers.md b/doc/containers.md
index 0f3f9af63..fab75c025 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -451,6 +451,7 @@ bind            | string    | host              | no        | Which side to bind
 uid             | int       | 0                 | no        | UID of the owner of the listening Unix socket
 gid             | int       | 0                 | no        | GID of the owner of the listening Unix socket
 mode            | int       | 0755              | no        | Mode for the listening Unix socket
+proxy\_protocol | bool      | false             | no        | Whether to use the HAProxy PROXY protocol to transmit sender information
 security.uid    | int       | 0                 | no        | What UID to drop privilege to
 security.gid    | int       | 0                 | no        | What GID to drop privilege to
 
diff --git a/lxd/container.go b/lxd/container.go
index d748aa597..d75f1e6e1 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -237,6 +237,8 @@ func containerValidDeviceConfigKey(t, k string) bool {
 			return true
 		case "mode":
 			return true
+		case "proxy_protocol":
+			return true
 		case "security.gid":
 			return true
 		case "security.uid":
@@ -469,6 +471,10 @@ func containerValidDevices(db *db.Cluster, devices types.Devices, profile bool,
 				return fmt.Errorf("Proxy device entry is missing the required \"connect\" property.")
 			}
 
+			if shared.IsTrue(m["proxy_protocol"]) && !strings.HasPrefix(m["connect"], "tcp") {
+				return fmt.Errorf("The PROXY header can only be sent to tcp servers")
+			}
+
 			if (!strings.HasPrefix(m["listen"], "unix:") || strings.HasPrefix(m["listen"], "unix:@")) &&
 				(m["uid"] != "" || m["gid"] != "" || m["mode"] != "") {
 				return fmt.Errorf("Only proxy devices for non-abstract unix sockets can carry uid, gid, or mode properties")
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index d2636e43f..f44030a06 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -6873,7 +6873,9 @@ func (c *containerLXC) insertProxyDevice(devName string, m types.Device) error {
 		proxyValues.listenAddrUid,
 		proxyValues.listenAddrMode,
 		proxyValues.securityGid,
-		proxyValues.securityUid)
+		proxyValues.securityUid,
+		proxyValues.proxyProtocol,
+	)
 	if err != nil {
 		return fmt.Errorf("Error occurred when starting proxy device: %s", err)
 	}
diff --git a/lxd/main_forkproxy.go b/lxd/main_forkproxy.go
index 4f6d94ca1..cc6f2a455 100644
--- a/lxd/main_forkproxy.go
+++ b/lxd/main_forkproxy.go
@@ -324,7 +324,7 @@ func rearmUDPFd(epFd C.int, connFd C.int) {
 	}
 }
 
-func listenerInstance(epFd C.int, lAddr *proxyAddress, cAddr *proxyAddress, connFd C.int, lStruct *lStruct) error {
+func listenerInstance(epFd C.int, lAddr *proxyAddress, cAddr *proxyAddress, connFd C.int, lStruct *lStruct, proxy bool) error {
 	fmt.Printf("Starting %s <-> %s proxy\n", lAddr.connType, cAddr.connType)
 	if lAddr.connType == "udp" {
 		// This only handles udp <-> udp. The C constructor will have
@@ -379,10 +379,37 @@ func listenerInstance(epFd C.int, lAddr *proxyAddress, cAddr *proxyAddress, conn
 		return err
 	}
 
+	if proxy && cAddr.connType == "tcp" {
+		if lAddr.connType == "unix" {
+			dstConn.Write([]byte(fmt.Sprintf("PROXY UNKNOWN\r\n")))
+		} else {
+			cHost, cPort, err := net.SplitHostPort(srcConn.RemoteAddr().String())
+			if err != nil {
+				return err
+			}
+
+			dHost, dPort, err := net.SplitHostPort(srcConn.LocalAddr().String())
+			if err != nil {
+				return err
+			}
+
+			proto := srcConn.LocalAddr().Network()
+			proto = strings.ToUpper(proto)
+			if strings.Contains(cHost, ":") {
+				proto = fmt.Sprintf("%s6", proto)
+			} else {
+				proto = fmt.Sprintf("%s4", proto)
+			}
+
+			dstConn.Write([]byte(fmt.Sprintf("PROXY %s %s %s %s %s\r\n", proto, cHost, dHost, cPort, dPort)))
+		}
+	}
+
 	if cAddr.connType == "unix" && lAddr.connType == "unix" {
 		// Handle OOB if both src and dst are using unix sockets
 		go unixRelay(srcConn, dstConn)
 	} else {
+
 		go genericRelay(srcConn, dstConn, false)
 	}
 
@@ -403,7 +430,7 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 	}
 
 	// Sanity checks
-	if len(args) != 11 {
+	if len(args) != 12 {
 		cmd.Help()
 
 		if len(args) == 0 {
@@ -647,7 +674,7 @@ func (c *cmdForkproxy) Run(cmd *cobra.Command, args []string) error {
 				continue
 			}
 
-			err := listenerInstance(epFd, lAddr, cAddr, curFd, srcConn)
+			err := listenerInstance(epFd, lAddr, cAddr, curFd, srcConn, args[11] == "true")
 			if err != nil {
 				fmt.Printf("Failed to prepare new listener instance: %s", err)
 			}
diff --git a/lxd/proxy_device_utils.go b/lxd/proxy_device_utils.go
index 176feb757..48f4ca571 100644
--- a/lxd/proxy_device_utils.go
+++ b/lxd/proxy_device_utils.go
@@ -23,6 +23,7 @@ type proxyProcInfo struct {
 	listenAddrMode string
 	securityUid    string
 	securityGid    string
+	proxyProtocol  string
 }
 
 func setupProxyProcInfo(c container, device map[string]string) (*proxyProcInfo, error) {
@@ -68,6 +69,7 @@ func setupProxyProcInfo(c container, device map[string]string) (*proxyProcInfo,
 		listenAddrMode: device["mode"],
 		securityGid:    device["security.gid"],
 		securityUid:    device["security.uid"],
+		proxyProtocol:  device["proxy_protocol"],
 	}
 
 	return p, nil
diff --git a/shared/version/api.go b/shared/version/api.go
index 323e6cf5d..dca9279f1 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -115,6 +115,7 @@ var APIExtensions = []string{
 	"container_protection_delete",
 	"unix_priv_drop",
 	"pprof_http",
+	"proxy_protocol",
 }
 
 // APIExtensionsCount returns the number of available API extensions.


More information about the lxc-devel mailing list