mirror of
https://github.com/moby/moby.git
synced 2026-08-03 14:41:03 +00:00
Merge pull request #48597 from robmry/nat-unprotected
Add gateway mode "nat-unprotected"
This commit is contained in:
@@ -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._
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
<details>
|
||||
<summary>iptables commands</summary>
|
||||
|
||||
-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
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
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].
|
||||
|
||||
<details>
|
||||
<summary>nat table</summary>
|
||||
|
||||
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
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
[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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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._
|
||||
|
||||
@@ -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"}}
|
||||
|
||||
<details>
|
||||
<summary>iptables commands</summary>
|
||||
|
||||
{{index . "SFilter4"}}
|
||||
|
||||
</details>
|
||||
|
||||
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].
|
||||
|
||||
<details>
|
||||
<summary>nat table</summary>
|
||||
|
||||
{{index . "LNat4"}}
|
||||
|
||||
{{index . "SNat4"}}
|
||||
|
||||
</details>
|
||||
|
||||
[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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user