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  }