github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/apiauth/apiauth_test.go (about) 1 // +build !windows 2 3 package apiauth 4 5 import ( 6 "context" 7 "crypto/tls" 8 "fmt" 9 "net" 10 "net/http" 11 "net/url" 12 "testing" 13 "time" 14 15 "github.com/golang/mock/gomock" 16 . "github.com/smartystreets/goconvey/convey" 17 "go.aporeto.io/enforcerd/trireme-lib/collector" 18 triremecommon "go.aporeto.io/enforcerd/trireme-lib/common" 19 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/serviceregistry" 20 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext" 21 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets" 22 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets/testhelper" 23 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/servicetokens" 24 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/usertokens/mockusertokens" 25 "go.aporeto.io/enforcerd/trireme-lib/policy" 26 "go.aporeto.io/enforcerd/trireme-lib/utils/portspec" 27 ) 28 29 const ( 30 policyID = "somepolicy" 31 rejectPolicyID = "somerejectepolicy" 32 serviceID = "someservice" 33 rejectServiceID = "somerejectservice" 34 namespace = "somenamespace" 35 appLabel = "app=web" 36 ) 37 38 func newBaseApplicationServices(ctrl *gomock.Controller, id string, ipAddr string, exposedPortValue, publicPortValue, privatePortValue uint16, external bool) *policy.ApplicationService { 39 40 exposedPort, err := portspec.NewPortSpec(exposedPortValue, exposedPortValue, nil) 41 So(err, ShouldBeNil) 42 publicPort, err := portspec.NewPortSpec(publicPortValue, publicPortValue, nil) 43 So(err, ShouldBeNil) 44 privatePort, err := portspec.NewPortSpec(privatePortValue, privatePortValue, nil) 45 So(err, ShouldBeNil) 46 47 return &policy.ApplicationService{ 48 ID: id, 49 NetworkInfo: &triremecommon.Service{ 50 Ports: exposedPort, 51 Protocol: 6, 52 Addresses: map[string]struct{}{ipAddr: struct{}{}}, 53 }, 54 PublicNetworkInfo: &triremecommon.Service{ 55 Ports: publicPort, 56 Protocol: 6, 57 Addresses: map[string]struct{}{ipAddr: struct{}{}}, 58 }, 59 PrivateNetworkInfo: &triremecommon.Service{ 60 Ports: privatePort, 61 Protocol: 6, 62 Addresses: map[string]struct{}{}, 63 }, 64 Type: policy.ServiceHTTP, 65 PublicServiceTLSType: policy.ServiceTLSTypeAporeto, 66 External: external, 67 HTTPRules: []*policy.HTTPRule{ 68 { 69 URIs: []string{"/admin"}, 70 Methods: []string{"GET"}, 71 ClaimMatchingRules: [][]string{ 72 {appLabel}, 73 }, 74 Public: false, 75 }, 76 { 77 URIs: []string{"/public"}, 78 Methods: []string{"GET"}, 79 Public: true, 80 }, 81 { 82 URIs: []string{"/forbidden"}, 83 Methods: []string{"GET"}, 84 ClaimMatchingRules: [][]string{ 85 {"Nobody"}, 86 }, 87 Public: false, 88 }, 89 }, 90 UserAuthorizationType: policy.UserAuthorizationOIDC, 91 UserAuthorizationHandler: mockusertokens.NewMockVerifier(ctrl), 92 } 93 } 94 95 func newAPIAuthProcessor(ctrl *gomock.Controller) (*serviceregistry.Registry, *pucontext.PUContext, secrets.Secrets) { 96 97 contextID := "test" 98 baseService := newBaseApplicationServices(ctrl, "base", "10.1.1.0/24", uint16(80), uint16(443), uint16(80), false) 99 externalService := newBaseApplicationServices(ctrl, "external", "45.0.0.0/8", uint16(80), uint16(443), uint16(80), true) 100 externalBadService := newBaseApplicationServices(ctrl, "external", "100.0.0.0/8", uint16(80), uint16(443), uint16(80), true) 101 102 exposedServices := policy.ApplicationServicesList{baseService} 103 dependentServices := policy.ApplicationServicesList{baseService, externalService, externalBadService} 104 105 networkACLs := policy.IPRuleList{ 106 { 107 Addresses: []string{"10.1.1.0/24"}, 108 Ports: []string{"80"}, 109 Protocols: []string{"6"}, 110 Policy: &policy.FlowPolicy{ 111 Action: policy.Accept, 112 PolicyID: policyID, 113 ServiceID: serviceID, 114 Labels: []string{"service=external"}, 115 }, 116 }, 117 { 118 Addresses: []string{"45.0.0.0/8"}, 119 Ports: []string{"80"}, 120 Protocols: []string{"6"}, 121 Policy: &policy.FlowPolicy{ 122 Action: policy.Accept, 123 PolicyID: policyID, 124 ServiceID: serviceID, 125 Labels: []string{"service=external"}, 126 }, 127 }, 128 { 129 Addresses: []string{"100.0.0.0/8"}, 130 Ports: []string{"80"}, 131 Protocols: []string{"6"}, 132 Policy: &policy.FlowPolicy{ 133 Action: policy.Reject, 134 PolicyID: rejectPolicyID, 135 ServiceID: rejectServiceID, 136 ObserveAction: policy.ObserveApply, 137 Labels: []string{"service=external"}, 138 }, 139 }, 140 } 141 142 applicationACLs := policy.IPRuleList{ 143 { 144 Addresses: []string{"100.0.0.0/8"}, 145 Ports: []string{"80"}, 146 Protocols: []string{"6"}, 147 Policy: &policy.FlowPolicy{ 148 Action: policy.Reject, 149 PolicyID: rejectPolicyID, 150 ServiceID: rejectServiceID, 151 Labels: []string{"service=external"}, 152 }, 153 }, 154 } 155 156 plc := policy.NewPUPolicy( 157 contextID, 158 namespace, 159 policy.Police, 160 applicationACLs, 161 networkACLs, 162 policy.DNSRuleList{}, 163 policy.TagSelectorList{}, 164 policy.TagSelectorList{ 165 policy.TagSelector{ 166 Clause: []policy.KeyValueOperator{ 167 { 168 Key: "app", 169 Value: []string{"web"}, 170 Operator: policy.Equal, 171 ID: "somepolicy", 172 }, 173 }, 174 Policy: &policy.FlowPolicy{ 175 Action: policy.Accept, 176 ServiceID: "pu" + serviceID, 177 PolicyID: "pu" + policyID, 178 ObserveAction: policy.ObserveApply, 179 }, 180 }, 181 policy.TagSelector{ 182 Clause: []policy.KeyValueOperator{ 183 { 184 Key: "app", 185 Value: []string{"bad"}, 186 Operator: policy.Equal, 187 ID: "rejectpolicy", 188 }, 189 }, 190 Policy: &policy.FlowPolicy{ 191 Action: policy.Reject, 192 PolicyID: "reject" + policyID, 193 }, 194 }, 195 }, 196 policy.NewTagStore(), 197 policy.NewTagStoreFromSlice([]string{appLabel, "type=aporeto"}), 198 nil, 199 nil, 200 0, 201 0, 202 exposedServices, 203 dependentServices, 204 []string{appLabel}, 205 policy.EnforcerMapping, 206 policy.Reject|policy.Log, 207 policy.Reject|policy.Log, 208 ) 209 210 puInfo := policy.NewPUInfo(contextID, namespace, triremecommon.ContainerPU) 211 puInfo.Policy = plc 212 pctx, err := pucontext.NewPU(contextID, puInfo, nil, time.Second*1000) 213 So(err, ShouldBeNil) 214 _, s, _ := testhelper.NewTestCompactPKISecrets() 215 216 r := serviceregistry.Instance() 217 _, err = r.Register(contextID, puInfo, pctx, s) 218 So(err, ShouldBeNil) 219 220 return r, pctx, s 221 } 222 223 func Test_New(t *testing.T) { 224 Convey("When I create a new processor it should be correctly propulated", t, func() { 225 ctrl := gomock.NewController(t) 226 _, _, s := newAPIAuthProcessor(ctrl) 227 p := New("test", s) 228 229 So(p.puContext, ShouldEqual, "test") 230 So(p.secrets, ShouldEqual, s) 231 }) 232 } 233 234 func Test_ApplicationRequest(t *testing.T) { 235 Convey("Given a valid authorization processor", t, func() { 236 ctrl := gomock.NewController(t) 237 _, pctx, s := newAPIAuthProcessor(ctrl) 238 p := New("test", s) 239 240 Convey("Given a request without context, it should error", func() { 241 242 u, _ := url.Parse("http://www.foo.com/public") // nolint 243 r := &Request{ 244 SourceAddress: &net.TCPAddr{ 245 IP: net.ParseIP("10.1.1.1"), 246 Port: 1000, 247 }, 248 OriginalDestination: &net.TCPAddr{ 249 IP: net.ParseIP("20.1.1.1"), 250 Port: 8080, 251 }, 252 Method: "GET", 253 URL: u, 254 RequestURI: "/public", 255 Header: http.Header{}, 256 Cookie: nil, 257 TLS: nil, 258 } 259 _, err := p.ApplicationRequest(r) 260 So(err, ShouldNotBeNil) 261 262 authErr, ok := err.(*AuthError) 263 So(ok, ShouldBeTrue) 264 So(authErr.Status(), ShouldEqual, http.StatusBadGateway) 265 }) 266 267 Convey("Given a request with valid context that is not external, I should get a token", func() { 268 u, _ := url.Parse("http://www.foo.com/public") // nolint 269 r := &Request{ 270 SourceAddress: &net.TCPAddr{ 271 IP: net.ParseIP("10.1.1.1"), 272 Port: 1000, 273 }, 274 OriginalDestination: &net.TCPAddr{ 275 IP: net.ParseIP("10.1.1.2"), 276 Port: 80, 277 }, 278 Method: "GET", 279 URL: u, 280 RequestURI: "/public", 281 Header: http.Header{}, 282 Cookie: nil, 283 TLS: nil, 284 } 285 response, err := p.ApplicationRequest(r) 286 So(err, ShouldBeNil) 287 So(response, ShouldNotBeNil) 288 So(len(response.Token), ShouldBeGreaterThan, 0) 289 So(response.PUContext, ShouldEqual, pctx) 290 So(response.TLSListener, ShouldBeTrue) 291 }) 292 293 Convey("Given a request for a public external service, I should accept it", func() { 294 u, _ := url.Parse("http://www.foo.com/public") // nolint 295 r := &Request{ 296 SourceAddress: &net.TCPAddr{ 297 IP: net.ParseIP("10.1.1.1"), 298 Port: 1000, 299 }, 300 OriginalDestination: &net.TCPAddr{ 301 IP: net.ParseIP("45.1.1.1"), 302 Port: 80, 303 }, 304 Method: "GET", 305 URL: u, 306 RequestURI: "/public", 307 Header: http.Header{}, 308 Cookie: nil, 309 TLS: nil, 310 } 311 response, err := p.ApplicationRequest(r) 312 So(err, ShouldBeNil) 313 So(response, ShouldNotBeNil) 314 So(len(response.Token), ShouldEqual, 0) 315 So(response.PUContext, ShouldEqual, pctx) 316 So(response.TLSListener, ShouldBeTrue) 317 }) 318 319 Convey("Given a request for a controlled external service with valid policy, I should accept it", func() { 320 u, _ := url.Parse("http://www.foo.com/admin") // nolint 321 r := &Request{ 322 SourceAddress: &net.TCPAddr{ 323 IP: net.ParseIP("10.1.1.1"), 324 Port: 1000, 325 }, 326 OriginalDestination: &net.TCPAddr{ 327 IP: net.ParseIP("45.1.1.1"), 328 Port: 80, 329 }, 330 Method: "GET", 331 URL: u, 332 RequestURI: "/admin", 333 Header: http.Header{}, 334 Cookie: nil, 335 TLS: nil, 336 } 337 response, err := p.ApplicationRequest(r) 338 So(err, ShouldBeNil) 339 So(response, ShouldNotBeNil) 340 So(len(response.Token), ShouldEqual, 0) 341 So(response.PUContext, ShouldEqual, pctx) 342 So(response.TLSListener, ShouldBeTrue) 343 }) 344 345 Convey("Given a request for a controlled external service with forbidden policy, I should reject it", func() { 346 u, _ := url.Parse("http://www.foo.com/forbidden") // nolint 347 r := &Request{ 348 SourceAddress: &net.TCPAddr{ 349 IP: net.ParseIP("10.1.1.1"), 350 Port: 1000, 351 }, 352 OriginalDestination: &net.TCPAddr{ 353 IP: net.ParseIP("45.1.1.1"), 354 Port: 80, 355 }, 356 Method: "GET", 357 URL: u, 358 RequestURI: "/forbidden", 359 Header: http.Header{}, 360 Cookie: nil, 361 TLS: nil, 362 } 363 _, err := p.ApplicationRequest(r) 364 So(err, ShouldNotBeNil) 365 authErr, ok := err.(*AuthError) 366 So(ok, ShouldBeTrue) 367 So(authErr.Status(), ShouldEqual, http.StatusForbidden) 368 }) 369 370 Convey("Given a request for a controlled external service with an uknown URI, I should reject it", func() { 371 u, _ := url.Parse("http://www.foo.com/random") // nolint 372 r := &Request{ 373 SourceAddress: &net.TCPAddr{ 374 IP: net.ParseIP("10.1.1.1"), 375 Port: 1000, 376 }, 377 OriginalDestination: &net.TCPAddr{ 378 IP: net.ParseIP("45.1.1.1"), 379 Port: 80, 380 }, 381 Method: "GET", 382 URL: u, 383 RequestURI: "/random", 384 Header: http.Header{}, 385 Cookie: nil, 386 TLS: nil, 387 } 388 _, err := p.ApplicationRequest(r) 389 So(err, ShouldNotBeNil) 390 authErr, ok := err.(*AuthError) 391 So(ok, ShouldBeTrue) 392 So(authErr.Status(), ShouldEqual, http.StatusForbidden) 393 }) 394 395 Convey("Given a request for a an external service dropped by network rules it should be rejected", func() { 396 u, _ := url.Parse("http://www.foo.com/random") // nolint 397 r := &Request{ 398 SourceAddress: &net.TCPAddr{ 399 IP: net.ParseIP("10.1.1.1"), 400 Port: 1000, 401 }, 402 OriginalDestination: &net.TCPAddr{ 403 IP: net.ParseIP("100.1.1.1"), 404 Port: 80, 405 }, 406 Method: "GET", 407 URL: u, 408 RequestURI: "/public", 409 Header: http.Header{}, 410 Cookie: nil, 411 TLS: nil, 412 } 413 _, err := p.ApplicationRequest(r) 414 So(err, ShouldNotBeNil) 415 authErr, ok := err.(*AuthError) 416 So(ok, ShouldBeTrue) 417 So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired) 418 }) 419 }) 420 } 421 422 func Test_NetworkRequest(t *testing.T) { 423 Convey("Given a valid authorization processor", t, func() { 424 ctx, cancel := context.WithCancel(context.Background()) 425 defer cancel() 426 427 ctrl := gomock.NewController(t) 428 _, pctx, s := newAPIAuthProcessor(ctrl) 429 p := New("test", s) 430 431 Convey("Requests for bad context should return errors", func() { 432 u, _ := url.Parse("http://www.foo.com/public") // nolint 433 r := &Request{ 434 SourceAddress: &net.TCPAddr{ 435 IP: net.ParseIP("10.1.1.1"), 436 Port: 1000, 437 }, 438 OriginalDestination: &net.TCPAddr{ 439 IP: net.ParseIP("20.1.1.1"), 440 Port: 8080, 441 }, 442 Method: "GET", 443 URL: u, 444 RequestURI: "/public", 445 Header: http.Header{}, 446 Cookie: nil, 447 TLS: nil, 448 } 449 _, err := p.NetworkRequest(ctx, r) 450 So(err, ShouldNotBeNil) 451 452 authErr, ok := err.(*AuthError) 453 So(ok, ShouldBeTrue) 454 So(authErr.Status(), ShouldEqual, http.StatusInternalServerError) 455 }) 456 457 Convey("Requests a valid context with a drop network policy must be rejected", func() { 458 u, _ := url.Parse("http://www.foo.com/public") // nolint 459 r := &Request{ 460 SourceAddress: &net.TCPAddr{ 461 IP: net.ParseIP("100.1.1.1"), 462 Port: 1000, 463 }, 464 OriginalDestination: &net.TCPAddr{ 465 IP: net.ParseIP("10.1.1.1"), 466 Port: 80, 467 }, 468 Method: "GET", 469 URL: u, 470 RequestURI: "/public", 471 Header: http.Header{}, 472 Cookie: nil, 473 TLS: nil, 474 } 475 response, err := p.NetworkRequest(ctx, r) 476 So(err, ShouldNotBeNil) 477 So(response, ShouldNotBeNil) 478 479 authErr, ok := err.(*AuthError) 480 So(ok, ShouldBeTrue) 481 So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired) 482 So(response.NetworkPolicyID, ShouldEqual, rejectPolicyID) 483 So(response.NetworkServiceID, ShouldEqual, rejectServiceID) 484 So(response.DropReason, ShouldEqual, collector.PolicyDrop) 485 So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP) 486 }) 487 488 Convey("Requests a valid context with an invalid token, I should get forbidden", func() { 489 u, _ := url.Parse("http://www.foo.com/public") // nolint 490 h := http.Header{} 491 h.Add("X-APORETO-AUTH", "badvalue") 492 h.Add("X-APORETO-KEY", "badvalue") 493 494 r := &Request{ 495 SourceAddress: &net.TCPAddr{ 496 IP: net.ParseIP("45.1.1.1"), 497 Port: 1000, 498 }, 499 OriginalDestination: &net.TCPAddr{ 500 IP: net.ParseIP("10.1.1.1"), 501 Port: 80, 502 }, 503 Method: "GET", 504 URL: u, 505 RequestURI: "/public", 506 Header: h, 507 Cookie: nil, 508 TLS: nil, 509 } 510 response, err := p.NetworkRequest(ctx, r) 511 So(err, ShouldNotBeNil) 512 So(response, ShouldNotBeNil) 513 514 authErr, ok := err.(*AuthError) 515 So(ok, ShouldBeTrue) 516 So(authErr.Status(), ShouldEqual, http.StatusForbidden) 517 So(authErr.Message(), ShouldContainSubstring, "Invalid Authorization Token:") 518 So(response.NetworkPolicyID, ShouldEqual, policyID) 519 So(response.NetworkServiceID, ShouldEqual, serviceID) 520 So(response.DropReason, ShouldEqual, collector.PolicyDrop) 521 So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP) 522 }) 523 524 Convey("Requests a valid context with a valid Aporeto token to a public URL from a valid network it should succeed", func() { 525 u, _ := url.Parse("http://www.foo.com/public") // nolint 526 token, err := servicetokens.CreateAndSign( 527 "somenode", 528 pctx.Identity().GetSlice(), 529 pctx.Scopes(), 530 pctx.ManagementID(), 531 DefaultValidity, 532 s.EncodingKey(), 533 nil, 534 ) 535 So(err, ShouldBeNil) 536 537 h := http.Header{} 538 h.Add("X-APORETO-AUTH", token) 539 h.Add("X-APORETO-KEY", string(s.TransmittedKey())) 540 541 r := &Request{ 542 SourceAddress: &net.TCPAddr{ 543 IP: net.ParseIP("45.1.1.1"), 544 Port: 1000, 545 }, 546 OriginalDestination: &net.TCPAddr{ 547 IP: net.ParseIP("10.1.1.1"), 548 Port: 80, 549 }, 550 Method: "GET", 551 URL: u, 552 RequestURI: "/public", 553 Header: h, 554 Cookie: nil, 555 TLS: nil, 556 } 557 response, err := p.NetworkRequest(ctx, r) 558 So(err, ShouldBeNil) 559 So(response.NetworkPolicyID, ShouldEqual, policyID) 560 So(response.NetworkServiceID, ShouldEqual, serviceID) 561 So(response.SourceType, ShouldEqual, collector.EndPointTypePU) 562 }) 563 564 Convey("Requests a valid context with a valid Aporeto token based on PU network policy it should succeed", func() { 565 u, _ := url.Parse("http://www.foo.com/public") // nolint 566 token, err := servicetokens.CreateAndSign( 567 "somenode", 568 pctx.Identity().GetSlice(), 569 pctx.Scopes(), 570 pctx.ManagementID(), 571 DefaultValidity, 572 s.EncodingKey(), 573 nil, 574 ) 575 So(err, ShouldBeNil) 576 577 h := http.Header{} 578 h.Add("X-APORETO-AUTH", token) 579 h.Add("X-APORETO-KEY", string(s.TransmittedKey())) 580 581 r := &Request{ 582 SourceAddress: &net.TCPAddr{ 583 IP: net.ParseIP("60.1.1.1"), 584 Port: 1000, 585 }, 586 OriginalDestination: &net.TCPAddr{ 587 IP: net.ParseIP("10.1.1.1"), 588 Port: 80, 589 }, 590 Method: "GET", 591 URL: u, 592 RequestURI: "/public", 593 Header: h, 594 Cookie: nil, 595 TLS: nil, 596 } 597 response, err := p.NetworkRequest(ctx, r) 598 So(err, ShouldBeNil) 599 So(response.ObservedPolicyID, ShouldEqual, "pu"+policyID) 600 So(response.ObservedAction, ShouldEqual, policy.Accept) 601 So(response.NetworkPolicyID, ShouldEqual, "pu"+policyID) 602 So(response.SourceType, ShouldEqual, collector.EndPointTypePU) 603 }) 604 605 Convey("Requests a valid context with no Aporeto claims and no network policy, it should be dropped", func() { 606 u, _ := url.Parse("http://www.foo.com/public") // nolint 607 608 r := &Request{ 609 SourceAddress: &net.TCPAddr{ 610 IP: net.ParseIP("60.1.1.1"), 611 Port: 1000, 612 }, 613 OriginalDestination: &net.TCPAddr{ 614 IP: net.ParseIP("10.1.1.1"), 615 Port: 80, 616 }, 617 Method: "GET", 618 URL: u, 619 RequestURI: "/public", 620 Header: http.Header{}, 621 Cookie: nil, 622 TLS: nil, 623 } 624 response, err := p.NetworkRequest(ctx, r) 625 So(err, ShouldNotBeNil) 626 authErr, ok := err.(*AuthError) 627 So(ok, ShouldBeTrue) 628 So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired) 629 So(response.NetworkPolicyID, ShouldEqual, collector.DefaultEndPoint) 630 So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP) 631 }) 632 633 Convey("Requests a valid context with a valid Aporeto token but network reject, it should be rejected", func() { 634 u, _ := url.Parse("http://www.foo.com/public") // nolint 635 badTags := append(pctx.Identity().GetSlice(), "app=bad") 636 token, err := servicetokens.CreateAndSign( 637 "badnode", 638 badTags, 639 pctx.Scopes(), 640 "badnodeID", 641 DefaultValidity, 642 s.EncodingKey(), 643 nil, 644 ) 645 So(err, ShouldBeNil) 646 647 h := http.Header{} 648 h.Add("X-APORETO-AUTH", token) 649 h.Add("X-APORETO-KEY", string(s.TransmittedKey())) 650 651 r := &Request{ 652 SourceAddress: &net.TCPAddr{ 653 IP: net.ParseIP("60.1.1.1"), 654 Port: 1000, 655 }, 656 OriginalDestination: &net.TCPAddr{ 657 IP: net.ParseIP("10.1.1.1"), 658 Port: 80, 659 }, 660 Method: "GET", 661 URL: u, 662 RequestURI: "/public", 663 Header: h, 664 Cookie: nil, 665 TLS: nil, 666 } 667 response, err := p.NetworkRequest(ctx, r) 668 So(err, ShouldNotBeNil) 669 So(response.NetworkPolicyID, ShouldEqual, "reject"+policyID) 670 So(response.SourceType, ShouldEqual, collector.EndPointTypePU) 671 }) 672 673 Convey("Requests a valid context with a valid Aporeto token to a private URL it should succeed", func() { 674 u, _ := url.Parse("http://www.foo.com/admin") // nolint 675 token, err := servicetokens.CreateAndSign( 676 "somenode", 677 pctx.Identity().GetSlice(), 678 pctx.Scopes(), 679 pctx.ManagementID(), 680 DefaultValidity, 681 s.EncodingKey(), 682 nil, 683 ) 684 So(err, ShouldBeNil) 685 686 h := http.Header{} 687 h.Add("X-APORETO-AUTH", token) 688 h.Add("X-APORETO-KEY", string(s.TransmittedKey())) 689 690 r := &Request{ 691 SourceAddress: &net.TCPAddr{ 692 IP: net.ParseIP("45.1.1.1"), 693 Port: 1000, 694 }, 695 OriginalDestination: &net.TCPAddr{ 696 IP: net.ParseIP("10.1.1.1"), 697 Port: 80, 698 }, 699 Method: "GET", 700 URL: u, 701 RequestURI: "/admin", 702 Header: h, 703 Cookie: nil, 704 TLS: nil, 705 } 706 response, err := p.NetworkRequest(ctx, r) 707 So(err, ShouldBeNil) 708 So(response.NetworkPolicyID, ShouldEqual, policyID) 709 So(response.NetworkServiceID, ShouldEqual, serviceID) 710 So(response.SourceType, ShouldEqual, collector.EndPointTypePU) 711 }) 712 713 Convey("Requests a valid context with a valid Aporeto token to a forbidden URL it should return error", func() { 714 u, _ := url.Parse("http://www.foo.com/forbidden") // nolint 715 token, err := servicetokens.CreateAndSign( 716 "somenode", 717 pctx.Identity().GetSlice(), 718 pctx.Scopes(), 719 "forbiddennode", 720 DefaultValidity, 721 s.EncodingKey(), 722 nil, 723 ) 724 So(err, ShouldBeNil) 725 726 h := http.Header{} 727 h.Add("X-APORETO-AUTH", token) 728 h.Add("X-APORETO-KEY", string(s.TransmittedKey())) 729 730 r := &Request{ 731 SourceAddress: &net.TCPAddr{ 732 IP: net.ParseIP("45.1.1.1"), 733 Port: 1000, 734 }, 735 OriginalDestination: &net.TCPAddr{ 736 IP: net.ParseIP("10.1.1.1"), 737 Port: 80, 738 }, 739 Method: "GET", 740 URL: u, 741 RequestURI: "/forbidden", 742 Header: h, 743 Cookie: nil, 744 TLS: nil, 745 } 746 response, err := p.NetworkRequest(ctx, r) 747 So(err, ShouldNotBeNil) 748 authError, ok := err.(*AuthError) 749 So(ok, ShouldBeTrue) 750 So(authError.Status(), ShouldEqual, http.StatusUnauthorized) 751 So(response.NetworkPolicyID, ShouldEqual, policyID) 752 So(response.NetworkServiceID, ShouldEqual, serviceID) 753 So(response.SourceType, ShouldEqual, collector.EndPointTypePU) 754 }) 755 }) 756 } 757 758 func Test_UserCredentials(t *testing.T) { 759 760 Convey("Given a valid authorizer", t, func() { 761 ctx, cancel := context.WithCancel(context.Background()) 762 defer cancel() 763 764 ctrl := gomock.NewController(t) 765 serviceRegistry, _, s := newAPIAuthProcessor(ctrl) 766 p := New("test", s) 767 So(p, ShouldNotBeNil) 768 769 portContext, err := serviceRegistry.RetrieveExposedServiceContext(net.ParseIP("10.1.1.1"), 80, "") 770 So(err, ShouldBeNil) 771 So(portContext, ShouldNotBeNil) 772 773 verifier, ok := portContext.Service.UserAuthorizationHandler.(*mockusertokens.MockVerifier) 774 So(ok, ShouldBeTrue) 775 776 Convey("When the request is not TLS, there is no user data", func() { 777 u, _ := url.Parse("http://www.foo.com/admin") 778 d := &NetworkAuthResponse{} 779 r := &Request{ 780 SourceAddress: &net.TCPAddr{ 781 IP: net.ParseIP("45.1.1.1"), 782 Port: 1000, 783 }, 784 OriginalDestination: &net.TCPAddr{ 785 IP: net.ParseIP("10.1.1.1"), 786 Port: 80, 787 }, 788 Method: "GET", 789 URL: u, 790 RequestURI: "/admin", 791 Header: http.Header{}, 792 Cookie: nil, 793 TLS: nil, 794 } 795 userCredentials(ctx, portContext, r, d) 796 So(len(d.UserAttributes), ShouldEqual, 0) 797 }) 798 799 Convey("When the request is TLS and a user is identified, the claims are correct", func() { 800 u, _ := url.Parse("http://www.foo.com/admin") 801 d := &NetworkAuthResponse{} 802 803 r := &Request{ 804 SourceAddress: &net.TCPAddr{ 805 IP: net.ParseIP("45.1.1.1"), 806 Port: 1000, 807 }, 808 OriginalDestination: &net.TCPAddr{ 809 IP: net.ParseIP("10.1.1.1"), 810 Port: 80, 811 }, 812 Method: "GET", 813 URL: u, 814 RequestURI: "/admin", 815 Header: http.Header{}, 816 Cookie: nil, 817 TLS: &tls.ConnectionState{}, 818 } 819 verifier.EXPECT().Validate(ctx, gomock.Any()).Return([]string{"user=flash"}, false, "", nil) 820 userCredentials(ctx, portContext, r, d) 821 So(len(d.UserAttributes), ShouldEqual, 1) 822 So(d.UserAttributes[0], ShouldEqual, "user=flash") 823 So(d.SourceType, ShouldEqual, collector.EndPointTypeClaims) 824 So(d.Redirect, ShouldBeFalse) 825 }) 826 827 Convey("When the request is TLS and user authorization fails with a redirect, the redirect should be set", func() { 828 u, _ := url.Parse("http://www.foo.com/admin") 829 d := &NetworkAuthResponse{} 830 831 h := http.Header{} 832 h.Add("Authorization", "Bearer MockJWTToken") 833 r := &Request{ 834 SourceAddress: &net.TCPAddr{ 835 IP: net.ParseIP("45.1.1.1"), 836 Port: 1000, 837 }, 838 OriginalDestination: &net.TCPAddr{ 839 IP: net.ParseIP("10.1.1.1"), 840 Port: 80, 841 }, 842 Method: "GET", 843 URL: u, 844 RequestURI: "/admin", 845 Header: http.Header{}, 846 Cookie: nil, 847 TLS: &tls.ConnectionState{}, 848 } 849 verifier.EXPECT().Validate(ctx, gomock.Any()).Return(nil, true, "MockJWTToken", fmt.Errorf("auth failed")) 850 userCredentials(ctx, portContext, r, d) 851 So(len(d.UserAttributes), ShouldEqual, 0) 852 So(d.Redirect, ShouldBeTrue) 853 }) 854 855 Convey("When the request is TLS and user authorization succeeds with a refresh token, the cookie must be set", func() { 856 u, _ := url.Parse("http://www.foo.com/admin") 857 d := &NetworkAuthResponse{} 858 859 h := http.Header{} 860 h.Add("Authorization", "Bearer MockJWTToken") 861 r := &Request{ 862 SourceAddress: &net.TCPAddr{ 863 IP: net.ParseIP("45.1.1.1"), 864 Port: 1000, 865 }, 866 OriginalDestination: &net.TCPAddr{ 867 IP: net.ParseIP("10.1.1.1"), 868 Port: 80, 869 }, 870 Method: "GET", 871 URL: u, 872 RequestURI: "/admin", 873 Header: http.Header{}, 874 Cookie: nil, 875 TLS: &tls.ConnectionState{}, 876 } 877 verifier.EXPECT().Validate(ctx, gomock.Any()).Return(nil, true, "NewToken", fmt.Errorf("auth failed")) 878 userCredentials(ctx, portContext, r, d) 879 So(len(d.UserAttributes), ShouldEqual, 0) 880 So(d.Redirect, ShouldBeTrue) 881 So(d.Cookie, ShouldNotBeNil) 882 So(d.Cookie.Name, ShouldEqual, "X-APORETO-AUTH") 883 So(d.Cookie.Value, ShouldEqual, "NewToken") 884 So(d.Cookie.HttpOnly, ShouldBeTrue) 885 So(d.Cookie.Secure, ShouldBeTrue) 886 So(d.Cookie.Path, ShouldEqual, "/") 887 }) 888 }) 889 }