github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/drivers/bridge/setup_ip_tables.go (about)

     1  package bridge
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  
     8  	"github.com/docker/libnetwork/iptables"
     9  	"github.com/sirupsen/logrus"
    10  	"github.com/vishvananda/netlink"
    11  )
    12  
    13  // DockerChain: DOCKER iptable chain name
    14  const (
    15  	DockerChain = "DOCKER"
    16  	// Isolation between bridge networks is achieved in two stages by means
    17  	// of the following two chains in the filter table. The first chain matches
    18  	// on the source interface being a bridge network's bridge and the
    19  	// destination being a different interface. A positive match leads to the
    20  	// second isolation chain. No match returns to the parent chain. The second
    21  	// isolation chain matches on destination interface being a bridge network's
    22  	// bridge. A positive match identifies a packet originated from one bridge
    23  	// network's bridge destined to another bridge network's bridge and will
    24  	// result in the packet being dropped. No match returns to the parent chain.
    25  
    26  	IsolationChain1 = "DOCKER-ISOLATION-STAGE-1"
    27  	IsolationChain2 = "DOCKER-ISOLATION-STAGE-2"
    28  )
    29  
    30  func setupIPChains(config *configuration, version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
    31  	// Sanity check.
    32  	if config.EnableIPTables == false {
    33  		return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled")
    34  	}
    35  
    36  	hairpinMode := !config.EnableUserlandProxy
    37  
    38  	iptable := iptables.GetIptable(version)
    39  
    40  	natChain, err := iptable.NewChain(DockerChain, iptables.Nat, hairpinMode)
    41  	if err != nil {
    42  		return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err)
    43  	}
    44  	defer func() {
    45  		if err != nil {
    46  			if err := iptable.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
    47  				logrus.Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err)
    48  			}
    49  		}
    50  	}()
    51  
    52  	filterChain, err := iptable.NewChain(DockerChain, iptables.Filter, false)
    53  	if err != nil {
    54  		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err)
    55  	}
    56  	defer func() {
    57  		if err != nil {
    58  			if err := iptable.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
    59  				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err)
    60  			}
    61  		}
    62  	}()
    63  
    64  	isolationChain1, err := iptable.NewChain(IsolationChain1, iptables.Filter, false)
    65  	if err != nil {
    66  		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
    67  	}
    68  	defer func() {
    69  		if err != nil {
    70  			if err := iptable.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil {
    71  				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err)
    72  			}
    73  		}
    74  	}()
    75  
    76  	isolationChain2, err := iptable.NewChain(IsolationChain2, iptables.Filter, false)
    77  	if err != nil {
    78  		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
    79  	}
    80  	defer func() {
    81  		if err != nil {
    82  			if err := iptable.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil {
    83  				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err)
    84  			}
    85  		}
    86  	}()
    87  
    88  	if err := iptable.AddReturnRule(IsolationChain1); err != nil {
    89  		return nil, nil, nil, nil, err
    90  	}
    91  
    92  	if err := iptable.AddReturnRule(IsolationChain2); err != nil {
    93  		return nil, nil, nil, nil, err
    94  	}
    95  
    96  	return natChain, filterChain, isolationChain1, isolationChain2, nil
    97  }
    98  
    99  func (n *bridgeNetwork) setupIP4Tables(config *networkConfiguration, i *bridgeInterface) error {
   100  	d := n.driver
   101  	d.Lock()
   102  	driverConfig := d.config
   103  	d.Unlock()
   104  
   105  	// Sanity check.
   106  	if !driverConfig.EnableIPTables {
   107  		return errors.New("Cannot program chains, EnableIPTable is disabled")
   108  	}
   109  
   110  	maskedAddrv4 := &net.IPNet{
   111  		IP:   i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask),
   112  		Mask: i.bridgeIPv4.Mask,
   113  	}
   114  	return n.setupIPTables(iptables.IPv4, maskedAddrv4, config, i)
   115  }
   116  
   117  func (n *bridgeNetwork) setupIP6Tables(config *networkConfiguration, i *bridgeInterface) error {
   118  	d := n.driver
   119  	d.Lock()
   120  	driverConfig := d.config
   121  	d.Unlock()
   122  
   123  	// Sanity check.
   124  	if !driverConfig.EnableIP6Tables {
   125  		return errors.New("Cannot program chains, EnableIP6Tables is disabled")
   126  	}
   127  
   128  	maskedAddrv6 := &net.IPNet{
   129  		IP:   i.bridgeIPv6.IP.Mask(i.bridgeIPv6.Mask),
   130  		Mask: i.bridgeIPv6.Mask,
   131  	}
   132  
   133  	return n.setupIPTables(iptables.IPv6, maskedAddrv6, config, i)
   134  }
   135  
   136  func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *net.IPNet, config *networkConfiguration, i *bridgeInterface) error {
   137  	var err error
   138  
   139  	d := n.driver
   140  	d.Lock()
   141  	driverConfig := d.config
   142  	d.Unlock()
   143  
   144  	// Pickup this configuration option from driver
   145  	hairpinMode := !driverConfig.EnableUserlandProxy
   146  
   147  	iptable := iptables.GetIptable(ipVersion)
   148  
   149  	if config.Internal {
   150  		if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil {
   151  			return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
   152  		}
   153  		n.registerIptCleanFunc(func() error {
   154  			return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false)
   155  		})
   156  	} else {
   157  		if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
   158  			return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
   159  		}
   160  		n.registerIptCleanFunc(func() error {
   161  			return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
   162  		})
   163  		natChain, filterChain, _, _, err := n.getDriverChains(ipVersion)
   164  		if err != nil {
   165  			return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
   166  		}
   167  
   168  		err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
   169  		if err != nil {
   170  			return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
   171  		}
   172  
   173  		err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
   174  		if err != nil {
   175  			return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
   176  		}
   177  
   178  		n.registerIptCleanFunc(func() error {
   179  			return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
   180  		})
   181  
   182  		if ipVersion == iptables.IPv4 {
   183  			n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName())
   184  		} else {
   185  			n.portMapperV6.SetIptablesChain(natChain, n.getNetworkBridgeName())
   186  		}
   187  	}
   188  
   189  	d.Lock()
   190  	err = iptable.EnsureJumpRule("FORWARD", IsolationChain1)
   191  	d.Unlock()
   192  	return err
   193  }
   194  
   195  type iptRule struct {
   196  	table   iptables.Table
   197  	chain   string
   198  	preArgs []string
   199  	args    []string
   200  }
   201  
   202  func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr *net.IPNet, icc, ipmasq, hairpin, enable bool) error {
   203  
   204  	var (
   205  		address   = addr.String()
   206  		skipDNAT  = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
   207  		outRule   = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
   208  		natArgs   []string
   209  		hpNatArgs []string
   210  	)
   211  	// if hostIP is set use this address as the src-ip during SNAT
   212  	if hostIP != nil {
   213  		hostAddr := hostIP.String()
   214  		natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
   215  		hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
   216  		// Else use MASQUERADE which picks the src-ip based on NH from the route table
   217  	} else {
   218  		natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
   219  		hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}
   220  	}
   221  
   222  	natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs}
   223  	hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs}
   224  
   225  	ipVersion := iptables.IPv4
   226  
   227  	if addr.IP.To4() == nil {
   228  		ipVersion = iptables.IPv6
   229  	}
   230  
   231  	// Set NAT.
   232  	if ipmasq {
   233  		if err := programChainRule(ipVersion, natRule, "NAT", enable); err != nil {
   234  			return err
   235  		}
   236  	}
   237  
   238  	if ipmasq && !hairpin {
   239  		if err := programChainRule(ipVersion, skipDNAT, "SKIP DNAT", enable); err != nil {
   240  			return err
   241  		}
   242  	}
   243  
   244  	// In hairpin mode, masquerade traffic from localhost
   245  	if hairpin {
   246  		if err := programChainRule(ipVersion, hpNatRule, "MASQ LOCAL HOST", enable); err != nil {
   247  			return err
   248  		}
   249  	}
   250  
   251  	// Set Inter Container Communication.
   252  	if err := setIcc(ipVersion, bridgeIface, icc, enable); err != nil {
   253  		return err
   254  	}
   255  
   256  	// Set Accept on all non-intercontainer outgoing packets.
   257  	return programChainRule(ipVersion, outRule, "ACCEPT NON_ICC OUTGOING", enable)
   258  }
   259  
   260  func programChainRule(version iptables.IPVersion, rule iptRule, ruleDescr string, insert bool) error {
   261  
   262  	iptable := iptables.GetIptable(version)
   263  
   264  	var (
   265  		prefix    []string
   266  		operation string
   267  		condition bool
   268  		doesExist = iptable.Exists(rule.table, rule.chain, rule.args...)
   269  	)
   270  
   271  	if insert {
   272  		condition = !doesExist
   273  		prefix = []string{"-I", rule.chain}
   274  		operation = "enable"
   275  	} else {
   276  		condition = doesExist
   277  		prefix = []string{"-D", rule.chain}
   278  		operation = "disable"
   279  	}
   280  	if rule.preArgs != nil {
   281  		prefix = append(rule.preArgs, prefix...)
   282  	}
   283  
   284  	if condition {
   285  		if err := iptable.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
   286  			return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
   287  		}
   288  	}
   289  
   290  	return nil
   291  }
   292  
   293  func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bool) error {
   294  	iptable := iptables.GetIptable(version)
   295  	var (
   296  		table      = iptables.Filter
   297  		chain      = "FORWARD"
   298  		args       = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
   299  		acceptArgs = append(args, "ACCEPT")
   300  		dropArgs   = append(args, "DROP")
   301  	)
   302  
   303  	if insert {
   304  		if !iccEnable {
   305  			iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
   306  
   307  			if !iptable.Exists(table, chain, dropArgs...) {
   308  				if err := iptable.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
   309  					return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
   310  				}
   311  			}
   312  		} else {
   313  			iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
   314  
   315  			if !iptable.Exists(table, chain, acceptArgs...) {
   316  				if err := iptable.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
   317  					return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
   318  				}
   319  			}
   320  		}
   321  	} else {
   322  		// Remove any ICC rule.
   323  		if !iccEnable {
   324  			if iptable.Exists(table, chain, dropArgs...) {
   325  				iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
   326  			}
   327  		} else {
   328  			if iptable.Exists(table, chain, acceptArgs...) {
   329  				iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
   330  			}
   331  		}
   332  	}
   333  
   334  	return nil
   335  }
   336  
   337  // Control Inter Network Communication. Install[Remove] only if it is [not] present.
   338  func setINC(version iptables.IPVersion, iface string, enable bool) error {
   339  	iptable := iptables.GetIptable(version)
   340  	var (
   341  		action    = iptables.Insert
   342  		actionMsg = "add"
   343  		chains    = []string{IsolationChain1, IsolationChain2}
   344  		rules     = [][]string{
   345  			{"-i", iface, "!", "-o", iface, "-j", IsolationChain2},
   346  			{"-o", iface, "-j", "DROP"},
   347  		}
   348  	)
   349  
   350  	if !enable {
   351  		action = iptables.Delete
   352  		actionMsg = "remove"
   353  	}
   354  
   355  	for i, chain := range chains {
   356  		if err := iptable.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil {
   357  			msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err)
   358  			if enable {
   359  				if i == 1 {
   360  					// Rollback the rule installed on first chain
   361  					if err2 := iptable.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil {
   362  						logrus.Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2)
   363  					}
   364  				}
   365  				return fmt.Errorf(msg)
   366  			}
   367  			logrus.Warn(msg)
   368  		}
   369  	}
   370  
   371  	return nil
   372  }
   373  
   374  // Obsolete chain from previous docker versions
   375  const oldIsolationChain = "DOCKER-ISOLATION"
   376  
   377  func removeIPChains(version iptables.IPVersion) {
   378  	ipt := iptables.IPTable{Version: version}
   379  
   380  	// Remove obsolete rules from default chains
   381  	ipt.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain})
   382  
   383  	// Remove chains
   384  	for _, chainInfo := range []iptables.ChainInfo{
   385  		{Name: DockerChain, Table: iptables.Nat, IPTable: ipt},
   386  		{Name: DockerChain, Table: iptables.Filter, IPTable: ipt},
   387  		{Name: IsolationChain1, Table: iptables.Filter, IPTable: ipt},
   388  		{Name: IsolationChain2, Table: iptables.Filter, IPTable: ipt},
   389  		{Name: oldIsolationChain, Table: iptables.Filter, IPTable: ipt},
   390  	} {
   391  
   392  		if err := chainInfo.Remove(); err != nil {
   393  			logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
   394  		}
   395  	}
   396  }
   397  
   398  func setupInternalNetworkRules(bridgeIface string, addr *net.IPNet, icc, insert bool) error {
   399  	var (
   400  		inDropRule  = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}}
   401  		outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}}
   402  	)
   403  
   404  	version := iptables.IPv4
   405  
   406  	if addr.IP.To4() == nil {
   407  		version = iptables.IPv6
   408  	}
   409  
   410  	if err := programChainRule(version, inDropRule, "DROP INCOMING", insert); err != nil {
   411  		return err
   412  	}
   413  	if err := programChainRule(version, outDropRule, "DROP OUTGOING", insert); err != nil {
   414  		return err
   415  	}
   416  	// Set Inter Container Communication.
   417  	return setIcc(version, bridgeIface, icc, insert)
   418  }
   419  
   420  func clearEndpointConnections(nlh *netlink.Handle, ep *bridgeEndpoint) {
   421  	var ipv4List []net.IP
   422  	var ipv6List []net.IP
   423  	if ep.addr != nil {
   424  		ipv4List = append(ipv4List, ep.addr.IP)
   425  	}
   426  	if ep.addrv6 != nil {
   427  		ipv6List = append(ipv6List, ep.addrv6.IP)
   428  	}
   429  	iptables.DeleteConntrackEntries(nlh, ipv4List, ipv6List)
   430  }