github.com/hernad/nomad@v1.6.112/nomad/acl_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package nomad 5 6 import ( 7 "fmt" 8 "path" 9 "testing" 10 "time" 11 12 msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc" 13 "github.com/hernad/nomad/acl" 14 "github.com/hernad/nomad/ci" 15 "github.com/hernad/nomad/helper/pointer" 16 "github.com/hernad/nomad/helper/uuid" 17 "github.com/hernad/nomad/nomad/mock" 18 "github.com/hernad/nomad/nomad/structs" 19 "github.com/hernad/nomad/nomad/structs/config" 20 "github.com/hernad/nomad/testutil" 21 "github.com/shoenig/test" 22 "github.com/shoenig/test/must" 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestAuthenticate_mTLS(t *testing.T) { 27 ci.Parallel(t) 28 29 // Set up a cluster with mTLS and ACLs 30 31 dir := t.TempDir() 32 33 tlsCfg := &config.TLSConfig{ 34 EnableHTTP: true, 35 EnableRPC: true, 36 VerifyServerHostname: true, 37 CAFile: "../helper/tlsutil/testdata/nomad-agent-ca.pem", 38 CertFile: "../helper/tlsutil/testdata/regionFoo-server-nomad.pem", 39 KeyFile: "../helper/tlsutil/testdata/regionFoo-server-nomad-key.pem", 40 } 41 clientTLSCfg := tlsCfg.Copy() 42 clientTLSCfg.CertFile = "../helper/tlsutil/testdata/regionFoo-client-nomad.pem" 43 clientTLSCfg.KeyFile = "../helper/tlsutil/testdata/regionFoo-client-nomad-key.pem" 44 45 setCfg := func(name string, bootstrapExpect int) func(*Config) { 46 return func(c *Config) { 47 c.Region = "regionFoo" 48 c.AuthoritativeRegion = "regionFoo" 49 c.ACLEnabled = true 50 c.BootstrapExpect = bootstrapExpect 51 c.NumSchedulers = 0 52 c.DevMode = false 53 c.DataDir = path.Join(dir, name) 54 c.TLSConfig = tlsCfg 55 } 56 } 57 58 leader, cleanupLeader := TestServer(t, setCfg("node1", 1)) 59 defer cleanupLeader() 60 testutil.WaitForLeader(t, leader.RPC) 61 62 follower, cleanupFollower := TestServer(t, setCfg("node2", 0)) 63 defer cleanupFollower() 64 65 TestJoin(t, leader, follower) 66 testutil.WaitForLeader(t, leader.RPC) 67 68 testutil.Wait(t, func() (bool, error) { 69 keyset, err := follower.encrypter.activeKeySet() 70 return keyset != nil, err 71 }) 72 73 rootToken := uuid.Generate() 74 var bootstrapResp *structs.ACLTokenUpsertResponse 75 76 codec := rpcClientWithTLS(t, follower, tlsCfg) 77 must.NoError(t, msgpackrpc.CallWithCodec(codec, 78 "ACL.Bootstrap", &structs.ACLTokenBootstrapRequest{ 79 BootstrapSecret: rootToken, 80 WriteRequest: structs.WriteRequest{Region: "regionFoo"}, 81 }, &bootstrapResp)) 82 must.NotNil(t, bootstrapResp) 83 must.Len(t, 1, bootstrapResp.Tokens) 84 rootAccessor := bootstrapResp.Tokens[0].AccessorID 85 86 // create some ACL tokens directly into raft so we can bypass RPC validation 87 // around expiration times 88 89 token1 := mock.ACLToken() 90 token2 := mock.ACLToken() 91 expireTime := time.Now().Add(time.Second * -10) 92 token2.ExpirationTime = &expireTime 93 94 _, _, err := leader.raftApply(structs.ACLTokenUpsertRequestType, 95 &structs.ACLTokenUpsertRequest{Tokens: []*structs.ACLToken{token1, token2}}) 96 must.NoError(t, err) 97 98 // create a node so we can test client RPCs 99 100 node := mock.Node() 101 nodeRegisterReq := &structs.NodeRegisterRequest{ 102 Node: node, 103 WriteRequest: structs.WriteRequest{Region: "regionFoo"}, 104 } 105 var nodeRegisterResp structs.NodeUpdateResponse 106 107 must.NoError(t, msgpackrpc.CallWithCodec(codec, 108 "Node.Register", nodeRegisterReq, &nodeRegisterResp)) 109 must.NotNil(t, bootstrapResp) 110 111 // create some allocations so we can test WorkloadIdentity claims. we'll 112 // create directly into raft so we can bypass RPC validation and the whole 113 // eval, plan, etc. workflow. 114 job := mock.Job() 115 116 _, _, err = leader.raftApply(structs.JobRegisterRequestType, 117 &structs.JobRegisterRequest{Job: job}) 118 must.NoError(t, err) 119 120 alloc1 := mock.Alloc() 121 alloc1.NodeID = node.ID 122 alloc1.ClientStatus = structs.AllocClientStatusFailed 123 alloc1.Job = job 124 alloc1.JobID = job.ID 125 126 alloc2 := mock.Alloc() 127 alloc2.NodeID = node.ID 128 alloc2.Job = job 129 alloc2.JobID = job.ID 130 alloc2.ClientStatus = structs.AllocClientStatusRunning 131 132 claims1 := alloc1.ToTaskIdentityClaims(nil, "web") 133 claims1Token, _, err := leader.encrypter.SignClaims(claims1) 134 must.NoError(t, err, must.Sprint("could not sign claims")) 135 136 claims2 := alloc2.ToTaskIdentityClaims(nil, "web") 137 claims2Token, _, err := leader.encrypter.SignClaims(claims2) 138 must.NoError(t, err, must.Sprint("could not sign claims")) 139 140 planReq := &structs.ApplyPlanResultsRequest{ 141 AllocUpdateRequest: structs.AllocUpdateRequest{ 142 Alloc: []*structs.Allocation{alloc1, alloc2}, 143 Job: job, 144 }, 145 } 146 _, _, err = leader.raftApply(structs.ApplyPlanResultsRequestType, planReq) 147 must.NoError(t, err) 148 149 testutil.WaitForResult(func() (bool, error) { 150 store := follower.fsm.State() 151 alloc, err := store.AllocByID(nil, alloc1.ID) 152 return alloc != nil, err 153 }, func(err error) { 154 t.Fatalf("alloc was not replicated via raft: %v", err) // should never happen 155 }) 156 157 testCases := []struct { 158 name string 159 tlsCfg *config.TLSConfig 160 stale bool 161 testToken string 162 expectAccessor string 163 expectClientID string 164 expectAllocID string 165 expectTLSName string 166 expectIP string 167 expectErr string 168 expectIDKey string 169 sendFromPeer *Server 170 }{ 171 { 172 name: "root token", 173 tlsCfg: clientTLSCfg, // TODO: this is a mixed use cert 174 testToken: rootToken, 175 expectAccessor: rootAccessor, 176 expectIDKey: fmt.Sprintf("token:%s", rootAccessor), 177 }, 178 { 179 name: "from peer to leader without token", // ex. Eval.Dequeue 180 tlsCfg: tlsCfg, 181 expectTLSName: "server.regionFoo.nomad", 182 expectAccessor: "anonymous", 183 expectIP: follower.GetConfig().RPCAddr.IP.String(), 184 sendFromPeer: follower, 185 expectIDKey: "token:anonymous", 186 }, 187 { 188 // note: this test is somewhat bogus because under test all the 189 // servers share the same IP address with the RPC client 190 name: "anonymous forwarded from peer to leader", 191 tlsCfg: tlsCfg, 192 expectAccessor: "anonymous", 193 expectTLSName: "server.regionFoo.nomad", 194 expectIP: "127.0.0.1", 195 expectIDKey: "token:anonymous", 196 }, 197 { 198 name: "invalid token", 199 tlsCfg: clientTLSCfg, 200 testToken: uuid.Generate(), 201 expectTLSName: "server.regionFoo.nomad", 202 expectIP: follower.GetConfig().RPCAddr.IP.String(), 203 expectIDKey: "server.regionFoo.nomad:127.0.0.1", 204 expectErr: "rpc error: Permission denied", 205 }, 206 { 207 name: "from peer to leader with leader ACL", // ex. core job GC 208 tlsCfg: tlsCfg, 209 testToken: leader.getLeaderAcl(), 210 expectTLSName: "server.regionFoo.nomad", 211 expectAccessor: "leader", 212 expectIP: follower.GetConfig().RPCAddr.IP.String(), 213 sendFromPeer: follower, 214 expectIDKey: "token:leader", 215 }, 216 { 217 name: "from client", // ex. Node.GetAllocs 218 tlsCfg: clientTLSCfg, 219 testToken: node.SecretID, 220 expectClientID: node.ID, 221 expectIDKey: fmt.Sprintf("client:%s", node.ID), 222 }, 223 { 224 name: "from client missing secret", // ex. Node.Register 225 tlsCfg: clientTLSCfg, 226 expectAccessor: "anonymous", 227 expectTLSName: "server.regionFoo.nomad", 228 expectIP: follower.GetConfig().RPCAddr.IP.String(), 229 }, 230 { 231 name: "from failed workload", // ex. Variables.List 232 tlsCfg: clientTLSCfg, 233 testToken: claims1Token, 234 expectErr: "rpc error: allocation is terminal", 235 }, 236 { 237 name: "from running workload", // ex. Variables.List 238 tlsCfg: clientTLSCfg, 239 testToken: claims2Token, 240 expectAllocID: alloc2.ID, 241 expectIDKey: fmt.Sprintf("alloc:%s", alloc2.ID), 242 }, 243 { 244 name: "valid user token", 245 tlsCfg: clientTLSCfg, 246 testToken: token1.SecretID, 247 expectAccessor: token1.AccessorID, 248 expectIDKey: fmt.Sprintf("token:%s", token1.AccessorID), 249 }, 250 { 251 name: "expired user token", 252 tlsCfg: clientTLSCfg, 253 testToken: token2.SecretID, 254 expectErr: "rpc error: ACL token expired", 255 }, 256 } 257 258 for _, tc := range testCases { 259 t.Run(tc.name, func(t *testing.T) { 260 261 req := &structs.GenericRequest{ 262 QueryOptions: structs.QueryOptions{ 263 Region: "regionFoo", 264 AllowStale: tc.stale, 265 AuthToken: tc.testToken, 266 }, 267 } 268 var resp structs.ACLWhoAmIResponse 269 var err error 270 271 if tc.sendFromPeer != nil { 272 aclEndpoint := NewACLEndpoint(tc.sendFromPeer, nil) 273 err = aclEndpoint.WhoAmI(req, &resp) 274 } else { 275 err = msgpackrpc.CallWithCodec(codec, "ACL.WhoAmI", req, &resp) 276 } 277 278 if tc.expectErr != "" { 279 must.EqError(t, err, tc.expectErr) 280 return 281 } 282 283 must.NoError(t, err) 284 must.NotNil(t, resp) 285 must.NotNil(t, resp.Identity) 286 287 if tc.expectIDKey != "" { 288 must.Eq(t, tc.expectIDKey, resp.Identity.String(), 289 must.Sprintf("expected identity key for metrics to match")) 290 } 291 292 if tc.expectAccessor != "" { 293 must.NotNil(t, resp.Identity.ACLToken, must.Sprint("expected ACL token")) 294 test.Eq(t, tc.expectAccessor, resp.Identity.ACLToken.AccessorID, 295 test.Sprint("expected ACL token accessor ID")) 296 } 297 298 test.Eq(t, tc.expectClientID, resp.Identity.ClientID, 299 test.Sprint("expected client ID")) 300 301 if tc.expectAllocID != "" { 302 must.NotNil(t, resp.Identity.Claims, must.Sprint("expected claims")) 303 test.Eq(t, tc.expectAllocID, resp.Identity.Claims.AllocationID, 304 test.Sprint("expected workload identity")) 305 } 306 307 test.Eq(t, tc.expectTLSName, resp.Identity.TLSName, test.Sprint("expected TLS name")) 308 309 if tc.expectIP == "" { 310 test.Nil(t, resp.Identity.RemoteIP, test.Sprint("expected no remote IP")) 311 } else { 312 test.Eq(t, tc.expectIP, resp.Identity.RemoteIP.String()) 313 } 314 315 }) 316 } 317 } 318 319 func TestResolveACLToken(t *testing.T) { 320 ci.Parallel(t) 321 322 testCases := []struct { 323 name string 324 testFn func() 325 }{ 326 { 327 name: "leader token", 328 testFn: func() { 329 330 testServer, _, testServerCleanup := TestACLServer(t, nil) 331 defer testServerCleanup() 332 testutil.WaitForLeader(t, testServer.RPC) 333 334 // Check the leader ACL token is correctly set. 335 leaderACL := testServer.getLeaderAcl() 336 require.NotEmpty(t, leaderACL) 337 338 // Resolve the token and ensure it's a management token. 339 aclResp, err := testServer.ResolveToken(leaderACL) 340 require.NoError(t, err) 341 require.NotNil(t, aclResp) 342 require.True(t, aclResp.IsManagement()) 343 }, 344 }, 345 { 346 name: "anonymous token", 347 testFn: func() { 348 349 testServer, _, testServerCleanup := TestACLServer(t, nil) 350 defer testServerCleanup() 351 testutil.WaitForLeader(t, testServer.RPC) 352 353 // Call the function with an empty input secret ID which is 354 // classed as representing anonymous access in clusters with 355 // ACLs enabled. 356 aclResp, err := testServer.ResolveToken("") 357 require.NoError(t, err) 358 require.NotNil(t, aclResp) 359 require.False(t, aclResp.IsManagement()) 360 }, 361 }, 362 { 363 name: "token not found", 364 testFn: func() { 365 366 testServer, _, testServerCleanup := TestACLServer(t, nil) 367 defer testServerCleanup() 368 testutil.WaitForLeader(t, testServer.RPC) 369 370 // Call the function with randomly generated secret ID which 371 // does not exist within state. 372 aclResp, err := testServer.ResolveToken(uuid.Generate()) 373 require.Equal(t, structs.ErrTokenNotFound, err) 374 require.Nil(t, aclResp) 375 }, 376 }, 377 { 378 name: "token expired", 379 testFn: func() { 380 381 testServer, _, testServerCleanup := TestACLServer(t, nil) 382 defer testServerCleanup() 383 testutil.WaitForLeader(t, testServer.RPC) 384 385 // Create a mock token with an expiration time long in the 386 // past, and upsert. 387 token := mock.ACLToken() 388 token.ExpirationTime = pointer.Of(time.Date( 389 1970, time.January, 1, 0, 0, 0, 0, time.UTC)) 390 391 err := testServer.State().UpsertACLTokens( 392 structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) 393 require.NoError(t, err) 394 395 // Perform the function call which should result in finding the 396 // token has expired. 397 aclResp, err := testServer.ResolveToken(uuid.Generate()) 398 require.Equal(t, structs.ErrTokenNotFound, err) 399 require.Nil(t, aclResp) 400 }, 401 }, 402 { 403 name: "management token", 404 testFn: func() { 405 406 testServer, _, testServerCleanup := TestACLServer(t, nil) 407 defer testServerCleanup() 408 testutil.WaitForLeader(t, testServer.RPC) 409 410 // Generate a management token and upsert this. 411 managementToken := mock.ACLToken() 412 managementToken.Type = structs.ACLManagementToken 413 managementToken.Policies = nil 414 415 err := testServer.State().UpsertACLTokens( 416 structs.MsgTypeTestSetup, 10, []*structs.ACLToken{managementToken}) 417 require.NoError(t, err) 418 419 // Resolve the token and check that we received a management 420 // ACL. 421 aclResp, err := testServer.ResolveToken(managementToken.SecretID) 422 require.Nil(t, err) 423 require.NotNil(t, aclResp) 424 require.True(t, aclResp.IsManagement()) 425 require.Equal(t, acl.ManagementACL, aclResp) 426 }, 427 }, 428 { 429 name: "client token with policies only", 430 testFn: func() { 431 432 testServer, _, testServerCleanup := TestACLServer(t, nil) 433 defer testServerCleanup() 434 testutil.WaitForLeader(t, testServer.RPC) 435 436 // Generate a client token with associated policies and upsert 437 // these. 438 policy1 := mock.ACLPolicy() 439 policy2 := mock.ACLPolicy() 440 err := testServer.State().UpsertACLPolicies( 441 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}) 442 443 clientToken := mock.ACLToken() 444 clientToken.Policies = []string{policy1.Name, policy2.Name} 445 err = testServer.State().UpsertACLTokens( 446 structs.MsgTypeTestSetup, 20, []*structs.ACLToken{clientToken}) 447 require.NoError(t, err) 448 449 // Resolve the token and check that we received a client 450 // ACL with appropriate permissions. 451 aclResp, err := testServer.ResolveToken(clientToken.SecretID) 452 require.Nil(t, err) 453 require.NotNil(t, aclResp) 454 require.False(t, aclResp.IsManagement()) 455 456 allowed := aclResp.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs) 457 require.True(t, allowed) 458 allowed = aclResp.AllowNamespaceOperation("other", acl.NamespaceCapabilityListJobs) 459 require.False(t, allowed) 460 461 // Resolve the same token again and ensure we get the same 462 // result. 463 aclResp2, err := testServer.ResolveToken(clientToken.SecretID) 464 require.Nil(t, err) 465 require.NotNil(t, aclResp2) 466 require.Equal(t, aclResp, aclResp2) 467 468 // Bust the cache by upserting the policy 469 err = testServer.State().UpsertACLPolicies( 470 structs.MsgTypeTestSetup, 30, []*structs.ACLPolicy{policy1}) 471 require.Nil(t, err) 472 473 // Resolve the same token again, should get different value 474 aclResp3, err := testServer.ResolveToken(clientToken.SecretID) 475 require.Nil(t, err) 476 require.NotNil(t, aclResp3) 477 require.NotEqual(t, aclResp2, aclResp3) 478 }, 479 }, 480 { 481 name: "client token with roles only", 482 testFn: func() { 483 484 testServer, _, testServerCleanup := TestACLServer(t, nil) 485 defer testServerCleanup() 486 testutil.WaitForLeader(t, testServer.RPC) 487 488 // Create a client token that only has a link to a role. 489 policy1 := mock.ACLPolicy() 490 policy2 := mock.ACLPolicy() 491 err := testServer.State().UpsertACLPolicies( 492 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}) 493 494 aclRole := mock.ACLRole() 495 aclRole.Policies = []*structs.ACLRolePolicyLink{ 496 {Name: policy1.Name}, 497 {Name: policy2.Name}, 498 } 499 err = testServer.State().UpsertACLRoles( 500 structs.MsgTypeTestSetup, 30, []*structs.ACLRole{aclRole}, false) 501 require.NoError(t, err) 502 503 clientToken := mock.ACLToken() 504 clientToken.Policies = []string{} 505 clientToken.Roles = []*structs.ACLTokenRoleLink{{ID: aclRole.ID}} 506 err = testServer.State().UpsertACLTokens( 507 structs.MsgTypeTestSetup, 30, []*structs.ACLToken{clientToken}) 508 require.NoError(t, err) 509 510 // Resolve the token and check that we received a client 511 // ACL with appropriate permissions. 512 aclResp, err := testServer.ResolveToken(clientToken.SecretID) 513 require.Nil(t, err) 514 require.NotNil(t, aclResp) 515 require.False(t, aclResp.IsManagement()) 516 517 allowed := aclResp.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs) 518 require.True(t, allowed) 519 allowed = aclResp.AllowNamespaceOperation("other", acl.NamespaceCapabilityListJobs) 520 require.False(t, allowed) 521 522 // Remove the policies from the ACL role and ensure the resolution 523 // permissions are updated. 524 aclRole.Policies = []*structs.ACLRolePolicyLink{} 525 err = testServer.State().UpsertACLRoles( 526 structs.MsgTypeTestSetup, 40, []*structs.ACLRole{aclRole}, false) 527 require.NoError(t, err) 528 529 aclResp, err = testServer.ResolveToken(clientToken.SecretID) 530 require.Nil(t, err) 531 require.NotNil(t, aclResp) 532 require.False(t, aclResp.IsManagement()) 533 require.False(t, aclResp.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs)) 534 }, 535 }, 536 { 537 name: "client with roles and policies", 538 testFn: func() { 539 540 testServer, _, testServerCleanup := TestACLServer(t, nil) 541 defer testServerCleanup() 542 testutil.WaitForLeader(t, testServer.RPC) 543 544 // Generate two policies, each with a different namespace 545 // permission set. 546 policy1 := &structs.ACLPolicy{ 547 Name: "policy-" + uuid.Generate(), 548 Rules: `namespace "platform" { policy = "write"}`, 549 CreateIndex: 10, 550 ModifyIndex: 10, 551 } 552 policy1.SetHash() 553 policy2 := &structs.ACLPolicy{ 554 Name: "policy-" + uuid.Generate(), 555 Rules: `namespace "web" { policy = "write"}`, 556 CreateIndex: 10, 557 ModifyIndex: 10, 558 } 559 policy2.SetHash() 560 561 err := testServer.State().UpsertACLPolicies( 562 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}) 563 require.NoError(t, err) 564 565 // Create a role which references the policy that has access to 566 // the web namespace. 567 aclRole := mock.ACLRole() 568 aclRole.Policies = []*structs.ACLRolePolicyLink{{Name: policy2.Name}} 569 err = testServer.State().UpsertACLRoles( 570 structs.MsgTypeTestSetup, 20, []*structs.ACLRole{aclRole}, false) 571 require.NoError(t, err) 572 573 // Create a token which references the policy and role. 574 clientToken := mock.ACLToken() 575 clientToken.Policies = []string{policy1.Name} 576 clientToken.Roles = []*structs.ACLTokenRoleLink{{ID: aclRole.ID}} 577 err = testServer.State().UpsertACLTokens( 578 structs.MsgTypeTestSetup, 30, []*structs.ACLToken{clientToken}) 579 require.NoError(t, err) 580 581 // Resolve the token and check that we received a client 582 // ACL with appropriate permissions. 583 aclResp, err := testServer.ResolveToken(clientToken.SecretID) 584 require.Nil(t, err) 585 require.NotNil(t, aclResp) 586 require.False(t, aclResp.IsManagement()) 587 588 allowed := aclResp.AllowNamespaceOperation("platform", acl.NamespaceCapabilityListJobs) 589 require.True(t, allowed) 590 allowed = aclResp.AllowNamespaceOperation("web", acl.NamespaceCapabilityListJobs) 591 require.True(t, allowed) 592 }, 593 }, 594 } 595 596 for _, tc := range testCases { 597 t.Run(tc.name, func(t *testing.T) { 598 tc.testFn() 599 }) 600 } 601 } 602 603 func TestResolveSecretToken(t *testing.T) { 604 ci.Parallel(t) 605 606 testServer, _, testServerCleanup := TestACLServer(t, nil) 607 defer testServerCleanup() 608 testutil.WaitForLeader(t, testServer.RPC) 609 610 testCases := []struct { 611 name string 612 testFn func(testServer *Server) 613 }{ 614 { 615 name: "valid token", 616 testFn: func(testServer *Server) { 617 618 // Generate and upsert a token. 619 token := mock.ACLToken() 620 err := testServer.State().UpsertACLTokens( 621 structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) 622 require.NoError(t, err) 623 624 // Attempt to look up the token and perform checks. 625 tokenResp, err := testServer.ResolveSecretToken(token.SecretID) 626 require.NoError(t, err) 627 require.NotNil(t, tokenResp) 628 require.Equal(t, token, tokenResp) 629 }, 630 }, 631 { 632 name: "anonymous token", 633 testFn: func(testServer *Server) { 634 635 // Call the function with an empty input secret ID which is 636 // classed as representing anonymous access in clusters with 637 // ACLs enabled. 638 tokenResp, err := testServer.ResolveSecretToken("") 639 require.NoError(t, err) 640 require.NotNil(t, tokenResp) 641 require.Equal(t, structs.AnonymousACLToken, tokenResp) 642 }, 643 }, 644 { 645 name: "token not found", 646 testFn: func(testServer *Server) { 647 648 // Call the function with randomly generated secret ID which 649 // does not exist within state. 650 tokenResp, err := testServer.ResolveSecretToken(uuid.Generate()) 651 require.Equal(t, structs.ErrTokenNotFound, err) 652 require.Nil(t, tokenResp) 653 }, 654 }, 655 { 656 name: "token expired", 657 testFn: func(testServer *Server) { 658 659 // Create a mock token with an expiration time long in the 660 // past, and upsert. 661 token := mock.ACLToken() 662 token.ExpirationTime = pointer.Of(time.Date( 663 1970, time.January, 1, 0, 0, 0, 0, time.UTC)) 664 665 err := testServer.State().UpsertACLTokens( 666 structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) 667 require.NoError(t, err) 668 669 // Perform the function call which should result in finding the 670 // token has expired. 671 tokenResp, err := testServer.ResolveSecretToken(uuid.Generate()) 672 require.Equal(t, structs.ErrTokenNotFound, err) 673 require.Nil(t, tokenResp) 674 }, 675 }, 676 } 677 678 for _, tc := range testCases { 679 t.Run(tc.name, func(t *testing.T) { 680 tc.testFn(testServer) 681 }) 682 } 683 } 684 685 func TestResolveClaims(t *testing.T) { 686 ci.Parallel(t) 687 688 srv, _, cleanup := TestACLServer(t, nil) 689 defer cleanup() 690 691 store := srv.fsm.State() 692 index := uint64(100) 693 694 alloc := mock.Alloc() 695 696 claims := &structs.IdentityClaims{ 697 Namespace: alloc.Namespace, 698 JobID: alloc.Job.ID, 699 AllocationID: alloc.ID, 700 TaskName: alloc.Job.TaskGroups[0].Tasks[0].Name, 701 } 702 703 // unrelated policy 704 policy0 := mock.ACLPolicy() 705 706 // policy for job 707 policy1 := mock.ACLPolicy() 708 policy1.JobACL = &structs.JobACL{ 709 Namespace: claims.Namespace, 710 JobID: claims.JobID, 711 } 712 713 // policy for job and group 714 policy2 := mock.ACLPolicy() 715 policy2.JobACL = &structs.JobACL{ 716 Namespace: claims.Namespace, 717 JobID: claims.JobID, 718 Group: alloc.Job.TaskGroups[0].Name, 719 } 720 721 // policy for job and group and task 722 policy3 := mock.ACLPolicy() 723 policy3.JobACL = &structs.JobACL{ 724 Namespace: claims.Namespace, 725 JobID: claims.JobID, 726 Group: alloc.Job.TaskGroups[0].Name, 727 Task: claims.TaskName, 728 } 729 730 // policy for job and group but different task 731 policy4 := mock.ACLPolicy() 732 policy4.JobACL = &structs.JobACL{ 733 Namespace: claims.Namespace, 734 JobID: claims.JobID, 735 Group: alloc.Job.TaskGroups[0].Name, 736 Task: "another", 737 } 738 739 // policy for job but different group 740 policy5 := mock.ACLPolicy() 741 policy5.JobACL = &structs.JobACL{ 742 Namespace: claims.Namespace, 743 JobID: claims.JobID, 744 Group: "another", 745 } 746 747 // policy for same namespace but different job 748 policy6 := mock.ACLPolicy() 749 policy6.JobACL = &structs.JobACL{ 750 Namespace: claims.Namespace, 751 JobID: "another", 752 } 753 754 // policy for same job in different namespace 755 policy7 := mock.ACLPolicy() 756 policy7.JobACL = &structs.JobACL{ 757 Namespace: "another", 758 JobID: claims.JobID, 759 } 760 761 aclObj, err := srv.ResolveClaims(claims) 762 must.Nil(t, aclObj) 763 must.EqError(t, err, "allocation does not exist") 764 765 // upsert the allocation 766 index++ 767 err = store.UpsertAllocs(structs.MsgTypeTestSetup, index, []*structs.Allocation{alloc}) 768 must.NoError(t, err) 769 770 // Resolve claims and check we that the ACL object without policies provides no access 771 aclObj, err = srv.ResolveClaims(claims) 772 must.NoError(t, err) 773 must.NotNil(t, aclObj) 774 must.False(t, aclObj.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs)) 775 776 // Add the policies 777 index++ 778 err = store.UpsertACLPolicies(structs.MsgTypeTestSetup, index, []*structs.ACLPolicy{ 779 policy0, policy1, policy2, policy3, policy4, policy5, policy6, policy7}) 780 must.NoError(t, err) 781 782 // Re-resolve and check that the resulting ACL looks reasonable 783 aclObj, err = srv.ResolveClaims(claims) 784 must.NoError(t, err) 785 must.NotNil(t, aclObj) 786 must.False(t, aclObj.IsManagement()) 787 must.True(t, aclObj.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs)) 788 must.False(t, aclObj.AllowNamespaceOperation("other", acl.NamespaceCapabilityListJobs)) 789 790 // Resolve the same claim again, should get cache value 791 aclObj2, err := srv.ResolveClaims(claims) 792 must.NoError(t, err) 793 must.NotNil(t, aclObj) 794 must.Eq(t, aclObj, aclObj2, must.Sprintf("expected cached value")) 795 796 policies, err := srv.resolvePoliciesForClaims(claims) 797 must.NoError(t, err) 798 must.Len(t, 3, policies) 799 must.SliceContainsAll(t, policies, []*structs.ACLPolicy{policy1, policy2, policy3}) 800 }