From 9714bcac8737724433fe2bbf13a85b61df751a35 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Wed, 6 May 2015 23:47:41 +0000 Subject: [PATCH 1/2] Brought in iptables package into libnetwork. Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/bridge/bridge_test.go | 2 +- libnetwork/drivers/bridge/link.go | 2 +- libnetwork/drivers/bridge/setup_ip_tables.go | 2 +- .../drivers/bridge/setup_ip_tables_test.go | 2 +- libnetwork/pkg/iptables/firewalld.go | 169 +++++++++ libnetwork/pkg/iptables/firewalld_test.go | 78 +++++ libnetwork/pkg/iptables/iptables.go | 321 ++++++++++++++++++ libnetwork/pkg/iptables/iptables_test.go | 198 +++++++++++ libnetwork/portmapper/mapper.go | 2 +- libnetwork/portmapper/mapper_test.go | 2 +- 10 files changed, 772 insertions(+), 6 deletions(-) create mode 100644 libnetwork/pkg/iptables/firewalld.go create mode 100644 libnetwork/pkg/iptables/firewalld_test.go create mode 100644 libnetwork/pkg/iptables/iptables.go create mode 100644 libnetwork/pkg/iptables/iptables_test.go diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 00f48d3bc9..04f9a19ea1 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -7,8 +7,8 @@ import ( "regexp" "testing" - "github.com/docker/docker/pkg/iptables" "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/pkg/iptables" "github.com/docker/libnetwork/pkg/netlabel" "github.com/vishvananda/netlink" ) diff --git a/libnetwork/drivers/bridge/link.go b/libnetwork/drivers/bridge/link.go index 20ecca04d2..be77134037 100644 --- a/libnetwork/drivers/bridge/link.go +++ b/libnetwork/drivers/bridge/link.go @@ -5,8 +5,8 @@ import ( "net" log "github.com/Sirupsen/logrus" - "github.com/docker/docker/pkg/iptables" "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/pkg/iptables" ) type link struct { diff --git a/libnetwork/drivers/bridge/setup_ip_tables.go b/libnetwork/drivers/bridge/setup_ip_tables.go index cba8df9cbf..6da0a00733 100644 --- a/libnetwork/drivers/bridge/setup_ip_tables.go +++ b/libnetwork/drivers/bridge/setup_ip_tables.go @@ -4,8 +4,8 @@ import ( "fmt" "net" - "github.com/docker/docker/pkg/iptables" "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/pkg/iptables" ) // DockerChain: DOCKER iptable chain name diff --git a/libnetwork/drivers/bridge/setup_ip_tables_test.go b/libnetwork/drivers/bridge/setup_ip_tables_test.go index 2e4e319f7d..35dd0d7f2d 100644 --- a/libnetwork/drivers/bridge/setup_ip_tables_test.go +++ b/libnetwork/drivers/bridge/setup_ip_tables_test.go @@ -4,8 +4,8 @@ import ( "net" "testing" - "github.com/docker/docker/pkg/iptables" "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/pkg/iptables" ) const ( diff --git a/libnetwork/pkg/iptables/firewalld.go b/libnetwork/pkg/iptables/firewalld.go new file mode 100644 index 0000000000..ee3fac5a7b --- /dev/null +++ b/libnetwork/pkg/iptables/firewalld.go @@ -0,0 +1,169 @@ +package iptables + +import ( + "fmt" + "strings" + + "github.com/Sirupsen/logrus" + "github.com/godbus/dbus" +) + +// IPV defines the table string +type IPV string + +const ( + // Iptables point ipv4 table + Iptables IPV = "ipv4" + // IP6tables point to ipv6 table + IP6tables IPV = "ipv6" + // Ebtables point to bridge table + Ebtables IPV = "eb" +) +const ( + dbusInterface = "org.fedoraproject.FirewallD1" + dbusPath = "/org/fedoraproject/FirewallD1" +) + +// Conn is a connection to firewalld dbus endpoint. +type Conn struct { + sysconn *dbus.Conn + sysobj *dbus.Object + signal chan *dbus.Signal +} + +var ( + connection *Conn + firewalldRunning bool // is Firewalld service running + onReloaded []*func() // callbacks when Firewalld has been reloaded +) + +// FirewalldInit initializes firewalld management code. +func FirewalldInit() { + var err error + + connection, err = newConnection() + + if err != nil { + logrus.Errorf("Failed to connect to D-Bus system bus: %s", err) + } + if connection != nil { + go signalHandler() + } + + firewalldRunning = checkRunning() +} + +// New() establishes a connection to the system bus. +func newConnection() (*Conn, error) { + c := new(Conn) + if err := c.initConnection(); err != nil { + return nil, err + } + + return c, nil +} + +// Innitialize D-Bus connection. +func (c *Conn) initConnection() error { + var err error + + c.sysconn, err = dbus.SystemBus() + if err != nil { + return err + } + + // This never fails, even if the service is not running atm. + c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) + + rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", + dbusPath, dbusInterface, dbusInterface) + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) + + rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", + dbusInterface) + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) + + c.signal = make(chan *dbus.Signal, 10) + c.sysconn.Signal(c.signal) + + return nil +} + +func signalHandler() { + for signal := range connection.signal { + if strings.Contains(signal.Name, "NameOwnerChanged") { + firewalldRunning = checkRunning() + dbusConnectionChanged(signal.Body) + } else if strings.Contains(signal.Name, "Reloaded") { + reloaded() + } + } +} + +func dbusConnectionChanged(args []interface{}) { + name := args[0].(string) + oldOwner := args[1].(string) + newOwner := args[2].(string) + + if name != dbusInterface { + return + } + + if len(newOwner) > 0 { + connectionEstablished() + } else if len(oldOwner) > 0 { + connectionLost() + } +} + +func connectionEstablished() { + reloaded() +} + +func connectionLost() { + // Doesn't do anything for now. Libvirt also doesn't react to this. +} + +// call all callbacks +func reloaded() { + for _, pf := range onReloaded { + (*pf)() + } +} + +// OnReloaded add callback +func OnReloaded(callback func()) { + for _, pf := range onReloaded { + if pf == &callback { + return + } + } + onReloaded = append(onReloaded, &callback) +} + +// Call some remote method to see whether the service is actually running. +func checkRunning() bool { + var zone string + var err error + + if connection != nil { + err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) + logrus.Infof("Firewalld running: %t", err == nil) + return err == nil + } + logrus.Info("Firewalld not running") + return false +} + +// Passthrough method simply passes args through to iptables/ip6tables +func Passthrough(ipv IPV, args ...string) ([]byte, error) { + var output string + + logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) + err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output) + if output != "" { + logrus.Debugf("passthrough output: %s", output) + } + + return []byte(output), err +} diff --git a/libnetwork/pkg/iptables/firewalld_test.go b/libnetwork/pkg/iptables/firewalld_test.go new file mode 100644 index 0000000000..3896007d64 --- /dev/null +++ b/libnetwork/pkg/iptables/firewalld_test.go @@ -0,0 +1,78 @@ +package iptables + +import ( + "net" + "strconv" + "testing" +) + +func TestFirewalldInit(t *testing.T) { + FirewalldInit() +} + +func TestReloaded(t *testing.T) { + var err error + var fwdChain *Chain + + fwdChain, err = NewChain("FWD", "lo", Filter) + if err != nil { + t.Fatal(err) + } + defer fwdChain.Remove() + + // copy-pasted from iptables_test:TestLink + ip1 := net.ParseIP("192.168.1.1") + ip2 := net.ParseIP("192.168.1.2") + port := 1234 + proto := "tcp" + + err = fwdChain.Link(Append, ip1, ip2, port, proto) + if err != nil { + t.Fatal(err) + } else { + // to be re-called again later + OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) }) + } + + rule1 := []string{ + "-i", fwdChain.Bridge, + "-o", fwdChain.Bridge, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { + t.Fatalf("rule1 does not exist") + } + + // flush all rules + fwdChain.Remove() + + reloaded() + + // make sure the rules have been recreated + if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { + t.Fatalf("rule1 hasn't been recreated") + } +} + +func TestPassthrough(t *testing.T) { + rule1 := []string{ + "-i", "lo", + "-p", "udp", + "--dport", "123", + "-j", "ACCEPT"} + + if firewalldRunning { + _, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...) + if err != nil { + t.Fatal(err) + } + if !Exists(Filter, "INPUT", rule1...) { + t.Fatalf("rule1 does not exist") + } + } + +} diff --git a/libnetwork/pkg/iptables/iptables.go b/libnetwork/pkg/iptables/iptables.go new file mode 100644 index 0000000000..77e117359c --- /dev/null +++ b/libnetwork/pkg/iptables/iptables.go @@ -0,0 +1,321 @@ +package iptables + +import ( + "errors" + "fmt" + "net" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/Sirupsen/logrus" +) + +//Action signifies the iptable action. +type Action string + +//Table refers to Nat, Filter or Mangle. +type Table string + +const ( + //Append appends the rule at the end of the chain. + Append Action = "-A" + //Delete deletes the rule from the chain. + Delete Action = "-D" + //Insert inserts the rule at the top of the chain. + Insert Action = "-I" + //Nat table is used for nat translation rules. + Nat Table = "nat" + //Filter table is used for filter rules. + Filter Table = "filter" + //Mangle table is used for mangling the packet. + Mangle Table = "mangle" +) + +var ( + iptablesPath string + supportsXlock = false + //ErrIptablesNotFound is returned when the rule is not found. + ErrIptablesNotFound = errors.New("Iptables not found") +) + +//Chain defines the iptables chain. +type Chain struct { + Name string + Bridge string + Table Table +} + +//ChainError is returned to represent errors during ip table operation. +type ChainError struct { + Chain string + Output []byte +} + +func (e ChainError) Error() string { + return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output)) +} + +func initCheck() error { + + if iptablesPath == "" { + path, err := exec.LookPath("iptables") + if err != nil { + return ErrIptablesNotFound + } + iptablesPath = path + supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil + } + return nil +} + +//NewChain adds a new chain to ip table. +func NewChain(name, bridge string, table Table) (*Chain, error) { + c := &Chain{ + Name: name, + Bridge: bridge, + Table: table, + } + + if string(c.Table) == "" { + c.Table = Filter + } + + // Add chain if it doesn't exist + if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil { + if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil { + return nil, err + } else if len(output) != 0 { + return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output) + } + } + + switch table { + case Nat: + preroute := []string{ + "-m", "addrtype", + "--dst-type", "LOCAL"} + if !Exists(Nat, "PREROUTING", preroute...) { + if err := c.Prerouting(Append, preroute...); err != nil { + return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err) + } + } + output := []string{ + "-m", "addrtype", + "--dst-type", "LOCAL", + "!", "--dst", "127.0.0.0/8"} + if !Exists(Nat, "OUTPUT", output...) { + if err := c.Output(Append, output...); err != nil { + return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err) + } + } + case Filter: + link := []string{ + "-o", c.Bridge, + "-j", c.Name} + if !Exists(Filter, "FORWARD", link...) { + insert := append([]string{string(Insert), "FORWARD"}, link...) + if output, err := Raw(insert...); err != nil { + return nil, err + } else if len(output) != 0 { + return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output) + } + } + } + return c, nil +} + +//RemoveExistingChain removes existing chain from the table. +func RemoveExistingChain(name string, table Table) error { + c := &Chain{ + Name: name, + Table: table, + } + if string(c.Table) == "" { + c.Table = Filter + } + return c.Remove() +} + +//Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table +func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error { + daddr := ip.String() + if ip.IsUnspecified() { + // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we + // want "0.0.0.0/0". "0/0" is correctly interpreted as "any + // value" by both iptables and ip6tables. + daddr = "0/0" + } + if output, err := Raw("-t", string(Nat), string(action), c.Name, + "-p", proto, + "-d", daddr, + "--dport", strconv.Itoa(port), + "!", "-i", c.Bridge, + "-j", "DNAT", + "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "FORWARD", Output: output} + } + + if output, err := Raw("-t", string(Filter), string(action), c.Name, + "!", "-i", c.Bridge, + "-o", c.Bridge, + "-p", proto, + "-d", destAddr, + "--dport", strconv.Itoa(destPort), + "-j", "ACCEPT"); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "FORWARD", Output: output} + } + + if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING", + "-p", proto, + "-s", destAddr, + "-d", destAddr, + "--dport", strconv.Itoa(destPort), + "-j", "MASQUERADE"); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "FORWARD", Output: output} + } + + return nil +} + +//Link adds reciprocal ACCEPT rule for two supplied IP addresses. +// Traffic is allowed from ip1 to ip2 and vice-versa +func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error { + if output, err := Raw("-t", string(Filter), string(action), c.Name, + "-i", c.Bridge, "-o", c.Bridge, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT"); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables forward: %s", output) + } + if output, err := Raw("-t", string(Filter), string(action), c.Name, + "-i", c.Bridge, "-o", c.Bridge, + "-p", proto, + "-s", ip2.String(), + "-d", ip1.String(), + "--sport", strconv.Itoa(port), + "-j", "ACCEPT"); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables forward: %s", output) + } + return nil +} + +//Prerouting adds linking rule to nat/PREROUTING chain. +func (c *Chain) Prerouting(action Action, args ...string) error { + a := []string{"-t", string(Nat), string(action), "PREROUTING"} + if len(args) > 0 { + a = append(a, args...) + } + if output, err := Raw(append(a, "-j", c.Name)...); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "PREROUTING", Output: output} + } + return nil +} + +//Output adds linking rule to an OUTPUT chain +func (c *Chain) Output(action Action, args ...string) error { + a := []string{"-t", string(c.Table), string(action), "OUTPUT"} + if len(args) > 0 { + a = append(a, args...) + } + if output, err := Raw(append(a, "-j", c.Name)...); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "OUTPUT", Output: output} + } + return nil +} + +// Remove removes the chain +func (c *Chain) Remove() error { + // Ignore errors - This could mean the chains were never set up + if c.Table == Nat { + c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL") + c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8") + c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6 + + c.Prerouting(Delete) + c.Output(Delete) + } + Raw("-t", string(c.Table), "-F", c.Name) + Raw("-t", string(c.Table), "-X", c.Name) + return nil +} + +//Exists checks if a rule exists +func Exists(table Table, chain string, rule ...string) bool { + if string(table) == "" { + table = Filter + } + + // iptables -C, --check option was added in v.1.4.11 + // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt + + // try -C + // if exit status is 0 then return true, the rule exists + if _, err := Raw(append([]string{ + "-t", string(table), "-C", chain}, rule...)...); err == nil { + return true + } + + // parse "iptables -S" for the rule (this checks rules in a specific chain + // in a specific table) + ruleString := strings.Join(rule, " ") + existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() + + // regex to replace ips in rule + // because MASQUERADE rule will not be exactly what was passed + re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`) + + return strings.Contains( + re.ReplaceAllString(string(existingRules), "?"), + re.ReplaceAllString(ruleString, "?"), + ) +} + +//Raw calls 'iptables' system command, passing supplied arguments +func Raw(args ...string) ([]byte, error) { + if firewalldRunning { + output, err := Passthrough(Iptables, args...) + if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { + return output, err + } + + } + + if err := initCheck(); err != nil { + return nil, err + } + if supportsXlock { + args = append([]string{"--wait"}, args...) + } + + logrus.Debugf("%s, %v", iptablesPath, args) + + output, err := exec.Command(iptablesPath, args...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) + } + + // ignore iptables' message about xtables lock + if strings.Contains(string(output), "waiting for it to exit") { + output = []byte("") + } + + return output, err +} diff --git a/libnetwork/pkg/iptables/iptables_test.go b/libnetwork/pkg/iptables/iptables_test.go new file mode 100644 index 0000000000..ced4262ce2 --- /dev/null +++ b/libnetwork/pkg/iptables/iptables_test.go @@ -0,0 +1,198 @@ +package iptables + +import ( + "net" + "os/exec" + "strconv" + "strings" + "testing" +) + +const chainName = "DOCKERTEST" + +var natChain *Chain +var filterChain *Chain + +func TestNewChain(t *testing.T) { + var err error + + natChain, err = NewChain(chainName, "lo", Nat) + if err != nil { + t.Fatal(err) + } + + filterChain, err = NewChain(chainName, "lo", Filter) + if err != nil { + t.Fatal(err) + } +} + +func TestForward(t *testing.T) { + ip := net.ParseIP("192.168.1.1") + port := 1234 + dstAddr := "172.17.0.1" + dstPort := 4321 + proto := "tcp" + + err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort) + if err != nil { + t.Fatal(err) + } + + dnatRule := []string{ + "!", "-i", filterChain.Bridge, + "-d", ip.String(), + "-p", proto, + "--dport", strconv.Itoa(port), + "-j", "DNAT", + "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort), + } + + if !Exists(natChain.Table, natChain.Name, dnatRule...) { + t.Fatalf("DNAT rule does not exist") + } + + filterRule := []string{ + "!", "-i", filterChain.Bridge, + "-o", filterChain.Bridge, + "-d", dstAddr, + "-p", proto, + "--dport", strconv.Itoa(dstPort), + "-j", "ACCEPT", + } + + if !Exists(filterChain.Table, filterChain.Name, filterRule...) { + t.Fatalf("filter rule does not exist") + } + + masqRule := []string{ + "-d", dstAddr, + "-s", dstAddr, + "-p", proto, + "--dport", strconv.Itoa(dstPort), + "-j", "MASQUERADE", + } + + if !Exists(natChain.Table, "POSTROUTING", masqRule...) { + t.Fatalf("MASQUERADE rule does not exist") + } +} + +func TestLink(t *testing.T) { + var err error + + ip1 := net.ParseIP("192.168.1.1") + ip2 := net.ParseIP("192.168.1.2") + port := 1234 + proto := "tcp" + + err = filterChain.Link(Append, ip1, ip2, port, proto) + if err != nil { + t.Fatal(err) + } + + rule1 := []string{ + "-i", filterChain.Bridge, + "-o", filterChain.Bridge, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !Exists(filterChain.Table, filterChain.Name, rule1...) { + t.Fatalf("rule1 does not exist") + } + + rule2 := []string{ + "-i", filterChain.Bridge, + "-o", filterChain.Bridge, + "-p", proto, + "-s", ip2.String(), + "-d", ip1.String(), + "--sport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !Exists(filterChain.Table, filterChain.Name, rule2...) { + t.Fatalf("rule2 does not exist") + } +} + +func TestPrerouting(t *testing.T) { + args := []string{ + "-i", "lo", + "-d", "192.168.1.1"} + + err := natChain.Prerouting(Insert, args...) + if err != nil { + t.Fatal(err) + } + + rule := []string{ + "-j", natChain.Name} + + rule = append(rule, args...) + + if !Exists(natChain.Table, "PREROUTING", rule...) { + t.Fatalf("rule does not exist") + } + + delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...) + if _, err = Raw(delRule...); err != nil { + t.Fatal(err) + } +} + +func TestOutput(t *testing.T) { + args := []string{ + "-o", "lo", + "-d", "192.168.1.1"} + + err := natChain.Output(Insert, args...) + if err != nil { + t.Fatal(err) + } + + rule := []string{ + "-j", natChain.Name} + + rule = append(rule, args...) + + if !Exists(natChain.Table, "OUTPUT", rule...) { + t.Fatalf("rule does not exist") + } + + delRule := append([]string{"-D", "OUTPUT", "-t", + string(natChain.Table)}, rule...) + if _, err = Raw(delRule...); err != nil { + t.Fatal(err) + } +} + +func TestCleanup(t *testing.T) { + var err error + var rules []byte + + // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty + link := []string{"-t", string(filterChain.Table), + string(Delete), "FORWARD", + "-o", filterChain.Bridge, + "-j", filterChain.Name} + if _, err = Raw(link...); err != nil { + t.Fatal(err) + } + filterChain.Remove() + + err = RemoveExistingChain(chainName, Nat) + if err != nil { + t.Fatal(err) + } + + rules, err = exec.Command("iptables-save").Output() + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(rules), chainName) { + t.Fatalf("Removing chain failed. %s found in iptables-save", chainName) + } +} diff --git a/libnetwork/portmapper/mapper.go b/libnetwork/portmapper/mapper.go index fdedd27d06..c545eae126 100644 --- a/libnetwork/portmapper/mapper.go +++ b/libnetwork/portmapper/mapper.go @@ -7,7 +7,7 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/docker/docker/pkg/iptables" + "github.com/docker/libnetwork/pkg/iptables" "github.com/docker/libnetwork/pkg/portallocator" ) diff --git a/libnetwork/portmapper/mapper_test.go b/libnetwork/portmapper/mapper_test.go index 73c44b7418..9085472d49 100644 --- a/libnetwork/portmapper/mapper_test.go +++ b/libnetwork/portmapper/mapper_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/docker/docker/pkg/iptables" + "github.com/docker/libnetwork/pkg/iptables" ) func init() { From 067e8977d5c5e37474f0fda95b5f63ee50127bd9 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Wed, 6 May 2015 23:51:52 +0000 Subject: [PATCH 2/2] Updated godeps Signed-off-by: Jana Radhakrishnan --- libnetwork/Godeps/Godeps.json | 5 - .../docker/docker/pkg/iptables/firewalld.go | 164 ---------- .../docker/pkg/iptables/firewalld_test.go | 78 ----- .../docker/docker/pkg/iptables/iptables.go | 306 ------------------ .../docker/pkg/iptables/iptables_test.go | 198 ------------ 5 files changed, 751 deletions(-) delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld.go delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld_test.go delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables.go delete mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables_test.go diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index c24317664b..ea90ab487b 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -20,11 +20,6 @@ "Comment": "v1.4.1-3152-g3e85803", "Rev": "3e85803f311c3883a9b395ad046c894ea255e9be" }, - { - "ImportPath": "github.com/docker/docker/pkg/iptables", - "Comment": "v1.4.1-3152-g3e85803", - "Rev": "3e85803f311c3883a9b395ad046c894ea255e9be" - }, { "ImportPath": "github.com/docker/docker/pkg/mflag", "Comment": "v1.4.1-3152-g3e85803", diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld.go deleted file mode 100644 index 1c0cddb0f0..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld.go +++ /dev/null @@ -1,164 +0,0 @@ -package iptables - -import ( - "fmt" - "strings" - - "github.com/Sirupsen/logrus" - "github.com/godbus/dbus" -) - -type IPV string - -const ( - Iptables IPV = "ipv4" - Ip6tables IPV = "ipv6" - Ebtables IPV = "eb" -) -const ( - dbusInterface = "org.fedoraproject.FirewallD1" - dbusPath = "/org/fedoraproject/FirewallD1" -) - -// Conn is a connection to firewalld dbus endpoint. -type Conn struct { - sysconn *dbus.Conn - sysobj *dbus.Object - signal chan *dbus.Signal -} - -var ( - connection *Conn - firewalldRunning bool // is Firewalld service running - onReloaded []*func() // callbacks when Firewalld has been reloaded -) - -func FirewalldInit() { - var err error - - connection, err = newConnection() - - if err != nil { - logrus.Errorf("Failed to connect to D-Bus system bus: %s", err) - } - if connection != nil { - go signalHandler() - } - - firewalldRunning = checkRunning() -} - -// New() establishes a connection to the system bus. -func newConnection() (*Conn, error) { - c := new(Conn) - if err := c.initConnection(); err != nil { - return nil, err - } - - return c, nil -} - -// Innitialize D-Bus connection. -func (c *Conn) initConnection() error { - var err error - - c.sysconn, err = dbus.SystemBus() - if err != nil { - return err - } - - // This never fails, even if the service is not running atm. - c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) - - rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", - dbusPath, dbusInterface, dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", - dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - c.signal = make(chan *dbus.Signal, 10) - c.sysconn.Signal(c.signal) - - return nil -} - -func signalHandler() { - for signal := range connection.signal { - if strings.Contains(signal.Name, "NameOwnerChanged") { - firewalldRunning = checkRunning() - dbusConnectionChanged(signal.Body) - } else if strings.Contains(signal.Name, "Reloaded") { - reloaded() - } - } -} - -func dbusConnectionChanged(args []interface{}) { - name := args[0].(string) - old_owner := args[1].(string) - new_owner := args[2].(string) - - if name != dbusInterface { - return - } - - if len(new_owner) > 0 { - connectionEstablished() - } else if len(old_owner) > 0 { - connectionLost() - } -} - -func connectionEstablished() { - reloaded() -} - -func connectionLost() { - // Doesn't do anything for now. Libvirt also doesn't react to this. -} - -// call all callbacks -func reloaded() { - for _, pf := range onReloaded { - (*pf)() - } -} - -// add callback -func OnReloaded(callback func()) { - for _, pf := range onReloaded { - if pf == &callback { - return - } - } - onReloaded = append(onReloaded, &callback) -} - -// Call some remote method to see whether the service is actually running. -func checkRunning() bool { - var zone string - var err error - - if connection != nil { - err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) - logrus.Infof("Firewalld running: %t", err == nil) - return err == nil - } - logrus.Info("Firewalld not running") - return false -} - -// Firewalld's passthrough method simply passes args through to iptables/ip6tables -func Passthrough(ipv IPV, args ...string) ([]byte, error) { - var output string - - logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) - err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output) - if output != "" { - logrus.Debugf("passthrough output: %s", output) - } - - return []byte(output), err -} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld_test.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld_test.go deleted file mode 100644 index 3896007d64..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/firewalld_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package iptables - -import ( - "net" - "strconv" - "testing" -) - -func TestFirewalldInit(t *testing.T) { - FirewalldInit() -} - -func TestReloaded(t *testing.T) { - var err error - var fwdChain *Chain - - fwdChain, err = NewChain("FWD", "lo", Filter) - if err != nil { - t.Fatal(err) - } - defer fwdChain.Remove() - - // copy-pasted from iptables_test:TestLink - ip1 := net.ParseIP("192.168.1.1") - ip2 := net.ParseIP("192.168.1.2") - port := 1234 - proto := "tcp" - - err = fwdChain.Link(Append, ip1, ip2, port, proto) - if err != nil { - t.Fatal(err) - } else { - // to be re-called again later - OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) }) - } - - rule1 := []string{ - "-i", fwdChain.Bridge, - "-o", fwdChain.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { - t.Fatalf("rule1 does not exist") - } - - // flush all rules - fwdChain.Remove() - - reloaded() - - // make sure the rules have been recreated - if !Exists(fwdChain.Table, fwdChain.Name, rule1...) { - t.Fatalf("rule1 hasn't been recreated") - } -} - -func TestPassthrough(t *testing.T) { - rule1 := []string{ - "-i", "lo", - "-p", "udp", - "--dport", "123", - "-j", "ACCEPT"} - - if firewalldRunning { - _, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...) - if err != nil { - t.Fatal(err) - } - if !Exists(Filter, "INPUT", rule1...) { - t.Fatalf("rule1 does not exist") - } - } - -} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables.go deleted file mode 100644 index 9983ec61ff..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables.go +++ /dev/null @@ -1,306 +0,0 @@ -package iptables - -import ( - "errors" - "fmt" - "net" - "os/exec" - "regexp" - "strconv" - "strings" - - "github.com/Sirupsen/logrus" -) - -type Action string -type Table string - -const ( - Append Action = "-A" - Delete Action = "-D" - Insert Action = "-I" - Nat Table = "nat" - Filter Table = "filter" - Mangle Table = "mangle" -) - -var ( - iptablesPath string - supportsXlock = false - ErrIptablesNotFound = errors.New("Iptables not found") -) - -type Chain struct { - Name string - Bridge string - Table Table -} - -type ChainError struct { - Chain string - Output []byte -} - -func (e ChainError) Error() string { - return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output)) -} - -func initCheck() error { - - if iptablesPath == "" { - path, err := exec.LookPath("iptables") - if err != nil { - return ErrIptablesNotFound - } - iptablesPath = path - supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil - } - return nil -} - -func NewChain(name, bridge string, table Table) (*Chain, error) { - c := &Chain{ - Name: name, - Bridge: bridge, - Table: table, - } - - if string(c.Table) == "" { - c.Table = Filter - } - - // Add chain if it doesn't exist - if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil { - if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil { - return nil, err - } else if len(output) != 0 { - return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output) - } - } - - switch table { - case Nat: - preroute := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL"} - if !Exists(Nat, "PREROUTING", preroute...) { - if err := c.Prerouting(Append, preroute...); err != nil { - return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err) - } - } - output := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL", - "!", "--dst", "127.0.0.0/8"} - if !Exists(Nat, "OUTPUT", output...) { - if err := c.Output(Append, output...); err != nil { - return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err) - } - } - case Filter: - link := []string{ - "-o", c.Bridge, - "-j", c.Name} - if !Exists(Filter, "FORWARD", link...) { - insert := append([]string{string(Insert), "FORWARD"}, link...) - if output, err := Raw(insert...); err != nil { - return nil, err - } else if len(output) != 0 { - return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output) - } - } - } - return c, nil -} - -func RemoveExistingChain(name string, table Table) error { - c := &Chain{ - Name: name, - Table: table, - } - if string(c.Table) == "" { - c.Table = Filter - } - return c.Remove() -} - -// Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table -func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error { - daddr := ip.String() - if ip.IsUnspecified() { - // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we - // want "0.0.0.0/0". "0/0" is correctly interpreted as "any - // value" by both iptables and ip6tables. - daddr = "0/0" - } - if output, err := Raw("-t", string(Nat), string(action), c.Name, - "-p", proto, - "-d", daddr, - "--dport", strconv.Itoa(port), - "!", "-i", c.Bridge, - "-j", "DNAT", - "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "!", "-i", c.Bridge, - "-o", c.Bridge, - "-p", proto, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING", - "-p", proto, - "-s", destAddr, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "MASQUERADE"); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "FORWARD", Output: output} - } - - return nil -} - -// Add reciprocal ACCEPT rule for two supplied IP addresses. -// Traffic is allowed from ip1 to ip2 and vice-versa -func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error { - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "-i", c.Bridge, "-o", c.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Error iptables forward: %s", output) - } - if output, err := Raw("-t", string(Filter), string(action), c.Name, - "-i", c.Bridge, "-o", c.Bridge, - "-p", proto, - "-s", ip2.String(), - "-d", ip1.String(), - "--sport", strconv.Itoa(port), - "-j", "ACCEPT"); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Error iptables forward: %s", output) - } - return nil -} - -// Add linking rule to nat/PREROUTING chain. -func (c *Chain) Prerouting(action Action, args ...string) error { - a := []string{"-t", string(Nat), string(action), "PREROUTING"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(append(a, "-j", c.Name)...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "PREROUTING", Output: output} - } - return nil -} - -// Add linking rule to an OUTPUT chain -func (c *Chain) Output(action Action, args ...string) error { - a := []string{"-t", string(c.Table), string(action), "OUTPUT"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(append(a, "-j", c.Name)...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "OUTPUT", Output: output} - } - return nil -} - -func (c *Chain) Remove() error { - // Ignore errors - This could mean the chains were never set up - if c.Table == Nat { - c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL") - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8") - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6 - - c.Prerouting(Delete) - c.Output(Delete) - } - Raw("-t", string(c.Table), "-F", c.Name) - Raw("-t", string(c.Table), "-X", c.Name) - return nil -} - -// Check if a rule exists -func Exists(table Table, chain string, rule ...string) bool { - if string(table) == "" { - table = Filter - } - - // iptables -C, --check option was added in v.1.4.11 - // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt - - // try -C - // if exit status is 0 then return true, the rule exists - if _, err := Raw(append([]string{ - "-t", string(table), "-C", chain}, rule...)...); err == nil { - return true - } - - // parse "iptables -S" for the rule (this checks rules in a specific chain - // in a specific table) - ruleString := strings.Join(rule, " ") - existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() - - // regex to replace ips in rule - // because MASQUERADE rule will not be exactly what was passed - re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`) - - return strings.Contains( - re.ReplaceAllString(string(existingRules), "?"), - re.ReplaceAllString(ruleString, "?"), - ) -} - -// Call 'iptables' system command, passing supplied arguments -func Raw(args ...string) ([]byte, error) { - if firewalldRunning { - output, err := Passthrough(Iptables, args...) - if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { - return output, err - } - - } - - if err := initCheck(); err != nil { - return nil, err - } - if supportsXlock { - args = append([]string{"--wait"}, args...) - } - - logrus.Debugf("%s, %v", iptablesPath, args) - - output, err := exec.Command(iptablesPath, args...).CombinedOutput() - if err != nil { - return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) - } - - // ignore iptables' message about xtables lock - if strings.Contains(string(output), "waiting for it to exit") { - output = []byte("") - } - - return output, err -} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables_test.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables_test.go deleted file mode 100644 index ced4262ce2..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/iptables/iptables_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package iptables - -import ( - "net" - "os/exec" - "strconv" - "strings" - "testing" -) - -const chainName = "DOCKERTEST" - -var natChain *Chain -var filterChain *Chain - -func TestNewChain(t *testing.T) { - var err error - - natChain, err = NewChain(chainName, "lo", Nat) - if err != nil { - t.Fatal(err) - } - - filterChain, err = NewChain(chainName, "lo", Filter) - if err != nil { - t.Fatal(err) - } -} - -func TestForward(t *testing.T) { - ip := net.ParseIP("192.168.1.1") - port := 1234 - dstAddr := "172.17.0.1" - dstPort := 4321 - proto := "tcp" - - err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort) - if err != nil { - t.Fatal(err) - } - - dnatRule := []string{ - "!", "-i", filterChain.Bridge, - "-d", ip.String(), - "-p", proto, - "--dport", strconv.Itoa(port), - "-j", "DNAT", - "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort), - } - - if !Exists(natChain.Table, natChain.Name, dnatRule...) { - t.Fatalf("DNAT rule does not exist") - } - - filterRule := []string{ - "!", "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-d", dstAddr, - "-p", proto, - "--dport", strconv.Itoa(dstPort), - "-j", "ACCEPT", - } - - if !Exists(filterChain.Table, filterChain.Name, filterRule...) { - t.Fatalf("filter rule does not exist") - } - - masqRule := []string{ - "-d", dstAddr, - "-s", dstAddr, - "-p", proto, - "--dport", strconv.Itoa(dstPort), - "-j", "MASQUERADE", - } - - if !Exists(natChain.Table, "POSTROUTING", masqRule...) { - t.Fatalf("MASQUERADE rule does not exist") - } -} - -func TestLink(t *testing.T) { - var err error - - ip1 := net.ParseIP("192.168.1.1") - ip2 := net.ParseIP("192.168.1.2") - port := 1234 - proto := "tcp" - - err = filterChain.Link(Append, ip1, ip2, port, proto) - if err != nil { - t.Fatal(err) - } - - rule1 := []string{ - "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(filterChain.Table, filterChain.Name, rule1...) { - t.Fatalf("rule1 does not exist") - } - - rule2 := []string{ - "-i", filterChain.Bridge, - "-o", filterChain.Bridge, - "-p", proto, - "-s", ip2.String(), - "-d", ip1.String(), - "--sport", strconv.Itoa(port), - "-j", "ACCEPT"} - - if !Exists(filterChain.Table, filterChain.Name, rule2...) { - t.Fatalf("rule2 does not exist") - } -} - -func TestPrerouting(t *testing.T) { - args := []string{ - "-i", "lo", - "-d", "192.168.1.1"} - - err := natChain.Prerouting(Insert, args...) - if err != nil { - t.Fatal(err) - } - - rule := []string{ - "-j", natChain.Name} - - rule = append(rule, args...) - - if !Exists(natChain.Table, "PREROUTING", rule...) { - t.Fatalf("rule does not exist") - } - - delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...) - if _, err = Raw(delRule...); err != nil { - t.Fatal(err) - } -} - -func TestOutput(t *testing.T) { - args := []string{ - "-o", "lo", - "-d", "192.168.1.1"} - - err := natChain.Output(Insert, args...) - if err != nil { - t.Fatal(err) - } - - rule := []string{ - "-j", natChain.Name} - - rule = append(rule, args...) - - if !Exists(natChain.Table, "OUTPUT", rule...) { - t.Fatalf("rule does not exist") - } - - delRule := append([]string{"-D", "OUTPUT", "-t", - string(natChain.Table)}, rule...) - if _, err = Raw(delRule...); err != nil { - t.Fatal(err) - } -} - -func TestCleanup(t *testing.T) { - var err error - var rules []byte - - // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty - link := []string{"-t", string(filterChain.Table), - string(Delete), "FORWARD", - "-o", filterChain.Bridge, - "-j", filterChain.Name} - if _, err = Raw(link...); err != nil { - t.Fatal(err) - } - filterChain.Remove() - - err = RemoveExistingChain(chainName, Nat) - if err != nil { - t.Fatal(err) - } - - rules, err = exec.Command("iptables-save").Output() - if err != nil { - t.Fatal(err) - } - if strings.Contains(string(rules), chainName) { - t.Fatalf("Removing chain failed. %s found in iptables-save", chainName) - } -}