github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/network/iptables/iptables_test.go (about) 1 package iptables_test 2 3 import ( 4 "errors" 5 "net" 6 "os/exec" 7 8 "github.com/cloudfoundry-incubator/garden" 9 . "github.com/cloudfoundry-incubator/garden-linux/network/iptables" 10 "github.com/cloudfoundry/gunk/command_runner/fake_command_runner" 11 . "github.com/cloudfoundry/gunk/command_runner/fake_command_runner/matchers" 12 "github.com/pivotal-golang/lager/lagertest" 13 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 var _ = Describe("Iptables", func() { 19 Describe("Chain", func() { 20 var fakeRunner *fake_command_runner.FakeCommandRunner 21 var subject Chain 22 var useKernelLogging bool 23 24 JustBeforeEach(func() { 25 fakeRunner = fake_command_runner.New() 26 subject = NewLoggingChain("foo-bar-baz", useKernelLogging, fakeRunner, lagertest.NewTestLogger("test")) 27 }) 28 29 Describe("Setup", func() { 30 Context("when kernel logging is not enabled", func() { 31 It("creates the log chain using iptables", func() { 32 Expect(subject.Setup("logPrefix")).To(Succeed()) 33 Expect(fakeRunner).To(HaveExecutedSerially( 34 fake_command_runner.CommandSpec{ 35 Path: "/sbin/iptables", 36 Args: []string{"-w", "-F", "foo-bar-baz-log"}, 37 }, 38 fake_command_runner.CommandSpec{ 39 Path: "/sbin/iptables", 40 Args: []string{"-w", "-X", "foo-bar-baz-log"}, 41 }, 42 fake_command_runner.CommandSpec{ 43 Path: "/sbin/iptables", 44 Args: []string{"-w", "-N", "foo-bar-baz-log"}, 45 }, 46 fake_command_runner.CommandSpec{ 47 Path: "/sbin/iptables", 48 Args: []string{"-w", "-A", "foo-bar-baz-log", "-m", "conntrack", "--ctstate", "NEW,UNTRACKED,INVALID", "--protocol", "tcp", "--jump", "NFLOG", "--nflog-prefix", "logPrefix", "--nflog-group", "1"}, 49 }, 50 fake_command_runner.CommandSpec{ 51 Path: "/sbin/iptables", 52 Args: []string{"-w", "-A", "foo-bar-baz-log", "--jump", "RETURN"}, 53 })) 54 }) 55 }) 56 57 Context("when kernel logging is enabled", func() { 58 BeforeEach(func() { 59 useKernelLogging = true 60 }) 61 62 It("creates the log chain using iptables", func() { 63 Expect(subject.Setup("logPrefix")).To(Succeed()) 64 Expect(fakeRunner).To(HaveExecutedSerially( 65 fake_command_runner.CommandSpec{ 66 Path: "/sbin/iptables", 67 Args: []string{"-w", "-F", "foo-bar-baz-log"}, 68 }, 69 fake_command_runner.CommandSpec{ 70 Path: "/sbin/iptables", 71 Args: []string{"-w", "-X", "foo-bar-baz-log"}, 72 }, 73 fake_command_runner.CommandSpec{ 74 Path: "/sbin/iptables", 75 Args: []string{"-w", "-N", "foo-bar-baz-log"}, 76 }, 77 fake_command_runner.CommandSpec{ 78 Path: "/sbin/iptables", 79 Args: []string{"-w", "-A", "foo-bar-baz-log", "-m", "conntrack", "--ctstate", "NEW,UNTRACKED,INVALID", "--protocol", "tcp", 80 "--jump", "LOG", "--log-prefix", "logPrefix"}, 81 }, 82 fake_command_runner.CommandSpec{ 83 Path: "/sbin/iptables", 84 Args: []string{"-w", "-A", "foo-bar-baz-log", "--jump", "RETURN"}, 85 })) 86 }) 87 }) 88 89 It("ignores failures to flush", func() { 90 someError := errors.New("y") 91 fakeRunner.WhenRunning( 92 fake_command_runner.CommandSpec{ 93 Path: "/sbin/iptables", 94 Args: []string{"-w", "-F", "foo-bar-baz-log"}, 95 }, 96 func(cmd *exec.Cmd) error { 97 return someError 98 }) 99 100 Expect(subject.Setup("logPrefix")).To(Succeed()) 101 }) 102 103 It("ignores failures to delete", func() { 104 someError := errors.New("y") 105 fakeRunner.WhenRunning( 106 fake_command_runner.CommandSpec{ 107 Path: "/sbin/iptables", 108 Args: []string{"-w", "-X", "foo-bar-baz-log"}, 109 }, 110 func(cmd *exec.Cmd) error { 111 return someError 112 }) 113 114 Expect(subject.Setup("logPrefix")).To(Succeed()) 115 }) 116 117 It("returns any error returned when the table is created", func() { 118 someError := errors.New("y") 119 fakeRunner.WhenRunning( 120 fake_command_runner.CommandSpec{ 121 Path: "/sbin/iptables", 122 Args: []string{"-w", "-N", "foo-bar-baz-log"}, 123 }, 124 func(cmd *exec.Cmd) error { 125 return someError 126 }) 127 128 Expect(subject.Setup("logPrefix")).To(MatchError("iptables: log chain setup: y")) 129 }) 130 131 It("returns any error returned when the logging rule is added", func() { 132 someError := errors.New("y") 133 fakeRunner.WhenRunning( 134 fake_command_runner.CommandSpec{ 135 Path: "/sbin/iptables", 136 Args: []string{"-w", "-A", "foo-bar-baz-log", "-m", "conntrack", "--ctstate", "NEW,UNTRACKED,INVALID", "--protocol", "tcp", "--jump", "LOG", "--log-prefix", "logPrefix"}, 137 }, 138 func(cmd *exec.Cmd) error { 139 return someError 140 }) 141 142 Expect(subject.Setup("logPrefix")).To(MatchError("iptables: log chain setup: y")) 143 }) 144 145 It("returns any error returned when the RETURN rule is added", func() { 146 someError := errors.New("y") 147 fakeRunner.WhenRunning( 148 fake_command_runner.CommandSpec{ 149 Path: "/sbin/iptables", 150 Args: []string{"-w", "-A", "foo-bar-baz-log", "--jump", "RETURN"}, 151 }, 152 func(cmd *exec.Cmd) error { 153 return someError 154 }) 155 156 Expect(subject.Setup("logPrefix")).To(MatchError("iptables: log chain setup: y")) 157 }) 158 }) 159 160 Describe("TearDown", func() { 161 It("should flush and delete the underlying iptables log chain", func() { 162 Expect(subject.TearDown()).To(Succeed()) 163 Expect(fakeRunner).To(HaveExecutedSerially( 164 fake_command_runner.CommandSpec{ 165 Path: "/sbin/iptables", 166 Args: []string{"-w", "-F", "foo-bar-baz-log"}, 167 }, 168 fake_command_runner.CommandSpec{ 169 Path: "/sbin/iptables", 170 Args: []string{"-w", "-X", "foo-bar-baz-log"}, 171 })) 172 }) 173 174 It("ignores failures to flush", func() { 175 someError := errors.New("y") 176 fakeRunner.WhenRunning( 177 fake_command_runner.CommandSpec{ 178 Path: "/sbin/iptables", 179 Args: []string{"-w", "-F", "foo-bar-baz-log"}, 180 }, 181 func(cmd *exec.Cmd) error { 182 return someError 183 }) 184 185 Expect(subject.TearDown()).To(Succeed()) 186 }) 187 188 It("ignores failures to delete", func() { 189 someError := errors.New("y") 190 fakeRunner.WhenRunning( 191 fake_command_runner.CommandSpec{ 192 Path: "/sbin/iptables", 193 Args: []string{"-w", "-X", "foo-bar-baz-log"}, 194 }, 195 func(cmd *exec.Cmd) error { 196 return someError 197 }) 198 199 Expect(subject.TearDown()).To(Succeed()) 200 }) 201 202 }) 203 204 Describe("AppendRule", func() { 205 It("runs iptables to create the rule with the correct parameters", func() { 206 subject.AppendRule("", "2.0.0.0/11", Return) 207 208 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 209 Path: "/sbin/iptables", 210 Args: []string{"-w", "-A", "foo-bar-baz", "--destination", "2.0.0.0/11", "--jump", "RETURN"}, 211 })) 212 }) 213 }) 214 215 Describe("AppendNatRule", func() { 216 Context("creating a rule", func() { 217 Context("when all parameters are specified", func() { 218 It("runs iptables to create the rule with the correct parameters", func() { 219 subject.AppendNatRule("1.3.5.0/28", "2.0.0.0/11", Return, net.ParseIP("1.2.3.4")) 220 221 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 222 Path: "/sbin/iptables", 223 Args: []string{"-w", "-t", "nat", "-A", "foo-bar-baz", "--source", "1.3.5.0/28", "--destination", "2.0.0.0/11", "--jump", "RETURN", "--to", "1.2.3.4"}, 224 })) 225 }) 226 }) 227 228 Context("when Source is not specified", func() { 229 It("does not include the --source parameter in the command", func() { 230 subject.AppendNatRule("", "2.0.0.0/11", Return, net.ParseIP("1.2.3.4")) 231 232 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 233 Path: "/sbin/iptables", 234 Args: []string{"-w", "-t", "nat", "-A", "foo-bar-baz", "--destination", "2.0.0.0/11", "--jump", "RETURN", "--to", "1.2.3.4"}, 235 })) 236 }) 237 }) 238 239 Context("when Destination is not specified", func() { 240 It("does not include the --destination parameter in the command", func() { 241 subject.AppendNatRule("1.3.5.0/28", "", Return, net.ParseIP("1.2.3.4")) 242 243 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 244 Path: "/sbin/iptables", 245 Args: []string{"-w", "-t", "nat", "-A", "foo-bar-baz", "--source", "1.3.5.0/28", "--jump", "RETURN", "--to", "1.2.3.4"}, 246 })) 247 }) 248 }) 249 250 Context("when To is not specified", func() { 251 It("does not include the --to parameter in the command", func() { 252 subject.AppendNatRule("1.3.5.0/28", "2.0.0.0/11", Return, nil) 253 254 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 255 Path: "/sbin/iptables", 256 Args: []string{"-w", "-t", "nat", "-A", "foo-bar-baz", "--source", "1.3.5.0/28", "--destination", "2.0.0.0/11", "--jump", "RETURN"}, 257 })) 258 }) 259 }) 260 261 Context("when the command returns an error", func() { 262 It("returns an error", func() { 263 someError := errors.New("badly laid iptable") 264 fakeRunner.WhenRunning( 265 fake_command_runner.CommandSpec{Path: "/sbin/iptables"}, 266 func(cmd *exec.Cmd) error { 267 return someError 268 }, 269 ) 270 271 Expect(subject.AppendRule("1.2.3.4/5", "", "")).ToNot(Succeed()) 272 }) 273 }) 274 }) 275 276 Describe("DeleteRule", func() { 277 It("runs iptables to delete the rule with the correct parameters", func() { 278 subject.DeleteRule("", "2.0.0.0/11", Return) 279 280 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 281 Path: "/sbin/iptables", 282 Args: []string{"-w", "-D", "foo-bar-baz", "--destination", "2.0.0.0/11", "--jump", "RETURN"}, 283 })) 284 }) 285 }) 286 287 Context("DeleteNatRule", func() { 288 Context("when all parameters are specified", func() { 289 It("runs iptables to delete the rule with the correct parameters", func() { 290 subject.DeleteNatRule("1.3.5.0/28", "2.0.0.0/11", Return, net.ParseIP("1.2.3.4")) 291 292 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 293 Path: "/sbin/iptables", 294 Args: []string{"-w", "-t", "nat", "-D", "foo-bar-baz", "--source", "1.3.5.0/28", "--destination", "2.0.0.0/11", "--jump", "RETURN", "--to", "1.2.3.4"}, 295 })) 296 }) 297 }) 298 299 Context("when Source is not specified", func() { 300 It("does not include the --source parameter in the command", func() { 301 subject.DeleteNatRule("", "2.0.0.0/11", Return, net.ParseIP("1.2.3.4")) 302 303 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 304 Path: "/sbin/iptables", 305 Args: []string{"-w", "-t", "nat", "-D", "foo-bar-baz", "--destination", "2.0.0.0/11", "--jump", "RETURN", "--to", "1.2.3.4"}, 306 })) 307 }) 308 }) 309 310 Context("when Destination is not specified", func() { 311 It("does not include the --destination parameter in the command", func() { 312 subject.DeleteNatRule("1.3.5.0/28", "", Return, net.ParseIP("1.2.3.4")) 313 314 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 315 Path: "/sbin/iptables", 316 Args: []string{"-w", "-t", "nat", "-D", "foo-bar-baz", "--source", "1.3.5.0/28", "--jump", "RETURN", "--to", "1.2.3.4"}, 317 })) 318 }) 319 }) 320 321 Context("when To is not specified", func() { 322 It("does not include the --to parameter in the command", func() { 323 subject.DeleteNatRule("1.3.5.0/28", "2.0.0.0/11", Return, nil) 324 325 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 326 Path: "/sbin/iptables", 327 Args: []string{"-w", "-t", "nat", "-D", "foo-bar-baz", "--source", "1.3.5.0/28", "--destination", "2.0.0.0/11", "--jump", "RETURN"}, 328 })) 329 }) 330 }) 331 332 Context("when the command returns an error", func() { 333 It("returns an error", func() { 334 someError := errors.New("badly laid iptable") 335 fakeRunner.WhenRunning( 336 fake_command_runner.CommandSpec{Path: "/sbin/iptables"}, 337 func(cmd *exec.Cmd) error { 338 return someError 339 }, 340 ) 341 342 Expect(subject.DeleteNatRule("1.3.4.5/6", "", "", nil)).ToNot(Succeed()) 343 }) 344 }) 345 }) 346 347 Describe("PrependFilterRule", func() { 348 Context("when all parameters are defaulted", func() { 349 It("runs iptables with appropriate parameters", func() { 350 Expect(subject.PrependFilterRule(garden.NetOutRule{})).To(Succeed()) 351 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 352 Path: "/sbin/iptables", 353 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--jump", "RETURN"}, 354 })) 355 }) 356 }) 357 358 Describe("Network", func() { 359 Context("when an empty IPRange is specified", func() { 360 It("does not limit the range", func() { 361 Expect(subject.PrependFilterRule(garden.NetOutRule{ 362 Networks: []garden.IPRange{ 363 garden.IPRange{}, 364 }, 365 })).To(Succeed()) 366 367 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 368 Path: "/sbin/iptables", 369 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--jump", "RETURN"}, 370 })) 371 }) 372 }) 373 374 Context("when a single destination IP is specified", func() { 375 It("opens only that IP", func() { 376 Expect(subject.PrependFilterRule(garden.NetOutRule{ 377 Networks: []garden.IPRange{ 378 { 379 Start: net.ParseIP("1.2.3.4"), 380 }, 381 }, 382 })).To(Succeed()) 383 384 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 385 Path: "/sbin/iptables", 386 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--destination", "1.2.3.4", "--jump", "RETURN"}, 387 })) 388 }) 389 }) 390 391 Context("when a multiple destination networks are specified", func() { 392 It("opens only that IP", func() { 393 Expect(subject.PrependFilterRule(garden.NetOutRule{ 394 Networks: []garden.IPRange{ 395 { 396 Start: net.ParseIP("1.2.3.4"), 397 }, 398 { 399 Start: net.ParseIP("2.2.3.4"), 400 End: net.ParseIP("2.2.3.9"), 401 }, 402 }, 403 })).To(Succeed()) 404 405 Expect(fakeRunner.ExecutedCommands()).To(HaveLen(2)) 406 Expect(fakeRunner).To(HaveExecutedSerially( 407 fake_command_runner.CommandSpec{ 408 Path: "/sbin/iptables", 409 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--destination", "1.2.3.4", "--jump", "RETURN"}, 410 }, 411 fake_command_runner.CommandSpec{ 412 Path: "/sbin/iptables", 413 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "-m", "iprange", "--dst-range", "2.2.3.4-2.2.3.9", "--jump", "RETURN"}, 414 }, 415 )) 416 }) 417 }) 418 419 Context("when a EndIP is specified without a StartIP", func() { 420 It("opens only that IP", func() { 421 Expect(subject.PrependFilterRule(garden.NetOutRule{ 422 Networks: []garden.IPRange{ 423 { 424 End: net.ParseIP("1.2.3.4"), 425 }, 426 }, 427 })).To(Succeed()) 428 429 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 430 Path: "/sbin/iptables", 431 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--destination", "1.2.3.4", "--jump", "RETURN"}, 432 })) 433 }) 434 }) 435 436 Context("when a range of IPs is specified", func() { 437 It("opens only the range", func() { 438 Expect(subject.PrependFilterRule(garden.NetOutRule{ 439 Networks: []garden.IPRange{ 440 { 441 net.ParseIP("1.2.3.4"), net.ParseIP("2.3.4.5"), 442 }, 443 }, 444 })).To(Succeed()) 445 446 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 447 Path: "/sbin/iptables", 448 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "-m", "iprange", "--dst-range", "1.2.3.4-2.3.4.5", "--jump", "RETURN"}, 449 })) 450 }) 451 }) 452 }) 453 454 Describe("Ports", func() { 455 Context("when a single port is specified", func() { 456 It("opens only that port", func() { 457 Expect(subject.PrependFilterRule(garden.NetOutRule{ 458 Protocol: garden.ProtocolTCP, 459 Ports: []garden.PortRange{ 460 garden.PortRangeFromPort(22), 461 }, 462 })).To(Succeed()) 463 464 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 465 Path: "/sbin/iptables", 466 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination-port", "22", "--jump", "RETURN"}, 467 })) 468 }) 469 }) 470 471 Context("when a port range is specified", func() { 472 It("opens that port range", func() { 473 Expect(subject.PrependFilterRule(garden.NetOutRule{ 474 Protocol: garden.ProtocolTCP, 475 Ports: []garden.PortRange{ 476 {12, 24}, 477 }, 478 })).To(Succeed()) 479 480 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 481 Path: "/sbin/iptables", 482 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination-port", "12:24", "--jump", "RETURN"}, 483 })) 484 }) 485 }) 486 487 Context("when multiple port ranges are specified", func() { 488 It("opens those port ranges", func() { 489 Expect(subject.PrependFilterRule(garden.NetOutRule{ 490 Protocol: garden.ProtocolTCP, 491 Ports: []garden.PortRange{ 492 {12, 24}, 493 {64, 942}, 494 }, 495 })).To(Succeed()) 496 497 Expect(fakeRunner).To(HaveExecutedSerially( 498 fake_command_runner.CommandSpec{ 499 Path: "/sbin/iptables", 500 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination-port", "12:24", "--jump", "RETURN"}, 501 }, 502 fake_command_runner.CommandSpec{ 503 Path: "/sbin/iptables", 504 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination-port", "64:942", "--jump", "RETURN"}, 505 }, 506 )) 507 }) 508 }) 509 }) 510 511 Describe("Protocol", func() { 512 Context("when tcp protocol is specified", func() { 513 It("passes tcp protocol to iptables", func() { 514 Expect(subject.PrependFilterRule(garden.NetOutRule{ 515 Protocol: garden.ProtocolTCP, 516 })).To(Succeed()) 517 518 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 519 Path: "/sbin/iptables", 520 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--jump", "RETURN"}, 521 })) 522 }) 523 }) 524 525 Context("when udp protocol is specified", func() { 526 It("passes udp protocol to iptables", func() { 527 Expect(subject.PrependFilterRule(garden.NetOutRule{ 528 Protocol: garden.ProtocolUDP, 529 })).To(Succeed()) 530 531 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 532 Path: "/sbin/iptables", 533 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "udp", "--jump", "RETURN"}, 534 })) 535 }) 536 }) 537 538 Context("when icmp protocol is specified", func() { 539 It("passes icmp protocol to iptables", func() { 540 Expect(subject.PrependFilterRule(garden.NetOutRule{ 541 Protocol: garden.ProtocolICMP, 542 })).To(Succeed()) 543 544 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 545 Path: "/sbin/iptables", 546 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "icmp", "--jump", "RETURN"}, 547 })) 548 }) 549 550 Context("when icmp type is specified", func() { 551 It("passes icmp protcol type to iptables", func() { 552 Expect(subject.PrependFilterRule(garden.NetOutRule{ 553 Protocol: garden.ProtocolICMP, 554 ICMPs: &garden.ICMPControl{ 555 Type: 99, 556 }, 557 })).To(Succeed()) 558 559 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 560 Path: "/sbin/iptables", 561 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "icmp", "--icmp-type", "99", "--jump", "RETURN"}, 562 })) 563 }) 564 }) 565 566 Context("when icmp type and code are specified", func() { 567 It("passes icmp protcol type and code to iptables", func() { 568 Expect(subject.PrependFilterRule(garden.NetOutRule{ 569 Protocol: garden.ProtocolICMP, 570 ICMPs: &garden.ICMPControl{ 571 Type: 99, 572 Code: garden.ICMPControlCode(11), 573 }, 574 })).To(Succeed()) 575 576 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 577 Path: "/sbin/iptables", 578 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "icmp", "--icmp-type", "99/11", "--jump", "RETURN"}, 579 })) 580 }) 581 }) 582 }) 583 }) 584 585 Describe("Log", func() { 586 It("redirects via the log chain if log is specified", func() { 587 Expect(subject.PrependFilterRule(garden.NetOutRule{ 588 Log: true, 589 })).To(Succeed()) 590 591 Expect(fakeRunner).To(HaveExecutedSerially(fake_command_runner.CommandSpec{ 592 Path: "/sbin/iptables", 593 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "all", "--goto", "foo-bar-baz-log"}, 594 })) 595 }) 596 }) 597 598 Context("when multiple port ranges and multiple networks are specified", func() { 599 It("opens the permutations of those port ranges and networks", func() { 600 Expect(subject.PrependFilterRule(garden.NetOutRule{ 601 Protocol: garden.ProtocolTCP, 602 Networks: []garden.IPRange{ 603 { 604 Start: net.ParseIP("1.2.3.4"), 605 }, 606 { 607 Start: net.ParseIP("2.2.3.4"), 608 End: net.ParseIP("2.2.3.9"), 609 }, 610 }, 611 Ports: []garden.PortRange{ 612 {12, 24}, 613 {64, 942}, 614 }, 615 })).To(Succeed()) 616 617 Expect(fakeRunner.ExecutedCommands()).To(HaveLen(4)) 618 Expect(fakeRunner).To(HaveExecutedSerially( 619 fake_command_runner.CommandSpec{ 620 Path: "/sbin/iptables", 621 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination", "1.2.3.4", "--destination-port", "12:24", "--jump", "RETURN"}, 622 }, 623 fake_command_runner.CommandSpec{ 624 Path: "/sbin/iptables", 625 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "--destination", "1.2.3.4", "--destination-port", "64:942", "--jump", "RETURN"}, 626 }, 627 fake_command_runner.CommandSpec{ 628 Path: "/sbin/iptables", 629 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "-m", "iprange", "--dst-range", "2.2.3.4-2.2.3.9", "--destination-port", "12:24", "--jump", "RETURN"}, 630 }, 631 fake_command_runner.CommandSpec{ 632 Path: "/sbin/iptables", 633 Args: []string{"-w", "-I", "foo-bar-baz", "1", "--protocol", "tcp", "-m", "iprange", "--dst-range", "2.2.3.4-2.2.3.9", "--destination-port", "64:942", "--jump", "RETURN"}, 634 }, 635 )) 636 }) 637 }) 638 639 Context("when a portrange is specified for ProtocolALL", func() { 640 It("returns a nice error message", func() { 641 Expect(subject.PrependFilterRule(garden.NetOutRule{ 642 Protocol: garden.ProtocolAll, 643 Ports: []garden.PortRange{{Start: 1, End: 5}}, 644 })).To(MatchError("Ports cannot be specified for Protocol ALL")) 645 }) 646 647 It("does not run iptables", func() { 648 subject.PrependFilterRule(garden.NetOutRule{ 649 Protocol: garden.ProtocolAll, 650 Ports: []garden.PortRange{{Start: 1, End: 5}}, 651 }) 652 653 Expect(fakeRunner.ExecutedCommands()).To(HaveLen(0)) 654 }) 655 }) 656 657 Context("when a portrange is specified for ProtocolICMP", func() { 658 It("returns a nice error message", func() { 659 Expect(subject.PrependFilterRule(garden.NetOutRule{ 660 Protocol: garden.ProtocolICMP, 661 Ports: []garden.PortRange{{Start: 1, End: 5}}, 662 })).To(MatchError("Ports cannot be specified for Protocol ICMP")) 663 }) 664 665 It("does not run iptables", func() { 666 subject.PrependFilterRule(garden.NetOutRule{ 667 Protocol: garden.ProtocolICMP, 668 Ports: []garden.PortRange{{Start: 1, End: 5}}, 669 }) 670 671 Expect(fakeRunner.ExecutedCommands()).To(HaveLen(0)) 672 }) 673 }) 674 675 Context("when an invaild protocol is specified", func() { 676 It("returns an error", func() { 677 err := subject.PrependFilterRule(garden.NetOutRule{ 678 Protocol: garden.Protocol(52), 679 }) 680 Expect(err).To(HaveOccurred()) 681 Expect(err).To(MatchError("invalid protocol: 52")) 682 }) 683 }) 684 685 Context("when the command returns an error", func() { 686 It("returns a wrapped error, including stderr", func() { 687 someError := errors.New("badly laid iptable") 688 fakeRunner.WhenRunning( 689 fake_command_runner.CommandSpec{Path: "/sbin/iptables"}, 690 func(cmd *exec.Cmd) error { 691 cmd.Stderr.Write([]byte("stderr contents")) 692 return someError 693 }, 694 ) 695 696 Expect(subject.PrependFilterRule(garden.NetOutRule{})).To(MatchError("iptables: badly laid iptable, stderr contents")) 697 }) 698 }) 699 }) 700 }) 701 }) 702 })