Docker-DCO-1.1-Signed-off-by: Ian Bishop <ianbishop@pace7.com> (github: porjo)
| ... | ... |
@@ -5,8 +5,6 @@ import ( |
| 5 | 5 |
"io/ioutil" |
| 6 | 6 |
"net" |
| 7 | 7 |
"os" |
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 | 8 |
"sync" |
| 11 | 9 |
|
| 12 | 10 |
log "github.com/Sirupsen/logrus" |
| ... | ... |
@@ -513,10 +511,6 @@ func LinkContainers(job *engine.Job) engine.Status {
|
| 513 | 513 |
ports = job.GetenvList("Ports")
|
| 514 | 514 |
chain = iptables.Chain{}
|
| 515 | 515 |
) |
| 516 |
- split := func(p string) (string, string) {
|
|
| 517 |
- parts := strings.Split(p, "/") |
|
| 518 |
- return parts[0], parts[1] |
|
| 519 |
- } |
|
| 520 | 516 |
|
| 521 | 517 |
switch action {
|
| 522 | 518 |
case "-A": |
| ... | ... |
@@ -541,13 +535,11 @@ func LinkContainers(job *engine.Job) engine.Status {
|
| 541 | 541 |
chain.Name = "DOCKER" |
| 542 | 542 |
chain.Bridge = bridgeIface |
| 543 | 543 |
for _, p := range ports {
|
| 544 |
- portStr, proto := split(p) |
|
| 545 |
- port, err := strconv.Atoi(portStr) |
|
| 546 |
- if !ignoreErrors && err != nil {
|
|
| 547 |
- return job.Errorf("port '%s' is invalid", portStr)
|
|
| 548 |
- } |
|
| 549 |
- if err := chain.Link(nfAction, ip1, ip2, port, proto); !ignoreErrors && err != nil {
|
|
| 544 |
+ port := nat.Port(p) |
|
| 545 |
+ if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil {
|
|
| 546 |
+ fmt.Print(err) |
|
| 550 | 547 |
return job.Error(err) |
| 548 |
+ |
|
| 551 | 549 |
} |
| 552 | 550 |
} |
| 553 | 551 |
return engine.StatusOK |
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/docker/daemon/networkdriver/portmapper" |
| 9 | 9 |
"github.com/docker/docker/engine" |
| 10 |
+ "github.com/docker/docker/pkg/iptables" |
|
| 10 | 11 |
) |
| 11 | 12 |
|
| 12 | 13 |
func init() {
|
| ... | ... |
@@ -118,3 +119,43 @@ func TestMacAddrGeneration(t *testing.T) {
|
| 118 | 118 |
t.Fatal("Non-unique MAC address")
|
| 119 | 119 |
} |
| 120 | 120 |
} |
| 121 |
+ |
|
| 122 |
+func TestLinkContainers(t *testing.T) {
|
|
| 123 |
+ eng := engine.New() |
|
| 124 |
+ eng.Logging = false |
|
| 125 |
+ |
|
| 126 |
+ // Init driver |
|
| 127 |
+ job := eng.Job("initdriver")
|
|
| 128 |
+ if res := InitDriver(job); res != engine.StatusOK {
|
|
| 129 |
+ t.Fatal("Failed to initialize network driver")
|
|
| 130 |
+ } |
|
| 131 |
+ |
|
| 132 |
+ // Allocate interface |
|
| 133 |
+ job = eng.Job("allocate_interface", "container_id")
|
|
| 134 |
+ if res := Allocate(job); res != engine.StatusOK {
|
|
| 135 |
+ t.Fatal("Failed to allocate network interface")
|
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ job.Args[0] = "-I" |
|
| 139 |
+ |
|
| 140 |
+ job.Setenv("ChildIP", "172.17.0.2")
|
|
| 141 |
+ job.Setenv("ParentIP", "172.17.0.1")
|
|
| 142 |
+ job.SetenvBool("IgnoreErrors", false)
|
|
| 143 |
+ job.SetenvList("Ports", []string{"1234"})
|
|
| 144 |
+ |
|
| 145 |
+ bridgeIface = "lo" |
|
| 146 |
+ _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
|
|
| 147 |
+ if err != nil {
|
|
| 148 |
+ t.Fatal(err) |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ if res := LinkContainers(job); res != engine.StatusOK {
|
|
| 152 |
+ t.Fatalf("LinkContainers failed")
|
|
| 153 |
+ } |
|
| 154 |
+ |
|
| 155 |
+ // flush rules |
|
| 156 |
+ if _, err = iptables.Raw([]string{"-F", "DOCKER"}...); err != nil {
|
|
| 157 |
+ t.Fatal(err) |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+} |
| ... | ... |
@@ -83,8 +83,8 @@ func TestLinksIpTablesRulesWhenLinkAndUnlink(t *testing.T) {
|
| 83 | 83 |
childIP := findContainerIP(t, "child") |
| 84 | 84 |
parentIP := findContainerIP(t, "parent") |
| 85 | 85 |
|
| 86 |
- sourceRule := []string{"FORWARD", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
|
|
| 87 |
- destinationRule := []string{"FORWARD", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
|
|
| 86 |
+ sourceRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
|
|
| 87 |
+ destinationRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
|
|
| 88 | 88 |
if !iptables.Exists(sourceRule...) || !iptables.Exists(destinationRule...) {
|
| 89 | 89 |
t.Fatal("Iptables rules not found")
|
| 90 | 90 |
} |
| ... | ... |
@@ -2070,7 +2070,7 @@ func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
|
| 2070 | 2070 |
if err != nil {
|
| 2071 | 2071 |
t.Fatal(err) |
| 2072 | 2072 |
} |
| 2073 |
- iptCmd := exec.Command("iptables", "-D", "FORWARD", "-d", fmt.Sprintf("%s/32", ip),
|
|
| 2073 |
+ iptCmd := exec.Command("iptables", "-D", "DOCKER", "-d", fmt.Sprintf("%s/32", ip),
|
|
| 2074 | 2074 |
"!", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-m", "tcp", "--dport", "23", "-j", "ACCEPT") |
| 2075 | 2075 |
out, _, err = runCommandWithOutput(iptCmd) |
| 2076 | 2076 |
if err != nil {
|
| ... | ... |
@@ -114,7 +114,7 @@ func RemoveExistingChain(name string, table Table) error {
|
| 114 | 114 |
} |
| 115 | 115 |
|
| 116 | 116 |
// Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table |
| 117 |
-func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
|
|
| 117 |
+func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
|
|
| 118 | 118 |
daddr := ip.String() |
| 119 | 119 |
if ip.IsUnspecified() {
|
| 120 | 120 |
// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we |
| ... | ... |
@@ -128,7 +128,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str |
| 128 | 128 |
"--dport", strconv.Itoa(port), |
| 129 | 129 |
"!", "-i", c.Bridge, |
| 130 | 130 |
"-j", "DNAT", |
| 131 |
- "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))); err != nil {
|
|
| 131 |
+ "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
|
|
| 132 | 132 |
return err |
| 133 | 133 |
} else if len(output) != 0 {
|
| 134 | 134 |
return &ChainError{Chain: "FORWARD", Output: output}
|
| ... | ... |
@@ -138,14 +138,25 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str |
| 138 | 138 |
"!", "-i", c.Bridge, |
| 139 | 139 |
"-o", c.Bridge, |
| 140 | 140 |
"-p", proto, |
| 141 |
- "-d", dest_addr, |
|
| 142 |
- "--dport", strconv.Itoa(dest_port), |
|
| 141 |
+ "-d", destAddr, |
|
| 142 |
+ "--dport", strconv.Itoa(destPort), |
|
| 143 | 143 |
"-j", "ACCEPT"); err != nil {
|
| 144 | 144 |
return err |
| 145 | 145 |
} else if len(output) != 0 {
|
| 146 | 146 |
return &ChainError{Chain: "FORWARD", Output: output}
|
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 |
+ if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING",
|
|
| 150 |
+ "-p", proto, |
|
| 151 |
+ "-s", destAddr, |
|
| 152 |
+ "-d", destAddr, |
|
| 153 |
+ "--dport", strconv.Itoa(destPort), |
|
| 154 |
+ "-j", "MASQUERADE"); err != nil {
|
|
| 155 |
+ return err |
|
| 156 |
+ } else if len(output) != 0 {
|
|
| 157 |
+ return &ChainError{Chain: "FORWARD", Output: output}
|
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 149 | 160 |
return nil |
| 150 | 161 |
} |
| 151 | 162 |
|
| ... | ... |
@@ -156,8 +167,8 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err |
| 156 | 156 |
"-i", c.Bridge, "-o", c.Bridge, |
| 157 | 157 |
"-p", proto, |
| 158 | 158 |
"-s", ip1.String(), |
| 159 |
- "--dport", strconv.Itoa(port), |
|
| 160 | 159 |
"-d", ip2.String(), |
| 160 |
+ "--dport", strconv.Itoa(port), |
|
| 161 | 161 |
"-j", "ACCEPT"); err != nil {
|
| 162 | 162 |
return err |
| 163 | 163 |
} else if len(output) != 0 {
|
| ... | ... |
@@ -167,8 +178,8 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err |
| 167 | 167 |
"-i", c.Bridge, "-o", c.Bridge, |
| 168 | 168 |
"-p", proto, |
| 169 | 169 |
"-s", ip2.String(), |
| 170 |
- "--dport", strconv.Itoa(port), |
|
| 171 | 170 |
"-d", ip1.String(), |
| 171 |
+ "--sport", strconv.Itoa(port), |
|
| 172 | 172 |
"-j", "ACCEPT"); err != nil {
|
| 173 | 173 |
return err |
| 174 | 174 |
} else if len(output) != 0 {
|
| ... | ... |
@@ -206,18 +217,17 @@ func (c *Chain) Output(action Action, args ...string) error {
|
| 206 | 206 |
} |
| 207 | 207 |
|
| 208 | 208 |
func (c *Chain) Remove() error {
|
| 209 |
+ // Ignore errors - This could mean the chains were never set up |
|
| 209 | 210 |
if c.Table == Nat {
|
| 210 |
- // Ignore errors - This could mean the chains were never set up |
|
| 211 | 211 |
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL") |
| 212 | 212 |
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8") |
| 213 | 213 |
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6 |
| 214 | 214 |
|
| 215 | 215 |
c.Prerouting(Delete) |
| 216 | 216 |
c.Output(Delete) |
| 217 |
- |
|
| 218 |
- Raw("-t", string(Nat), "-F", c.Name)
|
|
| 219 |
- Raw("-t", string(Nat), "-X", c.Name)
|
|
| 220 | 217 |
} |
| 218 |
+ Raw("-t", string(c.Table), "-F", c.Name)
|
|
| 219 |
+ Raw("-t", string(c.Table), "-X", c.Name)
|
|
| 221 | 220 |
return nil |
| 222 | 221 |
} |
| 223 | 222 |
|
| 224 | 223 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,204 @@ |
| 0 |
+package iptables |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "os/exec" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "strings" |
|
| 7 |
+ "testing" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+const chainName = "DOCKERTEST" |
|
| 11 |
+ |
|
| 12 |
+var natChain *Chain |
|
| 13 |
+var filterChain *Chain |
|
| 14 |
+ |
|
| 15 |
+func TestNewChain(t *testing.T) {
|
|
| 16 |
+ var err error |
|
| 17 |
+ |
|
| 18 |
+ natChain, err = NewChain(chainName, "lo", Nat) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ t.Fatal(err) |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ filterChain, err = NewChain(chainName, "lo", Filter) |
|
| 24 |
+ if err != nil {
|
|
| 25 |
+ t.Fatal(err) |
|
| 26 |
+ } |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func TestForward(t *testing.T) {
|
|
| 30 |
+ ip := net.ParseIP("192.168.1.1")
|
|
| 31 |
+ port := 1234 |
|
| 32 |
+ dstAddr := "172.17.0.1" |
|
| 33 |
+ dstPort := 4321 |
|
| 34 |
+ proto := "tcp" |
|
| 35 |
+ |
|
| 36 |
+ err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ t.Fatal(err) |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ dnatRule := []string{natChain.Name,
|
|
| 42 |
+ "-t", string(natChain.Table), |
|
| 43 |
+ "!", "-i", filterChain.Bridge, |
|
| 44 |
+ "-d", ip.String(), |
|
| 45 |
+ "-p", proto, |
|
| 46 |
+ "--dport", strconv.Itoa(port), |
|
| 47 |
+ "-j", "DNAT", |
|
| 48 |
+ "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort), |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ if !Exists(dnatRule...) {
|
|
| 52 |
+ t.Fatalf("DNAT rule does not exist")
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ filterRule := []string{filterChain.Name,
|
|
| 56 |
+ "-t", string(filterChain.Table), |
|
| 57 |
+ "!", "-i", filterChain.Bridge, |
|
| 58 |
+ "-o", filterChain.Bridge, |
|
| 59 |
+ "-d", dstAddr, |
|
| 60 |
+ "-p", proto, |
|
| 61 |
+ "--dport", strconv.Itoa(dstPort), |
|
| 62 |
+ "-j", "ACCEPT", |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ if !Exists(filterRule...) {
|
|
| 66 |
+ t.Fatalf("filter rule does not exist")
|
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ masqRule := []string{"POSTROUTING",
|
|
| 70 |
+ "-t", string(natChain.Table), |
|
| 71 |
+ "-d", dstAddr, |
|
| 72 |
+ "-s", dstAddr, |
|
| 73 |
+ "-p", proto, |
|
| 74 |
+ "--dport", strconv.Itoa(dstPort), |
|
| 75 |
+ "-j", "MASQUERADE", |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ if !Exists(masqRule...) {
|
|
| 79 |
+ t.Fatalf("MASQUERADE rule does not exist")
|
|
| 80 |
+ } |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func TestLink(t *testing.T) {
|
|
| 84 |
+ var err error |
|
| 85 |
+ |
|
| 86 |
+ ip1 := net.ParseIP("192.168.1.1")
|
|
| 87 |
+ ip2 := net.ParseIP("192.168.1.2")
|
|
| 88 |
+ port := 1234 |
|
| 89 |
+ proto := "tcp" |
|
| 90 |
+ |
|
| 91 |
+ err = filterChain.Link(Append, ip1, ip2, port, proto) |
|
| 92 |
+ if err != nil {
|
|
| 93 |
+ t.Fatal(err) |
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ rule1 := []string{filterChain.Name,
|
|
| 97 |
+ "-t", string(filterChain.Table), |
|
| 98 |
+ "-i", filterChain.Bridge, |
|
| 99 |
+ "-o", filterChain.Bridge, |
|
| 100 |
+ "-p", proto, |
|
| 101 |
+ "-s", ip1.String(), |
|
| 102 |
+ "-d", ip2.String(), |
|
| 103 |
+ "--dport", strconv.Itoa(port), |
|
| 104 |
+ "-j", "ACCEPT"} |
|
| 105 |
+ |
|
| 106 |
+ if !Exists(rule1...) {
|
|
| 107 |
+ t.Fatalf("rule1 does not exist")
|
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ rule2 := []string{filterChain.Name,
|
|
| 111 |
+ "-t", string(filterChain.Table), |
|
| 112 |
+ "-i", filterChain.Bridge, |
|
| 113 |
+ "-o", filterChain.Bridge, |
|
| 114 |
+ "-p", proto, |
|
| 115 |
+ "-s", ip2.String(), |
|
| 116 |
+ "-d", ip1.String(), |
|
| 117 |
+ "--sport", strconv.Itoa(port), |
|
| 118 |
+ "-j", "ACCEPT"} |
|
| 119 |
+ |
|
| 120 |
+ if !Exists(rule2...) {
|
|
| 121 |
+ t.Fatalf("rule2 does not exist")
|
|
| 122 |
+ } |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+func TestPrerouting(t *testing.T) {
|
|
| 126 |
+ args := []string{
|
|
| 127 |
+ "-i", "lo", |
|
| 128 |
+ "-d", "192.168.1.1"} |
|
| 129 |
+ |
|
| 130 |
+ err := natChain.Prerouting(Insert, args...) |
|
| 131 |
+ if err != nil {
|
|
| 132 |
+ t.Fatal(err) |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ rule := []string{"PREROUTING",
|
|
| 136 |
+ "-t", string(Nat), |
|
| 137 |
+ "-j", natChain.Name} |
|
| 138 |
+ |
|
| 139 |
+ rule = append(rule, args...) |
|
| 140 |
+ |
|
| 141 |
+ if !Exists(rule...) {
|
|
| 142 |
+ t.Fatalf("rule does not exist")
|
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ delRule := append([]string{"-D"}, rule...)
|
|
| 146 |
+ if _, err = Raw(delRule...); err != nil {
|
|
| 147 |
+ t.Fatal(err) |
|
| 148 |
+ } |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func TestOutput(t *testing.T) {
|
|
| 152 |
+ args := []string{
|
|
| 153 |
+ "-o", "lo", |
|
| 154 |
+ "-d", "192.168.1.1"} |
|
| 155 |
+ |
|
| 156 |
+ err := natChain.Output(Insert, args...) |
|
| 157 |
+ if err != nil {
|
|
| 158 |
+ t.Fatal(err) |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ rule := []string{"OUTPUT",
|
|
| 162 |
+ "-t", string(natChain.Table), |
|
| 163 |
+ "-j", natChain.Name} |
|
| 164 |
+ |
|
| 165 |
+ rule = append(rule, args...) |
|
| 166 |
+ |
|
| 167 |
+ if !Exists(rule...) {
|
|
| 168 |
+ t.Fatalf("rule does not exist")
|
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ delRule := append([]string{"-D"}, rule...)
|
|
| 172 |
+ if _, err = Raw(delRule...); err != nil {
|
|
| 173 |
+ t.Fatal(err) |
|
| 174 |
+ } |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func TestCleanup(t *testing.T) {
|
|
| 178 |
+ var err error |
|
| 179 |
+ var rules []byte |
|
| 180 |
+ |
|
| 181 |
+ // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty |
|
| 182 |
+ link := []string{"-t", string(filterChain.Table),
|
|
| 183 |
+ string(Delete), "FORWARD", |
|
| 184 |
+ "-o", filterChain.Bridge, |
|
| 185 |
+ "-j", filterChain.Name} |
|
| 186 |
+ if _, err = Raw(link...); err != nil {
|
|
| 187 |
+ t.Fatal(err) |
|
| 188 |
+ } |
|
| 189 |
+ filterChain.Remove() |
|
| 190 |
+ |
|
| 191 |
+ err = RemoveExistingChain(chainName, Nat) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ t.Fatal(err) |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ rules, err = exec.Command("iptables-save").Output()
|
|
| 197 |
+ if err != nil {
|
|
| 198 |
+ t.Fatal(err) |
|
| 199 |
+ } |
|
| 200 |
+ if strings.Contains(string(rules), chainName) {
|
|
| 201 |
+ t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
|
|
| 202 |
+ } |
|
| 203 |
+} |