mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Support for com.docker.network.host_ipv4 driver label
This commit allows a user to specify a Host IP via the com.docker.network.host_ipv4 label which is used as the Source IP during SNAT for bridge networks . The use case is for hosts with multiple interfaces and this label can dictate which IP will be used as Source IP for North-South traffic In the absence of this label, MASQUERADE is used which picks the Source IP based on Next Hop from the Route Table Addresses: https://github.com/moby/moby/issues/30053 Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>
This commit is contained in:
parent
141b53c77a
commit
8c8a25d524
5 changed files with 38 additions and 5 deletions
|
@ -71,6 +71,7 @@ type networkConfiguration struct {
|
||||||
Mtu int
|
Mtu int
|
||||||
DefaultBindingIP net.IP
|
DefaultBindingIP net.IP
|
||||||
DefaultBridge bool
|
DefaultBridge bool
|
||||||
|
HostIP net.IP
|
||||||
ContainerIfacePrefix string
|
ContainerIfacePrefix string
|
||||||
// Internal fields set after ipam data parsing
|
// Internal fields set after ipam data parsing
|
||||||
AddressIPv4 *net.IPNet
|
AddressIPv4 *net.IPNet
|
||||||
|
@ -253,6 +254,10 @@ func (c *networkConfiguration) fromLabels(labels map[string]string) error {
|
||||||
}
|
}
|
||||||
case netlabel.ContainerIfacePrefix:
|
case netlabel.ContainerIfacePrefix:
|
||||||
c.ContainerIfacePrefix = value
|
c.ContainerIfacePrefix = value
|
||||||
|
case netlabel.HostIP:
|
||||||
|
if c.HostIP = net.ParseIP(value); c.HostIP == nil {
|
||||||
|
return parseErr(label, value, "nil ip")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) {
|
||||||
nMap["Internal"] = ncfg.Internal
|
nMap["Internal"] = ncfg.Internal
|
||||||
nMap["DefaultBridge"] = ncfg.DefaultBridge
|
nMap["DefaultBridge"] = ncfg.DefaultBridge
|
||||||
nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String()
|
nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String()
|
||||||
|
nMap["HostIP"] = ncfg.HostIP.String()
|
||||||
nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String()
|
nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String()
|
||||||
nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String()
|
nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String()
|
||||||
nMap["ContainerIfacePrefix"] = ncfg.ContainerIfacePrefix
|
nMap["ContainerIfacePrefix"] = ncfg.ContainerIfacePrefix
|
||||||
|
@ -183,6 +184,10 @@ func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error {
|
||||||
ncfg.ContainerIfacePrefix = v.(string)
|
ncfg.ContainerIfacePrefix = v.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := nMap["HostIP"]; ok {
|
||||||
|
ncfg.HostIP = net.ParseIP(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
ncfg.DefaultBridge = nMap["DefaultBridge"].(bool)
|
ncfg.DefaultBridge = nMap["DefaultBridge"].(bool)
|
||||||
ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string))
|
ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string))
|
||||||
ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string))
|
ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string))
|
||||||
|
|
|
@ -264,6 +264,7 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bndIPs := "127.0.0.1"
|
bndIPs := "127.0.0.1"
|
||||||
|
testHostIP := "1.2.3.4"
|
||||||
nwV6s := "2001:db8:2600:2700:2800::/80"
|
nwV6s := "2001:db8:2600:2700:2800::/80"
|
||||||
gwV6s := "2001:db8:2600:2700:2800::25/80"
|
gwV6s := "2001:db8:2600:2700:2800::25/80"
|
||||||
nwV6, _ := types.ParseCIDR(nwV6s)
|
nwV6, _ := types.ParseCIDR(nwV6s)
|
||||||
|
@ -275,6 +276,7 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
EnableICC: "true",
|
EnableICC: "true",
|
||||||
EnableIPMasquerade: "true",
|
EnableIPMasquerade: "true",
|
||||||
DefaultBindingIP: bndIPs,
|
DefaultBindingIP: bndIPs,
|
||||||
|
netlabel.HostIP: testHostIP,
|
||||||
}
|
}
|
||||||
|
|
||||||
netOption := make(map[string]interface{})
|
netOption := make(map[string]interface{})
|
||||||
|
@ -322,6 +324,11 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
|
t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostIP := net.ParseIP(testHostIP)
|
||||||
|
if !hostIP.Equal(nw.config.HostIP) {
|
||||||
|
t.Fatalf("Unexpected: %v", nw.config.HostIP)
|
||||||
|
}
|
||||||
|
|
||||||
if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
|
if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
|
||||||
t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
|
t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,11 +121,11 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
|
||||||
return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, false)
|
return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, false)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
||||||
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
||||||
}
|
}
|
||||||
n.registerIptCleanFunc(func() error {
|
n.registerIptCleanFunc(func() error {
|
||||||
return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
|
return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
|
||||||
})
|
})
|
||||||
natChain, filterChain, _, _, err := n.getDriverChains()
|
natChain, filterChain, _, _, err := n.getDriverChains()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,15 +166,28 @@ type iptRule struct {
|
||||||
args []string
|
args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error {
|
func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
address = addr.String()
|
address = addr.String()
|
||||||
natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}}
|
|
||||||
hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
|
|
||||||
skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
|
skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
|
||||||
outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
|
outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
|
||||||
|
natArgs []string
|
||||||
|
hpNatArgs []string
|
||||||
)
|
)
|
||||||
|
// if hostIP is set use this address as the src-ip during SNAT
|
||||||
|
if hostIP != nil {
|
||||||
|
hostAddr := hostIP.String()
|
||||||
|
natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
||||||
|
hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
||||||
|
// Else use MASQUERADE which picks the src-ip based on NH from the route table
|
||||||
|
} else {
|
||||||
|
natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
|
||||||
|
hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}
|
||||||
|
}
|
||||||
|
|
||||||
|
natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs}
|
||||||
|
hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs}
|
||||||
|
|
||||||
// Set NAT.
|
// Set NAT.
|
||||||
if ipmasq {
|
if ipmasq {
|
||||||
|
|
|
@ -53,6 +53,9 @@ const (
|
||||||
|
|
||||||
// ContainerIfacePrefix can be used to override the interface prefix used inside the container
|
// ContainerIfacePrefix can be used to override the interface prefix used inside the container
|
||||||
ContainerIfacePrefix = Prefix + ".container_iface_prefix"
|
ContainerIfacePrefix = Prefix + ".container_iface_prefix"
|
||||||
|
|
||||||
|
// HostIP is the Source-IP Address used to SNAT container traffic
|
||||||
|
HostIP = Prefix + ".host_ipv4"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
Loading…
Add table
Reference in a new issue