diff --git a/integration/network/bridge/iptablesdoc/generated/new-daemon.md b/integration/network/bridge/iptablesdoc/generated/new-daemon.md
index 1d094dc0de..5988c1193a 100644
--- a/integration/network/bridge/iptablesdoc/generated/new-daemon.md
+++ b/integration/network/bridge/iptablesdoc/generated/new-daemon.md
@@ -112,7 +112,7 @@ bigger change than it should be._
The DOCKER chain has a single DROP rule for the bridge network, to drop any
packets routed to the network that have not originated in the network. Added by
-[defaultDrop][21].
+[setDefaultForwardRule][21].
_This means there is no dependency on the filter-FORWARD chain's default policy.
Even if it is ACCEPT, packets will be dropped unless container ports/protocols
are published._
diff --git a/integration/network/bridge/iptablesdoc/generated/usernet-portmap-natunprot.md b/integration/network/bridge/iptablesdoc/generated/usernet-portmap-natunprot.md
new file mode 100644
index 0000000000..85cbeaf047
--- /dev/null
+++ b/integration/network/bridge/iptablesdoc/generated/usernet-portmap-natunprot.md
@@ -0,0 +1,138 @@
+## Container on a nat-unprotected network, with a published port
+
+Running the daemon with the userland proxy disable then, as before, adding a network running a container with a mapped port, equivalent to:
+
+ docker network create \
+ -o com.docker.network.bridge.name=bridge1 \
+ -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
+ --subnet 192.0.2.0/24 --gateway 192.0.2.1 bridge1
+ docker run --network bridge1 -p 8080:80 --name c1 busybox
+
+The filter table is:
+
+ Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+
+ Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DOCKER-USER 0 -- * * 0.0.0.0/0 0.0.0.0/0
+ 2 0 0 ACCEPT 0 -- * * 0.0.0.0/0 0.0.0.0/0 match-set docker-ext-bridges-v4 dst ctstate RELATED,ESTABLISHED
+ 3 0 0 DOCKER-ISOLATION-STAGE-1 0 -- * * 0.0.0.0/0 0.0.0.0/0
+ 4 0 0 DOCKER 0 -- * * 0.0.0.0/0 0.0.0.0/0 match-set docker-ext-bridges-v4 dst
+ 5 0 0 ACCEPT 0 -- bridge1 !bridge1 0.0.0.0/0 0.0.0.0/0
+ 6 0 0 ACCEPT 0 -- bridge1 bridge1 0.0.0.0/0 0.0.0.0/0
+ 7 0 0 ACCEPT 0 -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
+ 8 0 0 ACCEPT 0 -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
+
+ Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+
+ Chain DOCKER (1 references)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DROP 0 -- !docker0 docker0 0.0.0.0/0 0.0.0.0/0
+ 2 0 0 ACCEPT 0 -- !bridge1 bridge1 0.0.0.0/0 0.0.0.0/0
+
+ Chain DOCKER-ISOLATION-STAGE-1 (1 references)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DOCKER-ISOLATION-STAGE-2 0 -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
+ 2 0 0 DOCKER-ISOLATION-STAGE-2 0 -- bridge1 !bridge1 0.0.0.0/0 0.0.0.0/0
+
+ Chain DOCKER-ISOLATION-STAGE-2 (2 references)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DROP 0 -- * bridge1 0.0.0.0/0 0.0.0.0/0
+ 2 0 0 DROP 0 -- * docker0 0.0.0.0/0 0.0.0.0/0
+
+ Chain DOCKER-USER (1 references)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0
+
+
+
+iptables commands
+
+ -P INPUT ACCEPT
+ -P FORWARD ACCEPT
+ -P OUTPUT ACCEPT
+ -N DOCKER
+ -N DOCKER-ISOLATION-STAGE-1
+ -N DOCKER-ISOLATION-STAGE-2
+ -N DOCKER-USER
+ -A FORWARD -j DOCKER-USER
+ -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+ -A FORWARD -j DOCKER-ISOLATION-STAGE-1
+ -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -j DOCKER
+ -A FORWARD -i bridge1 ! -o bridge1 -j ACCEPT
+ -A FORWARD -i bridge1 -o bridge1 -j ACCEPT
+ -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
+ -A FORWARD -i docker0 -o docker0 -j ACCEPT
+ -A DOCKER ! -i docker0 -o docker0 -j DROP
+ -A DOCKER ! -i bridge1 -o bridge1 -j ACCEPT
+ -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
+ -A DOCKER-ISOLATION-STAGE-1 -i bridge1 ! -o bridge1 -j DOCKER-ISOLATION-STAGE-2
+ -A DOCKER-ISOLATION-STAGE-2 -o bridge1 -j DROP
+ -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
+ -A DOCKER-USER -j RETURN
+
+
+
+
+Differences from [nat mode][400]:
+
+ - In the DOCKER chain:
+ - Where `nat` mode appended a default-DROP rule for any packets not accepted
+ by the per-port/protocol rules, `nat-unprotected` appends a default-ACCEPT
+ rule. [setDefaultForwardRule][402]
+ - The ACCEPT rule is needed in case the filter-FORWARD chain's default
+ policy is DROP.
+ - Because the default for this network is ACCEPT, there is no per-port/protocol
+ rule to ACCEPT packets for the published port `80/tcp`, [setPerPortIptables][401]
+ doesn't set it up.
+ - _If the userland proxy is enabled, it is still started._
+
+The nat table is identical to [nat mode][400].
+
+
+nat table
+
+ Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DOCKER 0 -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
+
+ Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+
+ Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 DOCKER 0 -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
+
+ Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 MASQUERADE 0 -- * !bridge1 192.0.2.0/24 0.0.0.0/0
+ 2 0 0 MASQUERADE 0 -- * !docker0 172.17.0.0/16 0.0.0.0/0
+
+ Chain DOCKER (2 references)
+ num pkts bytes target prot opt in out source destination
+ 1 0 0 RETURN 0 -- bridge1 * 0.0.0.0/0 0.0.0.0/0
+ 2 0 0 RETURN 0 -- docker0 * 0.0.0.0/0 0.0.0.0/0
+ 3 0 0 DNAT 6 -- !bridge1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:192.0.2.2:80
+
+
+ -P PREROUTING ACCEPT
+ -P INPUT ACCEPT
+ -P OUTPUT ACCEPT
+ -P POSTROUTING ACCEPT
+ -N DOCKER
+ -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
+ -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
+ -A POSTROUTING -s 192.0.2.0/24 ! -o bridge1 -j MASQUERADE
+ -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
+ -A DOCKER -i bridge1 -j RETURN
+ -A DOCKER -i docker0 -j RETURN
+ -A DOCKER ! -i bridge1 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 192.0.2.2:80
+
+
+
+
+[400]: usernet-portmap.md
+[401]: https://github.com/robmry/moby/blob/52c89d467fc5326149e4bbb8903d23589b66ff0d/libnetwork/drivers/bridge/port_mapping_linux.go#L747
+[402]: https://github.com/robmry/moby/blob/52c89d467fc5326149e4bbb8903d23589b66ff0d/libnetwork/drivers/bridge/setup_ip_tables_linux.go#L261-L266
diff --git a/integration/network/bridge/iptablesdoc/generated/usernet-portmap.md b/integration/network/bridge/iptablesdoc/generated/usernet-portmap.md
index 792b29fd84..23fad3ad76 100644
--- a/integration/network/bridge/iptablesdoc/generated/usernet-portmap.md
+++ b/integration/network/bridge/iptablesdoc/generated/usernet-portmap.md
@@ -90,8 +90,8 @@ Note that:
(unlike all the other rules so-far, which were created during driver or
network initialisation). [setPerPortForwarding][1]
- These per-port rules are inserted at the head of the chain, so that they
- appear before the network's DROP rule [defaultDrop][2] which is always
- appended to the end of the chain. In this case, because `docker0` was
+ appear before the network's DROP rule [setDefaultForwardRule][2] which is
+ always appended to the end of the chain. In this case, because `docker0` was
created before `bridge1`, the `bridge1` rules appear above and below the
`docker0` DROP rule.
diff --git a/integration/network/bridge/iptablesdoc/index.md b/integration/network/bridge/iptablesdoc/index.md
index 4a15e0b4b6..87b9292392 100644
--- a/integration/network/bridge/iptablesdoc/index.md
+++ b/integration/network/bridge/iptablesdoc/index.md
@@ -44,3 +44,4 @@ Scenarios:
- [Container on a user-defined network with inter-container communication disabled, with a published port](generated/usernet-portmap-noicc.md)
- [Container on a user-defined --internal network](generated/usernet-internal.md)
- [Container on a routed-mode network, with a published port](generated/usernet-portmap-routed.md)
+ - [Container on a nat-unprotected network, with a published port](generated/usernet-portmap-natunprot.md)
diff --git a/integration/network/bridge/iptablesdoc/iptablesdoc_linux_test.go b/integration/network/bridge/iptablesdoc/iptablesdoc_linux_test.go
index 780ec31aef..7355cba59a 100644
--- a/integration/network/bridge/iptablesdoc/iptablesdoc_linux_test.go
+++ b/integration/network/bridge/iptablesdoc/iptablesdoc_linux_test.go
@@ -133,6 +133,19 @@ var index = []section{
},
}},
},
+ {
+ name: "usernet-portmap-natunprot.md",
+ networks: []bridgeNetwork{{
+ bridge: "bridge1",
+ gwMode: "nat-unprotected",
+ containers: []ctr{
+ {
+ name: "c1",
+ portMappings: nat.PortMap{"80/tcp": {{HostPort: "8080"}}},
+ },
+ },
+ }},
+ },
}
// iptCmdType is used to look up iptCmds in the markdown (can't use an int
diff --git a/integration/network/bridge/iptablesdoc/templates/new-daemon.md b/integration/network/bridge/iptablesdoc/templates/new-daemon.md
index 8a4795b665..1c5ce2a673 100644
--- a/integration/network/bridge/iptablesdoc/templates/new-daemon.md
+++ b/integration/network/bridge/iptablesdoc/templates/new-daemon.md
@@ -65,7 +65,7 @@ bigger change than it should be._
The DOCKER chain has a single DROP rule for the bridge network, to drop any
packets routed to the network that have not originated in the network. Added by
-[defaultDrop][21].
+[setDefaultForwardRule][21].
_This means there is no dependency on the filter-FORWARD chain's default policy.
Even if it is ACCEPT, packets will be dropped unless container ports/protocols
are published._
diff --git a/integration/network/bridge/iptablesdoc/templates/usernet-portmap-natunprot.md b/integration/network/bridge/iptablesdoc/templates/usernet-portmap-natunprot.md
new file mode 100644
index 0000000000..cf48059217
--- /dev/null
+++ b/integration/network/bridge/iptablesdoc/templates/usernet-portmap-natunprot.md
@@ -0,0 +1,48 @@
+## Container on a nat-unprotected network, with a published port
+
+Running the daemon with the userland proxy disable then, as before, adding a network running a container with a mapped port, equivalent to:
+
+ docker network create \
+ -o com.docker.network.bridge.name=bridge1 \
+ -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
+ --subnet 192.0.2.0/24 --gateway 192.0.2.1 bridge1
+ docker run --network bridge1 -p 8080:80 --name c1 busybox
+
+The filter table is:
+
+ {{index . "LFilter4"}}
+
+
+iptables commands
+
+ {{index . "SFilter4"}}
+
+
+
+Differences from [nat mode][400]:
+
+ - In the DOCKER chain:
+ - Where `nat` mode appended a default-DROP rule for any packets not accepted
+ by the per-port/protocol rules, `nat-unprotected` appends a default-ACCEPT
+ rule. [setDefaultForwardRule][402]
+ - The ACCEPT rule is needed in case the filter-FORWARD chain's default
+ policy is DROP.
+ - Because the default for this network is ACCEPT, there is no per-port/protocol
+ rule to ACCEPT packets for the published port `80/tcp`, [setPerPortIptables][401]
+ doesn't set it up.
+ - _If the userland proxy is enabled, it is still started._
+
+The nat table is identical to [nat mode][400].
+
+
+nat table
+
+ {{index . "LNat4"}}
+
+ {{index . "SNat4"}}
+
+
+
+[400]: usernet-portmap.md
+[401]: https://github.com/robmry/moby/blob/52c89d467fc5326149e4bbb8903d23589b66ff0d/libnetwork/drivers/bridge/port_mapping_linux.go#L747
+[402]: https://github.com/robmry/moby/blob/52c89d467fc5326149e4bbb8903d23589b66ff0d/libnetwork/drivers/bridge/setup_ip_tables_linux.go#L261-L266
diff --git a/integration/network/bridge/iptablesdoc/templates/usernet-portmap.md b/integration/network/bridge/iptablesdoc/templates/usernet-portmap.md
index ecfee8c920..10eae93a56 100644
--- a/integration/network/bridge/iptablesdoc/templates/usernet-portmap.md
+++ b/integration/network/bridge/iptablesdoc/templates/usernet-portmap.md
@@ -31,8 +31,8 @@ Note that:
(unlike all the other rules so-far, which were created during driver or
network initialisation). [setPerPortForwarding][1]
- These per-port rules are inserted at the head of the chain, so that they
- appear before the network's DROP rule [defaultDrop][2] which is always
- appended to the end of the chain. In this case, because `docker0` was
+ appear before the network's DROP rule [setDefaultForwardRule][2] which is
+ always appended to the end of the chain. In this case, because `docker0` was
created before `bridge1`, the `bridge1` rules appear above and below the
`docker0` DROP rule.
diff --git a/integration/networking/port_mapping_linux_test.go b/integration/networking/port_mapping_linux_test.go
index 297ef0f5ee..3e2abd318d 100644
--- a/integration/networking/port_mapping_linux_test.go
+++ b/integration/networking/port_mapping_linux_test.go
@@ -10,6 +10,7 @@ import (
"os/exec"
"strconv"
"strings"
+ "syscall"
"testing"
"time"
@@ -671,10 +672,14 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
// Run http servers on ports 80 and 81, but only map/open port 80.
createNet := func(gwMode string) ctrDesc {
netName := "test-" + gwMode
+ brName := "br-" + gwMode
+ if len(brName) > syscall.IFNAMSIZ {
+ brName = brName[:syscall.IFNAMSIZ-1]
+ }
network.CreateNoError(ctx, t, c, netName,
network.WithDriver("bridge"),
network.WithIPv6(),
- network.WithOption(bridge.BridgeName, "br-"+gwMode),
+ network.WithOption(bridge.BridgeName, brName),
network.WithOption(bridge.IPv4GatewayMode, gwMode),
network.WithOption(bridge.IPv6GatewayMode, gwMode),
)
@@ -711,12 +716,19 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
)
networks := map[string]ctrDesc{
- "nat": createNet("nat"),
- "routed": createNet("routed"),
+ "nat": createNet("nat"),
+ "nat-unprotected": createNet("nat-unprotected"),
+ "routed": createNet("routed"),
}
expPingExit := map[string]int{
- "nat": pingFail,
- "routed": pingSuccess,
+ "nat": pingFail,
+ "nat-unprotected": pingSuccess,
+ "routed": pingSuccess,
+ }
+ expUnmappedPortHTTP := map[string]string{
+ "nat": httpFail,
+ "nat-unprotected": httpSuccess,
+ "routed": httpFail,
}
testPing := func(t *testing.T, cmd, addr string, expExit int) {
@@ -746,7 +758,7 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
for _, fwdPolicy := range []string{"ACCEPT", "DROP"} {
networking.SetFilterForwardPolicies(t, fwdPolicy)
t.Run(fwdPolicy, func(t *testing.T) {
- for _, gwMode := range []string{"nat", "routed"} {
+ for gwMode := range networks {
t.Run(gwMode+"/v4/ping", func(t *testing.T) {
testPing(t, "ping", networks[gwMode].ipv4, expPingExit[gwMode])
})
@@ -757,13 +769,13 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
testHttp(t, networks[gwMode].ipv4, "80", httpSuccess)
})
t.Run(gwMode+"/v4/http/81", func(t *testing.T) {
- testHttp(t, networks[gwMode].ipv4, "81", httpFail)
+ testHttp(t, networks[gwMode].ipv4, "81", expUnmappedPortHTTP[gwMode])
})
t.Run(gwMode+"/v6/http/80", func(t *testing.T) {
testHttp(t, networks[gwMode].ipv6, "80", httpSuccess)
})
t.Run(gwMode+"/v6/http/81", func(t *testing.T) {
- testHttp(t, networks[gwMode].ipv6, "81", httpFail)
+ testHttp(t, networks[gwMode].ipv6, "81", expUnmappedPortHTTP[gwMode])
})
}
})
diff --git a/libnetwork/drivers/bridge/bridge_linux.go b/libnetwork/drivers/bridge/bridge_linux.go
index 8170d8dd4a..8b3e83374a 100644
--- a/libnetwork/drivers/bridge/bridge_linux.go
+++ b/libnetwork/drivers/bridge/bridge_linux.go
@@ -167,9 +167,10 @@ type driver struct {
type gwMode string
const (
- gwModeDefault gwMode = ""
- gwModeNAT gwMode = "nat"
- gwModeRouted gwMode = "routed"
+ gwModeDefault gwMode = ""
+ gwModeNAT gwMode = "nat"
+ gwModeNATUnprot gwMode = "nat-unprotected"
+ gwModeRouted gwMode = "routed"
)
// New constructs a new bridge driver
@@ -366,6 +367,8 @@ func newGwMode(gwMode string) (gwMode, error) {
switch gwMode {
case "nat":
return gwModeNAT, nil
+ case "nat-unprotected":
+ return gwModeNATUnprot, nil
case "routed":
return gwModeRouted, nil
}
@@ -376,6 +379,10 @@ func (m gwMode) routed() bool {
return m == gwModeRouted
}
+func (m gwMode) unprotected() bool {
+ return m == gwModeNATUnprot
+}
+
func parseErr(label, value, errString string) error {
return types.InvalidParameterErrorf("failed to parse %s value: %v (%s)", label, value, errString)
}
@@ -428,6 +435,15 @@ func (n *bridgeNetwork) getNATDisabled() (ipv4, ipv6 bool) {
return n.config.GwModeIPv4.routed(), n.config.GwModeIPv6.routed()
}
+func (n *bridgeNetwork) gwMode(v iptables.IPVersion) gwMode {
+ n.Lock()
+ defer n.Unlock()
+ if v == iptables.IPv4 {
+ return n.config.GwModeIPv4
+ }
+ return n.config.GwModeIPv6
+}
+
func (n *bridgeNetwork) userlandProxyPath() string {
n.Lock()
defer n.Unlock()
diff --git a/libnetwork/drivers/bridge/port_mapping_linux.go b/libnetwork/drivers/bridge/port_mapping_linux.go
index 5bd6493713..0255c3c42c 100644
--- a/libnetwork/drivers/bridge/port_mapping_linux.go
+++ b/libnetwork/drivers/bridge/port_mapping_linux.go
@@ -776,8 +776,11 @@ func (n *bridgeNetwork) setPerPortIptables(b portBinding, enable bool) error {
if err := setPerPortNAT(b, v, proxyPath, bridgeName, enable); err != nil {
return err
}
- if err := setPerPortForwarding(b, v, bridgeName, enable); err != nil {
- return err
+
+ if !n.gwMode(v).unprotected() {
+ if err := setPerPortForwarding(b, v, bridgeName, enable); err != nil {
+ return err
+ }
}
return nil
}
diff --git a/libnetwork/drivers/bridge/setup_ip_tables_linux.go b/libnetwork/drivers/bridge/setup_ip_tables_linux.go
index cb3d850017..17e0cc643e 100644
--- a/libnetwork/drivers/bridge/setup_ip_tables_linux.go
+++ b/libnetwork/drivers/bridge/setup_ip_tables_linux.go
@@ -166,14 +166,14 @@ func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *
if config.Internal {
if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil {
- return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
+ return fmt.Errorf("Failed to Setup IP tables: %w", err)
}
n.registerIptCleanFunc(func() error {
return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false)
})
} else {
if err = setupIPTablesInternal(ipVersion, config, maskedAddr, hairpinMode, true); err != nil {
- return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
+ return fmt.Errorf("Failed to Setup IP tables: %w", err)
}
n.registerIptCleanFunc(func() error {
return setupIPTablesInternal(ipVersion, config, maskedAddr, hairpinMode, false)
@@ -181,28 +181,25 @@ func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *
natChain, filterChain, _, _, err := n.getDriverChains(ipVersion)
if err != nil {
- return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
+ return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %w", err)
}
err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
if err != nil {
- return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
+ return fmt.Errorf("Failed to program NAT chain: %w", err)
}
err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
if err != nil {
- return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
+ return fmt.Errorf("Failed to program FILTER chain: %w", err)
}
n.registerIptCleanFunc(func() error {
return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
})
- if err := defaultDrop(ipVersion, config.BridgeName, true); err != nil {
- return fmt.Errorf("failed to add default-drop rule: %s", err.Error())
+ if err := n.setDefaultForwardRule(ipVersion, config.BridgeName); err != nil {
+ return err
}
- n.registerIptCleanFunc(func() error {
- return defaultDrop(ipVersion, config.BridgeName, false)
- })
cidr, _ := maskedAddr.Mask.Size()
if cidr == 0 {
@@ -252,15 +249,37 @@ func setICMP(ipv iptables.IPVersion, bridgeName string, enable bool) error {
return appendOrDelChainRule(icmpRule, "ICMP", enable)
}
-// Append to the filter table's DOCKER chain (the default DROP rule must follow
-// per-port ACCEPT rules, which will be inserted at the top of the chain).
-func defaultDrop(ipv iptables.IPVersion, bridgeName string, enable bool) error {
- dropRule := iptRule{ipv: ipv, table: iptables.Filter, chain: DockerChain, args: []string{
+func (n *bridgeNetwork) setDefaultForwardRule(
+ ipVersion iptables.IPVersion,
+ bridgeName string,
+) error {
+ // Normally, DROP anything that hasn't been ACCEPTed by a per-port/protocol
+ // rule. This prevents direct access to un-mapped ports from remote hosts
+ // that can route directly to the container's address (by setting up a
+ // route via the host's address).
+ action := "DROP"
+ if n.gwMode(ipVersion).unprotected() {
+ // If the user really wants to allow all access from the wider network,
+ // explicitly ACCEPT anything so that the filter-FORWARD chain's
+ // default policy can't interfere.
+ action = "ACCEPT"
+ }
+
+ rule := iptRule{ipv: ipVersion, table: iptables.Filter, chain: DockerChain, args: []string{
"!", "-i", bridgeName,
"-o", bridgeName,
- "-j", "DROP",
+ "-j", action,
}}
- return appendOrDelChainRule(dropRule, "DEFAULT DROP", enable)
+
+ // Append to the filter table's DOCKER chain (the default rule must follow
+ // per-port ACCEPT rules, which will be inserted at the top of the chain).
+ if err := appendOrDelChainRule(rule, "DEFAULT FWD", true); err != nil {
+ return fmt.Errorf("failed to add default-drop rule: %w", err)
+ }
+ n.registerIptCleanFunc(func() error {
+ return appendOrDelChainRule(rule, "DEFAULT FWD", false)
+ })
+ return nil
}
type iptRule struct {
@@ -387,7 +406,7 @@ func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
fn = rule.Insert
}
if err := fn(); err != nil {
- return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
+ return fmt.Errorf("Unable to %s %s rule: %w", operation, ruleDescr, err)
}
return nil
}
@@ -400,7 +419,7 @@ func appendOrDelChainRule(rule iptRule, ruleDescr string, append bool) error {
fn = rule.Append
}
if err := fn(); err != nil {
- return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
+ return fmt.Errorf("Unable to %s %s rule: %w", operation, ruleDescr, err)
}
return nil
}
@@ -413,12 +432,12 @@ func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bo
if !iccEnable {
acceptRule.Delete()
if err := dropRule.Append(); err != nil {
- return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
+ return fmt.Errorf("Unable to prevent intercontainer communication: %w", err)
}
} else {
dropRule.Delete()
if err := acceptRule.Insert(); err != nil {
- return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
+ return fmt.Errorf("Unable to allow intercontainer communication: %w", err)
}
}
} else {