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