diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index dbe3a3e919..7b2bdebd20 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -68,6 +68,7 @@ type networkConfiguration struct { DefaultGatewayIPv6 net.IP dbIndex uint64 dbExists bool + Internal bool } // endpointConfiguration represents the user specified configuration for the sandbox endpoint @@ -280,16 +281,25 @@ func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) { // from each of the other networks func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) error { n.Lock() - thisIface := n.config.BridgeName + thisConfig := n.config n.Unlock() + if thisConfig.Internal { + return nil + } + // Install the rules to isolate this networks against each of the other networks for _, o := range others { o.Lock() - otherIface := o.config.BridgeName + otherConfig := o.config o.Unlock() - if thisIface != otherIface { - if err := setINC(thisIface, otherIface, enable); err != nil { + + if otherConfig.Internal { + continue + } + + if thisConfig.BridgeName != otherConfig.BridgeName { + if err := setINC(thisConfig.BridgeName, otherConfig.BridgeName, enable); err != nil { return err } } @@ -483,7 +493,7 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati if val, ok := option[netlabel.Internal]; ok { if internal, ok := val.(bool); ok && internal { - return nil, &driverapi.ErrNotImplemented{} + config.Internal = true } } diff --git a/libnetwork/drivers/bridge/setup_ip_tables.go b/libnetwork/drivers/bridge/setup_ip_tables.go index 60329c8f43..1d523d6c2c 100644 --- a/libnetwork/drivers/bridge/setup_ip_tables.go +++ b/libnetwork/drivers/bridge/setup_ip_tables.go @@ -82,38 +82,46 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt IP: ipnet.IP.Mask(ipnet.Mask), Mask: ipnet.Mask, } - if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil { - return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) - } - n.registerIptCleanFunc(func() error { - return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) - }) + if config.Internal { + if err = setupInternalNetworkRules(config.BridgeName, maskedAddrv4, true); err != nil { + return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) + } + n.registerIptCleanFunc(func() error { + return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, false) + }) + } else { + if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil { + return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) + } + n.registerIptCleanFunc(func() error { + return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) + }) + natChain, filterChain, _, err := n.getDriverChains() + if err != nil { + return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error()) + } - natChain, filterChain, _, err := n.getDriverChains() - if err != nil { - return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error()) - } + err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true) + if err != nil { + return fmt.Errorf("Failed to program NAT chain: %s", err.Error()) + } - err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true) - if err != nil { - return fmt.Errorf("Failed to program NAT chain: %s", err.Error()) - } + err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) + if err != nil { + return fmt.Errorf("Failed to program FILTER chain: %s", err.Error()) + } - err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) - if err != nil { - return fmt.Errorf("Failed to program FILTER chain: %s", err.Error()) + n.registerIptCleanFunc(func() error { + return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) + }) + + n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName()) } if err := ensureJumpRule("FORWARD", IsolationChain); err != nil { return err } - n.registerIptCleanFunc(func() error { - return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) - }) - - n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName()) - return nil } @@ -312,12 +320,26 @@ func ensureJumpRule(fromChain, toChain string) error { func removeIPChains() { for _, chainInfo := range []iptables.ChainInfo{ - iptables.ChainInfo{Name: DockerChain, Table: iptables.Nat}, - iptables.ChainInfo{Name: DockerChain, Table: iptables.Filter}, - iptables.ChainInfo{Name: IsolationChain, Table: iptables.Filter}, + {Name: DockerChain, Table: iptables.Nat}, + {Name: DockerChain, Table: iptables.Filter}, + {Name: IsolationChain, Table: iptables.Filter}, } { if err := chainInfo.Remove(); err != nil { logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err) } } } + +func setupInternalNetworkRules(bridgeIface string, addr net.Addr, insert bool) error { + var ( + inDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}} + outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}} + ) + if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil { + return err + } + if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil { + return err + } + return nil +} diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index a4e810c77f..3be0559fb7 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -2352,10 +2352,3 @@ func TestParallel2(t *testing.T) { func TestParallel3(t *testing.T) { runParallelTests(t, 3) } - -func TestNetworkInternal(t *testing.T) { - _, err := controller.NewNetwork(bridgeNetType, "testnetworkinternal", libnetwork.NetworkOptionInternalNetwork()) - if err == nil || err.Error() != (&driverapi.ErrNotImplemented{}).Error() { - t.Fatal("bridge network can't be internal") - } -} diff --git a/libnetwork/test/integration/dnet/bridge.bats b/libnetwork/test/integration/dnet/bridge.bats index a3a852ce54..71b40c9a2d 100644 --- a/libnetwork/test/integration/dnet/bridge.bats +++ b/libnetwork/test/integration/dnet/bridge.bats @@ -20,8 +20,10 @@ function test_single_network_connectivity() { # Now test connectivity between all the containers using service names for i in `seq ${start} ${end}`; do - runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \ - "ping -c 1 www.google.com" + if [ "${nw_name}" != "internal" ]; then + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \ + "ping -c 1 www.google.com" + fi for j in `seq ${start} ${end}`; do if [ "$i" -eq "$j" ]; then @@ -250,6 +252,7 @@ function test_single_network_connectivity() { dnet_cmd $(inst_id2port 1) network rm br1 } + @test "Test bridge network global alias support" { skip_for_circleci dnet_cmd $(inst_id2port 1) network create -d bridge br1 @@ -279,3 +282,24 @@ function test_single_network_connectivity() { dnet_cmd $(inst_id2port 1) network rm br1 } + +@test "Test bridge network internal network" { + skip_for_circleci + + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d bridge --internal internal + dnet_cmd $(inst_id2port 1) container create container_1 + # connects to internal network, confirm it can't conmunicate with outside world + net_connect 1 container_1 internal + run runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com" + [[ "$output" == *"1 packets transmitted, 0 packets received, 100% packet loss"* ]] + net_disconnect 1 container_1 internal + # connects to bridge network, confirm it can conmunicate with outside world + net_connect 1 container_1 bridge + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com" + net_disconnect 1 container_1 bridge + dnet_cmd $(inst_id2port 1) container rm container_1 + # test conmunications within internal network + test_single_network_connectivity internal 3 + dnet_cmd $(inst_id2port 1) network rm internal +}