github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/acls/acl_test.go (about) 1 package acls 2 3 import ( 4 "net" 5 "testing" 6 7 . "github.com/smartystreets/goconvey/convey" 8 "go.aporeto.io/enforcerd/trireme-lib/controller/constants" 9 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet" 10 "go.aporeto.io/enforcerd/trireme-lib/policy" 11 ) 12 13 var ( 14 rules = policy.IPRuleList{ 15 policy.IPRule{ 16 Addresses: []string{"172.0.0.0/8"}, 17 Ports: []string{"400:500"}, 18 Protocols: []string{constants.TCPProtoNum}, 19 Policy: &policy.FlowPolicy{ 20 Action: policy.Accept, 21 PolicyID: "tcp172/8"}, 22 }, 23 policy.IPRule{ 24 Addresses: []string{"172.17.0.0/16"}, 25 Ports: []string{"400:500"}, 26 Protocols: []string{constants.TCPProtoNum}, 27 Policy: &policy.FlowPolicy{ 28 Action: policy.Accept, 29 PolicyID: "tcp172.17/16"}, 30 }, 31 policy.IPRule{ 32 Addresses: []string{"192.168.100.0/24"}, 33 Protocols: []string{constants.TCPProtoNum}, 34 Ports: []string{"80"}, 35 Policy: &policy.FlowPolicy{ 36 Action: policy.Accept, 37 PolicyID: "tcp192.168.100/24"}, 38 }, 39 policy.IPRule{ 40 Addresses: []string{"10.1.1.1"}, 41 Protocols: []string{constants.TCPProtoNum}, 42 Ports: []string{"80"}, 43 Policy: &policy.FlowPolicy{ 44 Action: policy.Accept, 45 PolicyID: "tcp10.1.1.1"}}, 46 policy.IPRule{ 47 Addresses: []string{"0.0.0.0/0"}, 48 Protocols: []string{constants.TCPProtoNum}, 49 Ports: []string{"443"}, 50 Policy: &policy.FlowPolicy{ 51 Action: policy.Accept, 52 PolicyID: "tcp0/0"}}, 53 policy.IPRule{ 54 Addresses: []string{"0.0.0.0/0"}, 55 Protocols: []string{constants.UDPProtoNum}, 56 Ports: []string{"443"}, 57 Policy: &policy.FlowPolicy{ 58 Action: policy.Accept, 59 PolicyID: "udp0/0"}}, 60 } 61 ) 62 63 func TestLookup(t *testing.T) { 64 65 Convey("Given a good DB", t, func() { 66 67 a := newACL() 68 So(a, ShouldNotBeNil) 69 for _, r := range rules { 70 err := a.addRule(r) 71 So(err, ShouldBeNil) 72 } 73 74 Convey("When I lookup for a matching address and a port range, I should get the right action", func() { 75 ip := net.ParseIP("172.17.0.1") 76 port := uint16(401) 77 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 78 So(err, ShouldBeNil) 79 So(p.Action, ShouldEqual, policy.Accept) 80 So(p.PolicyID, ShouldEqual, "tcp172.17/16") 81 So(r.Action, ShouldEqual, policy.Accept) 82 So(r.PolicyID, ShouldEqual, "tcp172.17/16") 83 }) 84 85 Convey("When I lookup for a matching address with less specific match and a port range, I should get the right action", func() { 86 ip := net.ParseIP("172.16.0.1") 87 port := uint16(401) 88 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 89 So(err, ShouldBeNil) 90 So(p.Action, ShouldEqual, policy.Accept) 91 So(p.PolicyID, ShouldEqual, "tcp172/8") 92 So(r.Action, ShouldEqual, policy.Accept) 93 So(r.PolicyID, ShouldEqual, "tcp172/8") 94 }) 95 96 Convey("When I lookup for a matching address exact port, I should get the right action", func() { 97 ip := net.ParseIP("192.168.100.1") 98 port := uint16(80) 99 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 100 So(err, ShouldBeNil) 101 So(p.Action, ShouldEqual, policy.Accept) 102 So(p.PolicyID, ShouldEqual, "tcp192.168.100/24") 103 So(r.Action, ShouldEqual, policy.Accept) 104 So(r.PolicyID, ShouldEqual, "tcp192.168.100/24") 105 }) 106 107 Convey("When I lookup for a non matching address . I should get reject", func() { 108 ip := net.ParseIP("192.168.200.1") 109 port := uint16(80) 110 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 111 So(err, ShouldNotBeNil) 112 So(p, ShouldBeNil) 113 So(r, ShouldBeNil) 114 }) 115 116 Convey("When I lookup for a matching address but failed port, I should get reject", func() { 117 ip := net.ParseIP("192.168.100.1") 118 port := uint16(600) 119 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 120 So(err, ShouldNotBeNil) 121 So(p, ShouldBeNil) 122 So(r, ShouldBeNil) 123 }) 124 125 Convey("When I lookup for a matching exact address exact port, I should get the right action", func() { 126 ip := net.ParseIP("10.1.1.1") 127 port := uint16(80) 128 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 129 So(err, ShouldBeNil) 130 So(p.Action, ShouldEqual, policy.Accept) 131 So(p.PolicyID, ShouldEqual, "tcp10.1.1.1") 132 So(r.Action, ShouldEqual, policy.Accept) 133 So(r.PolicyID, ShouldEqual, "tcp10.1.1.1") 134 }) 135 136 }) 137 } 138 139 func TestICMPMatch(t *testing.T) { 140 141 var icmpRules = policy.IPRuleList{ 142 policy.IPRule{ 143 Addresses: []string{"0.0.0.0/0"}, 144 Protocols: []string{"ICMP/8/1:3"}, 145 Ports: []string{}, 146 Policy: &policy.FlowPolicy{ 147 Action: policy.Accept, 148 PolicyID: "icmp0/0-8/1:3", 149 }, 150 }, 151 policy.IPRule{ 152 Addresses: []string{"684D:1111:222:3333:4444:5555:6:77"}, 153 Protocols: []string{"ICMP6"}, 154 Ports: []string{}, 155 Policy: &policy.FlowPolicy{ 156 Action: policy.Accept, 157 PolicyID: "icmp6", 158 }, 159 }, 160 policy.IPRule{ 161 Addresses: []string{"192.0.2.1"}, 162 Protocols: []string{"icmp"}, 163 Ports: []string{}, 164 Policy: &policy.FlowPolicy{ 165 Action: policy.Accept, 166 PolicyID: "removeme", 167 }, 168 }, 169 } 170 171 Convey("Given a good DB", t, func() { 172 173 a := newACL() 174 So(a, ShouldNotBeNil) 175 for _, r := range icmpRules { 176 err := a.addRule(r) 177 So(err, ShouldBeNil) 178 } 179 180 Convey("When I lookup for a matching address for icmp but wrong type or code, I should get the right action", func() { 181 ip := net.ParseIP("172.17.0.1") 182 r, p, err := a.matchICMPRule(ip.To4(), 8, 2) 183 So(err, ShouldBeNil) 184 So(p.Action, ShouldEqual, policy.Accept) 185 So(p.PolicyID, ShouldEqual, "icmp0/0-8/1:3") 186 So(r.Action, ShouldEqual, policy.Accept) 187 So(r.PolicyID, ShouldEqual, "icmp0/0-8/1:3") 188 }) 189 190 Convey("When I lookup for a matching address for icmp, I should not get a match", func() { 191 ip := net.ParseIP("172.17.0.1") 192 r, p, err := a.matchICMPRule(ip.To4(), 8, 4) 193 So(err, ShouldNotBeNil) 194 So(p, ShouldBeNil) 195 So(r, ShouldBeNil) 196 }) 197 198 Convey("When I lookup for a matching address for icmp6, I should get the right action", func() { 199 ip := net.ParseIP("684D:1111:222:3333:4444:5555:6:77") 200 r, p, err := a.matchICMPRule(ip, 8, 1) 201 So(err, ShouldBeNil) 202 So(p.Action, ShouldEqual, policy.Accept) 203 So(p.PolicyID, ShouldEqual, "icmp6") 204 So(r.Action, ShouldEqual, policy.Accept) 205 So(r.PolicyID, ShouldEqual, "icmp6") 206 }) 207 208 Convey("When I lookup for a non-matching address for icmp6, I should not get a match", func() { 209 ip := net.ParseIP("684D:1111:222:3333:4444:5555:6:77") 210 r, p, err := a.matchICMPRule(ip, 8, 1) 211 So(err, ShouldBeNil) 212 So(p.Action, ShouldEqual, policy.Accept) 213 So(p.PolicyID, ShouldEqual, "icmp6") 214 So(r.Action, ShouldEqual, policy.Accept) 215 So(r.PolicyID, ShouldEqual, "icmp6") 216 }) 217 }) 218 } 219 220 func TestICMPRemove(t *testing.T) { 221 222 var icmpRules = policy.IPRuleList{ 223 policy.IPRule{ 224 Addresses: []string{"192.0.2.1"}, 225 Protocols: []string{"icmp"}, 226 Ports: []string{}, 227 Policy: &policy.FlowPolicy{ 228 Action: policy.Accept, 229 PolicyID: "removeme", 230 }, 231 }, 232 } 233 234 removeMePolicy := &policy.FlowPolicy{ 235 Action: policy.Accept, 236 PolicyID: "removeme", 237 } 238 239 Convey("Given a good DB", t, func() { 240 241 a := newACL() 242 So(a, ShouldNotBeNil) 243 for _, r := range icmpRules { 244 err := a.addRule(r) 245 So(err, ShouldBeNil) 246 } 247 248 Convey("When I try to remove a rule which does not exist, then it should not error", func() { 249 ip := net.ParseIP("192.0.2.2") 250 err := a.removeFromCache(ip, 32, false, "icmp", nil, removeMePolicy) 251 So(err, ShouldBeNil) 252 }) 253 254 Convey("When I try to remove a rule which does not match, then nothing should change", func() { 255 ip := net.ParseIP("192.0.2.1") 256 oldVal, ok := a.icmpCache.Get(ip, 32) 257 So(ok, ShouldBeTrue) 258 old := oldVal.([]*icmpRule) 259 So(old, ShouldNotBeEmpty) 260 err := a.removeFromCache(ip, 32, false, "icmp", nil, removeMePolicy) 261 So(err, ShouldBeNil) 262 newVal, ok := a.icmpCache.Get(ip, 32) 263 So(ok, ShouldBeTrue) 264 new := newVal.([]*icmpRule) 265 So(new, ShouldNotBeEmpty) 266 So(old, ShouldResemble, new) 267 }) 268 269 Convey("When I try to remove a rule which matches, then it should get removed", func() { 270 ip := net.ParseIP("192.0.2.1") 271 oldVal, ok := a.icmpCache.Get(ip, 32) 272 So(ok, ShouldBeTrue) 273 old := oldVal.([]*icmpRule) 274 So(old, ShouldNotBeEmpty) 275 err := a.removeFromCache(ip, 32, false, "icmp", []string{}, removeMePolicy) 276 So(err, ShouldBeNil) 277 newVal, ok := a.icmpCache.Get(ip, 32) 278 So(ok, ShouldBeTrue) 279 new := newVal.([]*icmpRule) 280 So(new, ShouldBeEmpty) 281 }) 282 283 }) 284 } 285 286 func TestRemove(t *testing.T) { 287 288 // keep one policy here for a direct pointer comparison 289 policyOne := &policy.FlowPolicy{ 290 Action: policy.Accept, 291 PolicyID: "1", 292 } 293 294 removeRules := policy.IPRuleList{ 295 policy.IPRule{ 296 Addresses: []string{"192.0.2.1"}, 297 Ports: []string{"80"}, 298 Protocols: []string{constants.TCPProtoNum}, 299 Policy: policyOne, 300 }, 301 policy.IPRule{ 302 Addresses: []string{"192.0.2.1"}, 303 Ports: []string{"80"}, 304 Protocols: []string{constants.UDPProtoNum}, 305 Policy: &policy.FlowPolicy{ 306 Action: policy.Accept, 307 PolicyID: "2", 308 }, 309 }, 310 } 311 312 // and one here for a content comparison 313 policyTwo := &policy.FlowPolicy{ 314 Action: policy.Accept, 315 PolicyID: "2", 316 } 317 318 Convey("Given a good DB", t, func() { 319 320 a := newACL() 321 So(a, ShouldNotBeNil) 322 for _, r := range removeRules { 323 err := a.addRule(r) 324 So(err, ShouldBeNil) 325 } 326 327 Convey("When I try to remove a rule with an unsupported protocol, then it should not error", func() { 328 ip := net.ParseIP("192.0.2.1") 329 err := a.removeFromCache(ip, 32, false, "unsupported", nil, nil) 330 So(err, ShouldBeNil) 331 }) 332 333 Convey("When I try to remove a TCP rule which does not exist, then it should not error", func() { 334 ip := net.ParseIP("192.0.2.2") 335 err := a.removeFromCache(ip, 32, false, constants.TCPProtoNum, []string{"42"}, nil) 336 So(err, ShouldBeNil) 337 }) 338 339 Convey("When I try to remove a TCP rule which cannot be parsed correctly, then it should error", func() { 340 ip := net.ParseIP("192.0.2.1") 341 err := a.removeFromCache(ip, 32, false, constants.TCPProtoNum, []string{"invalid port"}, nil) 342 So(err, ShouldNotBeNil) 343 }) 344 345 Convey("When I try to remove a UDP rule which does not exist, then it should not error", func() { 346 ip := net.ParseIP("192.0.2.2") 347 err := a.removeFromCache(ip, 32, false, constants.UDPProtoNum, []string{"43"}, nil) 348 So(err, ShouldBeNil) 349 }) 350 351 Convey("When I try to remove a UDP rule which cannot be parsed correctly, then it should error", func() { 352 ip := net.ParseIP("192.0.2.1") 353 err := a.removeFromCache(ip, 32, false, constants.UDPProtoNum, []string{"another invalid port"}, nil) 354 So(err, ShouldNotBeNil) 355 }) 356 357 Convey("When I try to remove a TCP rule which does not match, then nothing should change", func() { 358 ip := net.ParseIP("192.0.2.1") 359 oldVal, ok := a.tcpCache.Get(ip, 32) 360 So(ok, ShouldBeTrue) 361 old := oldVal.(portActionList) 362 So(old, ShouldNotBeEmpty) 363 err := a.removeFromCache(ip, 32, false, constants.TCPProtoNum, []string{"44"}, policyOne) 364 So(err, ShouldBeNil) 365 newVal, ok := a.tcpCache.Get(ip, 32) 366 So(ok, ShouldBeTrue) 367 new := newVal.(portActionList) 368 So(new, ShouldNotBeEmpty) 369 So(old, ShouldResemble, new) 370 }) 371 372 Convey("When I try to remove a TCP rule which matches, then it should get removed", func() { 373 ip := net.ParseIP("192.0.2.1") 374 oldVal, ok := a.tcpCache.Get(ip, 32) 375 So(ok, ShouldBeTrue) 376 old := oldVal.(portActionList) 377 So(old, ShouldNotBeEmpty) 378 oldLength := len(old) 379 err := a.removeFromCache(ip, 32, false, constants.TCPProtoNum, []string{"80"}, policyOne) 380 So(err, ShouldBeNil) 381 newVal, ok := a.tcpCache.Get(ip, 32) 382 So(ok, ShouldBeTrue) 383 new := newVal.(portActionList) 384 So(new, ShouldHaveLength, oldLength-1) 385 }) 386 387 Convey("When I try to remove a UDP rule which does not match, then nothing should change", func() { 388 ip := net.ParseIP("192.0.2.1") 389 oldVal, ok := a.udpCache.Get(ip, 32) 390 So(ok, ShouldBeTrue) 391 old := oldVal.(portActionList) 392 So(old, ShouldNotBeEmpty) 393 err := a.removeFromCache(ip, 32, false, constants.UDPProtoNum, []string{"45"}, policyTwo) 394 So(err, ShouldBeNil) 395 newVal, ok := a.udpCache.Get(ip, 32) 396 So(ok, ShouldBeTrue) 397 new := newVal.(portActionList) 398 So(new, ShouldNotBeEmpty) 399 So(old, ShouldResemble, new) 400 }) 401 402 Convey("When I try to remove a UDP rule which matches, then it should get removed", func() { 403 ip := net.ParseIP("192.0.2.1") 404 oldVal, ok := a.udpCache.Get(ip, 32) 405 So(ok, ShouldBeTrue) 406 old := oldVal.(portActionList) 407 So(old, ShouldNotBeEmpty) 408 oldLength := len(old) 409 err := a.removeFromCache(ip, 32, false, constants.UDPProtoNum, []string{"80"}, policyTwo) 410 So(err, ShouldBeNil) 411 newVal, ok := a.udpCache.Get(ip, 32) 412 So(ok, ShouldBeTrue) 413 new := newVal.(portActionList) 414 So(new, ShouldHaveLength, oldLength-1) 415 }) 416 }) 417 } 418 419 func TestObservedLookup(t *testing.T) { 420 421 ip1 := "200.17.0.0/17" 422 ip2 := "200.18.0.0/17" 423 ip3 := "200.0.0.0/9" 424 var ( 425 rulesWithObservation = policy.IPRuleList{ 426 policy.IPRule{ 427 Addresses: []string{ip1}, 428 Ports: []string{"401"}, 429 Protocols: []string{constants.TCPProtoNum}, 430 Policy: &policy.FlowPolicy{ 431 Action: policy.Accept, 432 ObserveAction: policy.ObserveContinue, 433 PolicyID: "observed-continue-tcp200.17/17"}, 434 }, 435 policy.IPRule{ 436 Addresses: []string{ip2}, 437 Ports: []string{"401"}, 438 Protocols: []string{constants.TCPProtoNum}, 439 Policy: &policy.FlowPolicy{ 440 Action: policy.Accept, 441 ObserveAction: policy.ObserveApply, 442 PolicyID: "observed-applied-tcp200.18/17"}, 443 }, 444 policy.IPRule{ 445 Addresses: []string{ip3}, 446 Ports: []string{"401"}, 447 Protocols: []string{constants.TCPProtoNum}, 448 Policy: &policy.FlowPolicy{ 449 Action: policy.Accept, 450 PolicyID: "tcp200/9"}, 451 }, 452 } 453 ) 454 455 Convey("Given a good DB", t, func() { 456 a := newACL() 457 So(a, ShouldNotBeNil) 458 for _, r := range rulesWithObservation { 459 err := a.addRule(r) 460 So(err, ShouldBeNil) 461 } 462 463 // Ensure all the elements are there in the cache 464 cidrs := []string{ip1, ip2, ip3} 465 466 for _, cidr := range cidrs { 467 ip, ipnet, _ := net.ParseCIDR(cidr) 468 size, _ := ipnet.Mask.Size() 469 _, ok := a.tcpCache.Get(ip, size) 470 So(ok, ShouldEqual, true) 471 } 472 473 Convey("When I lookup for a matching address and a port range, I should get the right action and observed action", func() { 474 ip := net.ParseIP("200.17.0.1") 475 port := uint16(401) 476 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 477 So(err, ShouldBeNil) 478 So(p.Action, ShouldEqual, policy.Accept) 479 So(p.PolicyID, ShouldEqual, "tcp200/9") 480 So(r.Action, ShouldEqual, policy.Accept) 481 So(r.PolicyID, ShouldEqual, "observed-continue-tcp200.17/17") 482 }) 483 484 Convey("When I lookup for a matching address and a port range, I should get the observed action as applied", func() { 485 ip := net.ParseIP("200.18.0.1") 486 port := uint16(401) 487 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 488 So(err, ShouldBeNil) 489 So(p.Action, ShouldEqual, policy.Accept) 490 So(p.PolicyID, ShouldEqual, "observed-applied-tcp200.18/17") 491 So(r.Action, ShouldEqual, policy.Accept) 492 So(r.PolicyID, ShouldEqual, "observed-applied-tcp200.18/17") 493 }) 494 495 Convey("When I lookup for a matching address and a port range with an already reported action of reject, I should get the observed action as applied", func() { 496 ip := net.ParseIP("200.18.0.1") 497 port := uint16(401) 498 preReported := &policy.FlowPolicy{ 499 Action: policy.Reject, 500 PolicyID: "preReportedPolicyID", 501 } 502 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, preReported) 503 So(err, ShouldBeNil) 504 So(p.Action, ShouldEqual, policy.Accept) 505 So(p.PolicyID, ShouldEqual, "observed-applied-tcp200.18/17") 506 So(r.Action, ShouldEqual, policy.Reject) 507 So(r.PolicyID, ShouldEqual, "preReportedPolicyID") 508 }) 509 }) 510 } 511 512 func TestNomatchLookup(t *testing.T) { 513 514 ip1 := "200.17.0.0/16" 515 ip2 := "200.18.0.0/16" 516 ip3 := "200.0.0.0/8" 517 var ( 518 rulesWithNomatch = policy.IPRuleList{ 519 policy.IPRule{ 520 Addresses: []string{"!" + ip1}, 521 Ports: []string{"401"}, 522 Protocols: []string{constants.TCPProtoNum}, 523 Policy: &policy.FlowPolicy{ 524 Action: policy.Accept, 525 PolicyID: "nomatch-tcp200.17/16"}, 526 }, 527 policy.IPRule{ 528 Addresses: []string{"!" + ip2}, 529 Ports: []string{"401"}, 530 Protocols: []string{constants.TCPProtoNum}, 531 Policy: &policy.FlowPolicy{ 532 Action: policy.Accept, 533 PolicyID: "nomatch-tcp200.18/16"}, 534 }, 535 policy.IPRule{ 536 Addresses: []string{ip3}, 537 Ports: []string{"401"}, 538 Protocols: []string{constants.TCPProtoNum}, 539 Policy: &policy.FlowPolicy{ 540 Action: policy.Accept, 541 PolicyID: "tcp200/8"}, 542 }, 543 } 544 ) 545 546 Convey("Given a good DB", t, func() { 547 a := newACL() 548 So(a, ShouldNotBeNil) 549 for _, r := range rulesWithNomatch { 550 err := a.addRule(r) 551 So(err, ShouldBeNil) 552 } 553 554 // Ensure all the elements are there in the cache 555 cidrs := []string{ip1, ip2, ip3} 556 557 for _, cidr := range cidrs { 558 ip, ipnet, _ := net.ParseCIDR(cidr) 559 size, _ := ipnet.Mask.Size() 560 _, ok := a.tcpCache.Get(ip, size) 561 So(ok, ShouldEqual, true) 562 } 563 564 Convey("When I lookup for a nomatch address and a port range, I should get nomatch", func() { 565 ip := net.ParseIP("200.17.0.1") 566 port := uint16(401) 567 _, _, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 568 So(err, ShouldNotBeNil) 569 }) 570 571 Convey("When I lookup for another nomatch address and a port range, I should get nomatch", func() { 572 ip := net.ParseIP("200.18.0.1") 573 port := uint16(401) 574 _, _, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 575 So(err, ShouldNotBeNil) 576 }) 577 578 Convey("When I lookup for a matching address and a port range, I should get the accept action", func() { 579 ip := net.ParseIP("200.19.0.1") 580 port := uint16(401) 581 r, p, err := a.getMatchingAction(ip.To4(), port, packet.IPProtocolTCP, nil) 582 So(err, ShouldBeNil) 583 So(p.Action, ShouldEqual, policy.Accept) 584 So(p.PolicyID, ShouldEqual, "tcp200/8") 585 So(r.Action, ShouldEqual, policy.Accept) 586 So(r.PolicyID, ShouldEqual, "tcp200/8") 587 }) 588 }) 589 }