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