func (l *Link) toggle(action string, ignoreErrors bool) error { for _, p := range l.Ports { if output, err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ParentIP, "--dport", p.Port(), "-d", l.ChildIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err } else if len(output) != 0 { return fmt.Errorf("Error toggle iptables forward: %s", output) } if output, err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ChildIP, "--sport", p.Port(), "-d", l.ParentIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err } else if len(output) != 0 { return fmt.Errorf("Error toggle iptables forward: %s", output) } } return nil }
func TestIptables(t *testing.T) { if _, err := iptables.Raw("-L"); err != nil { t.Fatal(err) } path := os.Getenv("PATH") os.Setenv("PATH", "") defer os.Setenv("PATH", path) if _, err := iptables.Raw("-L"); err == nil { t.Fatal("Not finding iptables in the PATH should cause an error") } }
func (l *Link) toggle(action string, ignoreErrors bool) error { for _, p := range l.Ports { if err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ParentIP, "--dport", p.Port(), "-d", l.ChildIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err } if err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ChildIP, "--sport", p.Port(), "-d", l.ParentIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err } } return nil }
// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, // and attempts to configure it with an address which doesn't conflict with any other interface on the host. // If it can't find an address which doesn't conflict, it will return an error. func CreateBridgeIface(config *DaemonConfig) error { addrs := []string{ // Here we don't follow the convention of using the 1st IP of the range for the gateway. // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. // In theory this shouldn't matter - in practice there's bound to be a few scripts relying // on the internal addressing or other stupid things like that. // The shouldn't, but hey, let's not break them unless we really have to. "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive "10.1.42.1/16", "10.42.42.1/16", "172.16.42.1/24", "172.16.43.1/24", "172.16.44.1/24", "10.0.42.1/24", "10.0.43.1/24", "192.168.42.1/24", "192.168.43.1/24", "192.168.44.1/24", } nameservers := []string{} resolvConf, _ := utils.GetResolvConf() // we don't check for an error here, because we don't really care // if we can't read /etc/resolv.conf. So instead we skip the append // if resolvConf is nil. It either doesn't exist, or we can't read it // for some reason. if resolvConf != nil { nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) } var ifaceAddr string for _, addr := range addrs { _, dockerNetwork, err := net.ParseCIDR(addr) if err != nil { return err } routes, err := netlink.NetworkGetRoutes() if err != nil { return err } if err := checkRouteOverlaps(routes, dockerNetwork); err == nil { if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil { ifaceAddr = addr break } } else { utils.Debugf("%s: %s", addr, err) } } if ifaceAddr == "" { return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", config.BridgeIface, config.BridgeIface) } utils.Debugf("Creating bridge %s with network %s", config.BridgeIface, ifaceAddr) if err := netlink.NetworkLinkAdd(config.BridgeIface, "bridge"); err != nil { return fmt.Errorf("Error creating bridge: %s", err) } iface, err := net.InterfaceByName(config.BridgeIface) if err != nil { return err } ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) if err != nil { return err } if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { return fmt.Errorf("Unable to add private network: %s", err) } if err := netlink.NetworkLinkUp(iface); err != nil { return fmt.Errorf("Unable to start network bridge: %s", err) } if config.EnableIptables { // Enable NAT if output, err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr, "!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil { return fmt.Errorf("Unable to enable network bridge NAT: %s", err) } else if len(output) != 0 { return fmt.Errorf("Error iptables postrouting: %s", output) } // Accept incoming packets for existing connections if output, err := iptables.Raw("-I", "FORWARD", "-o", config.BridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil { return fmt.Errorf("Unable to allow incoming packets: %s", err) } else if len(output) != 0 { return fmt.Errorf("Error iptables allow incoming: %s", output) } // Accept all non-intercontainer outgoing packets if output, err := iptables.Raw("-I", "FORWARD", "-i", config.BridgeIface, "!", "-o", config.BridgeIface, "-j", "ACCEPT"); err != nil { return fmt.Errorf("Unable to allow outgoing packets: %s", err) } else if len(output) != 0 { return fmt.Errorf("Error iptables allow outgoing: %s", output) } } return nil }
func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { if config.BridgeIface == DisableNetworkBridge { manager := &NetworkManager{ disabled: true, } return manager, nil } addr, err := getIfaceAddr(config.BridgeIface) if err != nil { // If the iface is not found, try to create it if err := CreateBridgeIface(config); err != nil { return nil, err } addr, err = getIfaceAddr(config.BridgeIface) if err != nil { return nil, err } } network := addr.(*net.IPNet) // Configure iptables for link support if config.EnableIptables { args := []string{"FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j"} acceptArgs := append(args, "ACCEPT") dropArgs := append(args, "DROP") if !config.InterContainerCommunication { iptables.Raw(append([]string{"-D"}, acceptArgs...)...) if !iptables.Exists(dropArgs...) { utils.Debugf("Disable inter-container communication") if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil { return nil, fmt.Errorf("Unable to prevent intercontainer communication: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error disabling intercontainer communication: %s", output) } } } else { iptables.Raw(append([]string{"-D"}, dropArgs...)...) if !iptables.Exists(acceptArgs...) { utils.Debugf("Enable inter-container communication") if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil { return nil, fmt.Errorf("Unable to allow intercontainer communication: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error enabling intercontainer communication: %s", output) } } } } ipAllocator := newIPAllocator(network) tcpPortAllocator, err := newPortAllocator() if err != nil { return nil, err } udpPortAllocator, err := newPortAllocator() if err != nil { return nil, err } portMapper, err := newPortMapper(config) if err != nil { return nil, err } manager := &NetworkManager{ bridgeIface: config.BridgeIface, bridgeNetwork: network, ipAllocator: ipAllocator, tcpPortAllocator: tcpPortAllocator, udpPortAllocator: udpPortAllocator, portMapper: portMapper, } return manager, nil }
func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { if config.BridgeIface == DisableNetworkBridge { manager := &NetworkManager{ disabled: true, } return manager, nil } addr, err := getIfaceAddr(config.BridgeIface) if err != nil { // If the iface is not found, try to create it if err := CreateBridgeIface(config); err != nil { return nil, err } addr, err = getIfaceAddr(config.BridgeIface) if err != nil { return nil, err } } network := addr.(*net.IPNet) // Configure iptables for link support if config.EnableIptables { // Enable NAT natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-d", addr.String(), "-j", "MASQUERADE"} if !iptables.Exists(natArgs...) { if output, err := iptables.Raw(append([]string{"-A"}, natArgs...)...); err != nil { return nil, fmt.Errorf("Unable to enable network bridge NAT: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error iptables postrouting: %s", output) } } // Accept incoming packets for existing connections existingArgs := []string{"FORWARD", "-o", config.BridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} if !iptables.Exists(existingArgs...) { if output, err := iptables.Raw(append([]string{"-I"}, existingArgs...)...); err != nil { return nil, fmt.Errorf("Unable to allow incoming packets: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error iptables allow incoming: %s", output) } } // Accept all non-intercontainer outgoing packets outgoingArgs := []string{"FORWARD", "-i", config.BridgeIface, "!", "-o", config.BridgeIface, "-j", "ACCEPT"} if !iptables.Exists(outgoingArgs...) { if output, err := iptables.Raw(append([]string{"-I"}, outgoingArgs...)...); err != nil { return nil, fmt.Errorf("Unable to allow outgoing packets: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error iptables allow outgoing: %s", output) } } args := []string{"FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j"} acceptArgs := append(args, "ACCEPT") dropArgs := append(args, "DROP") if !config.InterContainerCommunication { iptables.Raw(append([]string{"-D"}, acceptArgs...)...) if !iptables.Exists(dropArgs...) { utils.Debugf("Disable inter-container communication") if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil { return nil, fmt.Errorf("Unable to prevent intercontainer communication: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error disabling intercontainer communication: %s", output) } } } else { iptables.Raw(append([]string{"-D"}, dropArgs...)...) if !iptables.Exists(acceptArgs...) { utils.Debugf("Enable inter-container communication") if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil { return nil, fmt.Errorf("Unable to allow intercontainer communication: %s", err) } else if len(output) != 0 { return nil, fmt.Errorf("Error enabling intercontainer communication: %s", output) } } } } ipAllocator := newIPAllocator(network) tcpPortAllocator, err := newPortAllocator() if err != nil { return nil, err } udpPortAllocator, err := newPortAllocator() if err != nil { return nil, err } portMapper, err := newPortMapper(config) if err != nil { return nil, err } manager := &NetworkManager{ bridgeIface: config.BridgeIface, bridgeNetwork: network, ipAllocator: ipAllocator, tcpPortAllocator: tcpPortAllocator, udpPortAllocator: udpPortAllocator, portMapper: portMapper, } return manager, nil }
// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, // and attempts to configure it with an address which doesn't conflict with any other interface on the host. // If it can't find an address which doesn't conflict, it will return an error. func CreateBridgeIface(config *DaemonConfig) error { addrs := []string{ // Here we don't follow the convention of using the 1st IP of the range for the gateway. // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. // In theory this shouldn't matter - in practice there's bound to be a few scripts relying // on the internal addressing or other stupid things like that. // The shouldn't, but hey, let's not break them unless we really have to. "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive "10.1.42.1/16", "10.42.42.1/16", "172.16.42.1/24", "172.16.43.1/24", "172.16.44.1/24", "10.0.42.1/24", "10.0.43.1/24", "192.168.42.1/24", "192.168.43.1/24", "192.168.44.1/24", } var ifaceAddr string for _, addr := range addrs { _, dockerNetwork, err := net.ParseCIDR(addr) if err != nil { return err } routes, err := netlink.NetworkGetRoutes() if err != nil { return err } if err := checkRouteOverlaps(routes, dockerNetwork); err == nil { ifaceAddr = addr break } else { utils.Debugf("%s: %s", addr, err) } } if ifaceAddr == "" { return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", config.BridgeIface, config.BridgeIface) } utils.Debugf("Creating bridge %s with network %s", config.BridgeIface, ifaceAddr) if err := netlink.NetworkLinkAdd(config.BridgeIface, "bridge"); err != nil { return fmt.Errorf("Error creating bridge: %s", err) } iface, err := net.InterfaceByName(config.BridgeIface) if err != nil { return err } ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) if err != nil { return err } if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { return fmt.Errorf("Unable to add private network: %s", err) } if err := netlink.NetworkLinkUp(iface); err != nil { return fmt.Errorf("Unable to start network bridge: %s", err) } if config.EnableIptables { if err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr, "!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil { return fmt.Errorf("Unable to enable network bridge NAT: %s", err) } } return nil }