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 }