libnet/d/bridge: port mapping: proxy LL connections

Link-local connections were DNATed like other non-loopback connections,
but the kernel would drop them even before their reach the container.

This commit changes the DNAT rule inserted in ip6tables to exclude
link-local addresses. Instead, these connections will be proxied by
docker-proxy, at least if --userland-proxy=true.

If dockerd is started with the userland-proxy disabled, link-local
port-bindings won't be supported (ie. silently discarded).

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton
2024-09-24 22:55:41 +02:00
parent 2d049d11a9
commit 7ca9e9b496
3 changed files with 18 additions and 5 deletions

View File

@@ -323,14 +323,21 @@ func TestAccessPublishedPortFromHost(t *testing.T) {
// loopback address.
continue
}
addr := hostAddr.String()
if hostAddr.IsLinkLocalUnicast() {
// Mapping ports on link-local addresses is currently
// unsupported.
continue
if !tc.ulpEnabled {
// iptables can DNAT packets addressed to link-local
// addresses, but they won't be SNATed, so the
// target server won't know where to reply. Thus,
// the userland-proxy is required for these addresses.
continue
}
addr += "%25" + iface
}
httpClient := &http.Client{Timeout: 3 * time.Second}
resp, err := httpClient.Get("http://" + net.JoinHostPort(hostAddr.String(), hostPort))
resp, err := httpClient.Get("http://" + net.JoinHostPort(addr, hostPort))
assert.NilError(t, err)
assert.Check(t, is.Equal(resp.StatusCode, 404))
}

View File

@@ -772,6 +772,9 @@ func setPerPortNAT(b portBinding, ipv iptables.IPVersion, proxyPath string, brid
if !hairpinMode {
args = append(args, "!", "-i", bridgeName)
}
if ipv == iptables.IPv6 {
args = append(args, "!", "-s", "fe80::/10")
}
rule := iptRule{ipv: ipv, table: iptables.Nat, chain: DockerChain, args: args}
if err := appendOrDelChainRule(rule, "DNAT", enable); err != nil {
return err

View File

@@ -921,9 +921,12 @@ func TestAddPortMappings(t *testing.T) {
// Check the DNAT rule.
dnatRule := ""
if ipv == iptables.IPv6 && !tc.gwMode6.natDisabled() {
dnatRule += "! -s fe80::/10 "
}
if tc.proxyPath != "" {
// No docker-proxy, so expect "hairpinMode".
dnatRule = "! -i dummybridge "
dnatRule += "! -i dummybridge "
}
dnatRule += fmt.Sprintf("-d %s -p %s -m %s --dport %d -j DNAT --to-destination %s:%d",
addrH, expPB.Proto, expPB.Proto, expPB.HostPort, addrD, expPB.Port)