mirror of
https://github.com/moby/moby.git
synced 2026-01-12 03:01:38 +00:00
Factor out top-level iptables setup into its own object
Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
@@ -67,6 +67,12 @@ type configuration struct {
|
||||
Rootless bool
|
||||
}
|
||||
|
||||
type firewaller struct {
|
||||
IPv4 bool
|
||||
IPv6 bool
|
||||
Hairpin bool
|
||||
}
|
||||
|
||||
// networkConfiguration for network specific configuration
|
||||
type networkConfiguration struct {
|
||||
ID string
|
||||
@@ -159,6 +165,7 @@ type driver struct {
|
||||
nlh nlwrap.Handle
|
||||
portDriverClient portDriverClient
|
||||
configNetwork sync.Mutex
|
||||
firewaller firewaller
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
@@ -403,16 +410,13 @@ func (n *bridgeNetwork) newIptablesNetwork() (*iptablesNetwork, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newIptablesNetwork(networkConfig{
|
||||
return n.driver.firewaller.NewNetwork(networkConfig{
|
||||
IfName: n.config.BridgeName,
|
||||
Internal: n.config.Internal,
|
||||
ICC: n.config.EnableICC,
|
||||
Masquerade: n.config.EnableIPMasquerade,
|
||||
Config4: config4,
|
||||
Config6: config6,
|
||||
Hairpin: !n.driver.config.EnableUserlandProxy || n.driver.config.UserlandProxyPath == "",
|
||||
Enable4: n.driver.config.EnableIPTables,
|
||||
Enable6: n.driver.config.EnableIP6Tables,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -503,50 +507,12 @@ func (d *driver) configure(option map[string]interface{}) error {
|
||||
return errdefs.InvalidParameter(fmt.Errorf("invalid configuration type (%T) passed", opt))
|
||||
}
|
||||
|
||||
if config.EnableIPTables {
|
||||
removeIPChains(iptables.IPv4)
|
||||
|
||||
if err := setupIPChains(config, iptables.IPv4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure on firewall reload, first thing being re-played is chains creation
|
||||
iptables.OnReloaded(func() {
|
||||
log.G(context.TODO()).Debugf("Recreating iptables chains on firewall reload")
|
||||
if err := setupIPChains(config, iptables.IPv4); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Error("Error reloading iptables chains")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if config.EnableIP6Tables {
|
||||
if err := modprobe.LoadModules(context.TODO(), func() error {
|
||||
iptable := iptables.GetIptable(iptables.IPv6)
|
||||
_, err := iptable.Raw("-t", "filter", "-n", "-L", "FORWARD")
|
||||
return err
|
||||
}, "ip6_tables"); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Debug("Loading ip6_tables")
|
||||
}
|
||||
|
||||
removeIPChains(iptables.IPv6)
|
||||
|
||||
if err := setupIPChains(config, iptables.IPv6); err != nil {
|
||||
// If the chains couldn't be set up, it's probably because the kernel has no IPv6
|
||||
// support, or it doesn't have module ip6_tables loaded. It won't be possible to
|
||||
// create IPv6 networks without enabling ip6_tables in the kernel, or disabling
|
||||
// ip6tables in the daemon config. But, allow the daemon to start because IPv4
|
||||
// will work. So, log the problem, and continue.
|
||||
log.G(context.TODO()).WithError(err).Warn("ip6tables is enabled, but cannot set up ip6tables chains")
|
||||
} else {
|
||||
// Make sure on firewall reload, first thing being re-played is chains creation
|
||||
iptables.OnReloaded(func() {
|
||||
log.G(context.TODO()).Debugf("Recreating ip6tables chains on firewall reload")
|
||||
if err := setupIPChains(config, iptables.IPv6); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Error("Error reloading ip6tables chains")
|
||||
}
|
||||
})
|
||||
}
|
||||
d.firewaller = firewaller{
|
||||
IPv4: config.EnableIPTables,
|
||||
IPv6: config.EnableIP6Tables,
|
||||
Hairpin: !config.EnableUserlandProxy || config.UserlandProxyPath == "",
|
||||
}
|
||||
d.firewaller.init()
|
||||
iptables.OnReloaded(d.handleFirewalldReload)
|
||||
|
||||
var pdc portDriverClient
|
||||
@@ -566,6 +532,56 @@ func (d *driver) configure(option map[string]interface{}) error {
|
||||
return d.initStore()
|
||||
}
|
||||
|
||||
func (fw *firewaller) init() error {
|
||||
if fw.IPv4 {
|
||||
removeIPChains(iptables.IPv4)
|
||||
|
||||
if err := setupIPChains(iptables.IPv4, fw.Hairpin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure on firewall reload, first thing being re-played is chains creation
|
||||
iptables.OnReloaded(func() {
|
||||
log.G(context.TODO()).Debugf("Recreating iptables chains on firewall reload")
|
||||
if err := setupIPChains(iptables.IPv4, fw.Hairpin); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Error("Error reloading iptables chains")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if fw.IPv6 {
|
||||
if err := modprobe.LoadModules(context.TODO(), func() error {
|
||||
iptable := iptables.GetIptable(iptables.IPv6)
|
||||
_, err := iptable.Raw("-t", "filter", "-n", "-L", "FORWARD")
|
||||
return err
|
||||
}, "ip6_tables"); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Debug("Loading ip6_tables")
|
||||
}
|
||||
|
||||
removeIPChains(iptables.IPv6)
|
||||
|
||||
err := setupIPChains(iptables.IPv6, fw.Hairpin)
|
||||
if err != nil {
|
||||
// If the chains couldn't be set up, it's probably because the kernel has no IPv6
|
||||
// support, or it doesn't have module ip6_tables loaded. It won't be possible to
|
||||
// create IPv6 networks without enabling ip6_tables in the kernel, or disabling
|
||||
// ip6tables in the daemon config. But, allow the daemon to start because IPv4
|
||||
// will work. So, log the problem, and continue.
|
||||
log.G(context.TODO()).WithError(err).Warn("ip6tables is enabled, but cannot set up ip6tables chains")
|
||||
} else {
|
||||
// Make sure on firewall reload, first thing being re-played is chains creation
|
||||
iptables.OnReloaded(func() {
|
||||
log.G(context.TODO()).Debugf("Recreating ip6tables chains on firewall reload")
|
||||
if err := setupIPChains(iptables.IPv6, fw.Hairpin); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Error("Error reloading ip6tables chains")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) getNetwork(id string) (*bridgeNetwork, error) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
@@ -1260,13 +1260,9 @@ func TestCleanupIptableRules(t *testing.T) {
|
||||
}
|
||||
|
||||
ipVersions := []iptables.IPVersion{iptables.IPv4, iptables.IPv6}
|
||||
configs := map[iptables.IPVersion]configuration{
|
||||
iptables.IPv4: {EnableIPTables: true},
|
||||
iptables.IPv6: {EnableIP6Tables: true},
|
||||
}
|
||||
|
||||
for _, version := range ipVersions {
|
||||
err := setupIPChains(configs[version], version)
|
||||
err := setupIPChains(version, true)
|
||||
assert.NilError(t, err, "version:%s", version)
|
||||
|
||||
iptable := iptables.GetIptable(version)
|
||||
|
||||
@@ -792,11 +792,11 @@ func (n *iptablesNetwork) modPorts(ctx context.Context, pbs []types.PortBinding,
|
||||
|
||||
func (n *iptablesNetwork) setPerPortIptables(ctx context.Context, b types.PortBinding, enable bool) error {
|
||||
v := iptables.IPv4
|
||||
enabled := n.Enable4
|
||||
enabled := n.fw.IPv4
|
||||
config := n.Config4
|
||||
if b.IP.To4() == nil {
|
||||
v = iptables.IPv6
|
||||
enabled = n.Enable6
|
||||
enabled = n.fw.IPv6
|
||||
config = n.Config6
|
||||
}
|
||||
|
||||
@@ -851,7 +851,7 @@ func (n *iptablesNetwork) setPerPortNAT(ipv iptables.IPVersion, b types.PortBind
|
||||
"-j", "DNAT",
|
||||
"--to-destination", net.JoinHostPort(b.IP.String(), strconv.Itoa(int(b.Port))),
|
||||
}
|
||||
if !n.Hairpin {
|
||||
if !n.fw.Hairpin {
|
||||
args = append(args, "!", "-i", n.IfName)
|
||||
}
|
||||
if ipv == iptables.IPv6 {
|
||||
@@ -869,7 +869,7 @@ func (n *iptablesNetwork) setPerPortNAT(ipv iptables.IPVersion, b types.PortBind
|
||||
"--dport", strconv.Itoa(int(b.Port)),
|
||||
"-j", "MASQUERADE",
|
||||
}}
|
||||
if err := appendOrDelChainRule(rule, "MASQUERADE", n.Hairpin && enable); err != nil {
|
||||
if err := appendOrDelChainRule(rule, "MASQUERADE", n.fw.Hairpin && enable); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -42,15 +42,7 @@ const (
|
||||
// Can be modified by tests.
|
||||
var wslinfoPath = "/usr/bin/wslinfo"
|
||||
|
||||
func setupIPChains(config configuration, version iptables.IPVersion) (retErr error) {
|
||||
// Sanity check.
|
||||
if version == iptables.IPv4 && !config.EnableIPTables {
|
||||
return errors.New("cannot create new chains, iptables is disabled")
|
||||
}
|
||||
if version == iptables.IPv6 && !config.EnableIP6Tables {
|
||||
return errors.New("cannot create new chains, ip6tables is disabled")
|
||||
}
|
||||
|
||||
func setupIPChains(version iptables.IPVersion, hairpin bool) (retErr error) {
|
||||
iptable := iptables.GetIptable(version)
|
||||
|
||||
_, err := iptable.NewChain(DockerChain, iptables.Nat)
|
||||
@@ -137,12 +129,12 @@ func setupIPChains(config configuration, version iptables.IPVersion) (retErr err
|
||||
}
|
||||
}()
|
||||
|
||||
if err := addNATJumpRules(version, !config.EnableUserlandProxy, true); err != nil {
|
||||
if err := addNATJumpRules(version, hairpin, true); err != nil {
|
||||
return fmt.Errorf("failed to add jump rules to %s NAT table: %w", version, err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := addNATJumpRules(version, !config.EnableUserlandProxy, false); err != nil {
|
||||
if err := addNATJumpRules(version, hairpin, false); err != nil {
|
||||
log.G(context.TODO()).Warnf("failed on removing jump rules from %s NAT table: %v", version, err)
|
||||
}
|
||||
}
|
||||
@@ -164,7 +156,7 @@ func setupIPChains(config configuration, version iptables.IPVersion) (retErr err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mirroredWSL2Workaround(config, version); err != nil {
|
||||
if err := mirroredWSL2Workaround(version, hairpin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -206,23 +198,24 @@ type networkConfig struct {
|
||||
Masquerade bool
|
||||
Config4 networkConfigFam
|
||||
Config6 networkConfigFam
|
||||
Hairpin bool
|
||||
Enable4 bool
|
||||
Enable6 bool
|
||||
}
|
||||
|
||||
type iptablesNetwork struct {
|
||||
networkConfig
|
||||
fw *firewaller
|
||||
cleanFuncs iptablesCleanFuncs
|
||||
}
|
||||
|
||||
func newIptablesNetwork(nc networkConfig) (_ *iptablesNetwork, retErr error) {
|
||||
func (fw *firewaller) NewNetwork(nc networkConfig) (_ *iptablesNetwork, retErr error) {
|
||||
n := &iptablesNetwork{
|
||||
fw: fw,
|
||||
networkConfig: nc,
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
n.delNetworkLevelRules()
|
||||
if err := n.delNetworkLevelRules(); err != nil {
|
||||
log.G(context.TODO()).WithError(err).Warnf("Failed to delete network level rules following earlier error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -233,12 +226,12 @@ func newIptablesNetwork(nc networkConfig) (_ *iptablesNetwork, retErr error) {
|
||||
}
|
||||
|
||||
func (n *iptablesNetwork) reapplyNetworkLevelRules() error {
|
||||
if n.Enable4 {
|
||||
if n.fw.IPv4 {
|
||||
if err := n.configure(iptables.IPv4, n.Config4); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if n.Enable6 {
|
||||
if n.fw.IPv6 {
|
||||
if err := n.configure(iptables.IPv6, n.Config6); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -510,7 +503,7 @@ func (n *iptablesNetwork) setupNonInternalNetworkRules(ipVer iptables.IPVersion,
|
||||
// enable access to ports published by containers in the same network. But, the INC rules
|
||||
// will block access to that published port from containers in other networks. (However,
|
||||
// users may add a rule to DOCKER-USER to work around the INC rules if needed.)
|
||||
if !n.Hairpin {
|
||||
if !n.fw.Hairpin {
|
||||
skipDNAT := iptables.Rule{IPVer: ipVer, Table: iptables.Nat, Chain: DockerChain, Args: []string{
|
||||
"-i", n.IfName,
|
||||
"-j", "RETURN",
|
||||
@@ -523,7 +516,7 @@ func (n *iptablesNetwork) setupNonInternalNetworkRules(ipVer iptables.IPVersion,
|
||||
|
||||
// In hairpin mode, masquerade traffic from localhost. If hairpin is disabled or if we're tearing down
|
||||
// that bridge, make sure the iptables rule isn't lying around.
|
||||
if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable && n.Hairpin); err != nil {
|
||||
if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable && n.fw.Hairpin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -859,12 +852,12 @@ func clearConntrackEntries(nlh nlwrap.Handle, ep *bridgeEndpoint) {
|
||||
// arriving from any other bridge network. Similarly, this function adds (or
|
||||
// removes) a rule to RETURN early for packets delivered via loopback0 with
|
||||
// destination 127.0.0.0/8.
|
||||
func mirroredWSL2Workaround(config configuration, ipv iptables.IPVersion) error {
|
||||
func mirroredWSL2Workaround(ipv iptables.IPVersion, hairpin bool) error {
|
||||
// WSL2 does not (currently) support Windows<->Linux communication via ::1.
|
||||
if ipv != iptables.IPv4 {
|
||||
return nil
|
||||
}
|
||||
return programChainRule(mirroredWSL2Rule(), "WSL2 loopback", insertMirroredWSL2Rule(config))
|
||||
return programChainRule(mirroredWSL2Rule(), "WSL2 loopback", insertMirroredWSL2Rule(hairpin))
|
||||
}
|
||||
|
||||
// insertMirroredWSL2Rule returns true if the NAT rule for mirrored WSL2 workaround
|
||||
@@ -875,8 +868,8 @@ func mirroredWSL2Workaround(config configuration, ipv iptables.IPVersion) error
|
||||
// running - no workaround is needed, the normal DNAT/masquerading works.
|
||||
// - and, the host Linux appears to be running under Windows WSL2 with mirrored
|
||||
// mode networking.
|
||||
func insertMirroredWSL2Rule(config configuration) bool {
|
||||
if !config.EnableUserlandProxy || config.UserlandProxyPath == "" {
|
||||
func insertMirroredWSL2Rule(hairpin bool) bool {
|
||||
if hairpin {
|
||||
return false
|
||||
}
|
||||
return isRunningUnderWSL2MirroredMode()
|
||||
|
||||
@@ -157,11 +157,11 @@ func assertIPTableChainProgramming(rule iptables.Rule, descr string, t *testing.
|
||||
func assertChainConfig(d *driver, t *testing.T) {
|
||||
var err error
|
||||
|
||||
err = setupIPChains(d.config, iptables.IPv4)
|
||||
err = setupIPChains(iptables.IPv4, !d.config.EnableUserlandProxy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
if d.config.EnableIP6Tables {
|
||||
err = setupIPChains(d.config, iptables.IPv6)
|
||||
err = setupIPChains(iptables.IPv6, !d.config.EnableUserlandProxy)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
@@ -462,7 +462,7 @@ func TestMirroredWSL2Workaround(t *testing.T) {
|
||||
config.UserlandProxyPath = "some-proxy"
|
||||
config.EnableUserlandProxy = true
|
||||
}
|
||||
err := setupIPChains(config, iptables.IPv4)
|
||||
err := setupIPChains(iptables.IPv4, !tc.userlandProxy)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(mirroredWSL2Rule().Exists(), tc.expLoopback0Rule))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user