diff --git a/nomad/acl_endpoint.go b/nomad/acl_endpoint.go index 9b7bbdaa7..68ea4e684 100644 --- a/nomad/acl_endpoint.go +++ b/nomad/acl_endpoint.go @@ -33,6 +33,13 @@ func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *struct } defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_policies"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Validate non-zero set of policies if len(args.Policies) == 0 { return fmt.Errorf("must specify as least one policy") @@ -69,6 +76,13 @@ func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *struct } defer metrics.MeasureSince([]string{"nomad", "acl", "delete_policies"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Validate non-zero set of policies if len(args.Names) == 0 { return fmt.Errorf("must specify as least one policy") @@ -95,6 +109,13 @@ func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.AC } defer metrics.MeasureSince([]string{"nomad", "acl", "list_policies"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, @@ -150,6 +171,13 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.S } defer metrics.MeasureSince([]string{"nomad", "acl", "get_policy"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, @@ -188,6 +216,13 @@ func (a *ACL) GetPolicies(args *structs.ACLPolicySetRequest, reply *structs.ACLP } defer metrics.MeasureSince([]string{"nomad", "acl", "get_policies"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, @@ -293,6 +328,13 @@ func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.A } defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_tokens"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Validate non-zero set of tokens if len(args.Tokens) == 0 { return fmt.Errorf("must specify as least one token") @@ -371,6 +413,13 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G } defer metrics.MeasureSince([]string{"nomad", "acl", "delete_tokens"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Validate non-zero set of tokens if len(args.AccessorIDs) == 0 { return fmt.Errorf("must specify as least one token") @@ -397,6 +446,13 @@ func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTo } defer metrics.MeasureSince([]string{"nomad", "acl", "list_tokens"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, @@ -448,6 +504,13 @@ func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.Sin } defer metrics.MeasureSince([]string{"nomad", "acl", "get_token"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, @@ -486,6 +549,13 @@ func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLToke } defer metrics.MeasureSince([]string{"nomad", "acl", "get_tokens"}, time.Now()) + // Check management level permissions + if acl, err := a.srv.resolveToken(args.SecretID); err != nil { + return err + } else if acl == nil || !acl.IsManagement() { + return structs.ErrPermissionDenied + } + // Setup the blocking query opts := blockingOptions{ queryOpts: &args.QueryOptions, diff --git a/nomad/acl_endpoint_test.go b/nomad/acl_endpoint_test.go index 51832246b..ec2271941 100644 --- a/nomad/acl_endpoint_test.go +++ b/nomad/acl_endpoint_test.go @@ -14,7 +14,7 @@ import ( func TestACLEndpoint_GetPolicy(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -25,8 +25,11 @@ func TestACLEndpoint_GetPolicy(t *testing.T) { // Lookup the policy get := &structs.ACLPolicySpecificRequest{ - Name: policy.Name, - QueryOptions: structs.QueryOptions{Region: "global"}, + Name: policy.Name, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.SingleACLPolicyResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", get, &resp); err != nil { @@ -46,7 +49,7 @@ func TestACLEndpoint_GetPolicy(t *testing.T) { func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -78,6 +81,7 @@ func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 150, + SecretID: root.SecretID, }, } var resp structs.SingleACLPolicyResponse @@ -124,7 +128,7 @@ func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) { func TestACLEndpoint_GetPolicies(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -136,8 +140,11 @@ func TestACLEndpoint_GetPolicies(t *testing.T) { // Lookup the policy get := &structs.ACLPolicySetRequest{ - Names: []string{policy.Name, policy2.Name}, - QueryOptions: structs.QueryOptions{Region: "global"}, + Names: []string{policy.Name, policy2.Name}, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.ACLPolicySetResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.GetPolicies", get, &resp); err != nil { @@ -160,7 +167,7 @@ func TestACLEndpoint_GetPolicies(t *testing.T) { func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -192,6 +199,7 @@ func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 150, + SecretID: root.SecretID, }, } var resp structs.ACLPolicySetResponse @@ -238,7 +246,7 @@ func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) { func TestACLEndpoint_ListPolicies(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -253,7 +261,10 @@ func TestACLEndpoint_ListPolicies(t *testing.T) { // Lookup the policies get := &structs.ACLPolicyListRequest{ - QueryOptions: structs.QueryOptions{Region: "global"}, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.ACLPolicyListResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.ListPolicies", get, &resp); err != nil { @@ -265,8 +276,9 @@ func TestACLEndpoint_ListPolicies(t *testing.T) { // Lookup the policies by prefix get = &structs.ACLPolicyListRequest{ QueryOptions: structs.QueryOptions{ - Region: "global", - Prefix: "aaaabb", + Region: "global", + Prefix: "aaaabb", + SecretID: root.SecretID, }, } var resp2 structs.ACLPolicyListResponse @@ -279,7 +291,7 @@ func TestACLEndpoint_ListPolicies(t *testing.T) { func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -299,6 +311,7 @@ func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 1, + SecretID: root.SecretID, }, } start := time.Now() @@ -338,7 +351,7 @@ func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) { func TestACLEndpoint_DeletePolicies(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -349,8 +362,11 @@ func TestACLEndpoint_DeletePolicies(t *testing.T) { // Lookup the policies req := &structs.ACLPolicyDeleteRequest{ - Names: []string{p1.Name}, - WriteRequest: structs.WriteRequest{Region: "global"}, + Names: []string{p1.Name}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.GenericResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.DeletePolicies", req, &resp); err != nil { @@ -361,7 +377,7 @@ func TestACLEndpoint_DeletePolicies(t *testing.T) { func TestACLEndpoint_UpsertPolicies(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -371,8 +387,11 @@ func TestACLEndpoint_UpsertPolicies(t *testing.T) { // Lookup the policies req := &structs.ACLPolicyUpsertRequest{ - Policies: []*structs.ACLPolicy{p1}, - WriteRequest: structs.WriteRequest{Region: "global"}, + Policies: []*structs.ACLPolicy{p1}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.GenericResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.UpsertPolicies", req, &resp); err != nil { @@ -388,7 +407,7 @@ func TestACLEndpoint_UpsertPolicies(t *testing.T) { func TestACLEndpoint_UpsertPolicies_Invalid(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -399,8 +418,11 @@ func TestACLEndpoint_UpsertPolicies_Invalid(t *testing.T) { // Lookup the policies req := &structs.ACLPolicyUpsertRequest{ - Policies: []*structs.ACLPolicy{p1}, - WriteRequest: structs.WriteRequest{Region: "global"}, + Policies: []*structs.ACLPolicy{p1}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.GenericResponse err := msgpackrpc.CallWithCodec(codec, "ACL.UpsertPolicies", req, &resp) @@ -412,7 +434,7 @@ func TestACLEndpoint_UpsertPolicies_Invalid(t *testing.T) { func TestACLEndpoint_GetToken(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -423,8 +445,11 @@ func TestACLEndpoint_GetToken(t *testing.T) { // Lookup the token get := &structs.ACLTokenSpecificRequest{ - AccessorID: token.AccessorID, - QueryOptions: structs.QueryOptions{Region: "global"}, + AccessorID: token.AccessorID, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.SingleACLTokenResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.GetToken", get, &resp); err != nil { @@ -444,7 +469,7 @@ func TestACLEndpoint_GetToken(t *testing.T) { func TestACLEndpoint_GetToken_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -476,6 +501,7 @@ func TestACLEndpoint_GetToken_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 150, + SecretID: root.SecretID, }, } var resp structs.SingleACLTokenResponse @@ -522,7 +548,7 @@ func TestACLEndpoint_GetToken_Blocking(t *testing.T) { func TestACLEndpoint_GetTokens(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -534,8 +560,11 @@ func TestACLEndpoint_GetTokens(t *testing.T) { // Lookup the token get := &structs.ACLTokenSetRequest{ - AccessorIDS: []string{token.AccessorID, token2.AccessorID}, - QueryOptions: structs.QueryOptions{Region: "global"}, + AccessorIDS: []string{token.AccessorID, token2.AccessorID}, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.ACLTokenSetResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.GetTokens", get, &resp); err != nil { @@ -557,7 +586,7 @@ func TestACLEndpoint_GetTokens(t *testing.T) { func TestACLEndpoint_GetTokens_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -589,6 +618,7 @@ func TestACLEndpoint_GetTokens_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 150, + SecretID: root.SecretID, }, } var resp structs.ACLTokenSetResponse @@ -635,7 +665,7 @@ func TestACLEndpoint_GetTokens_Blocking(t *testing.T) { func TestACLEndpoint_ListTokens(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -651,7 +681,10 @@ func TestACLEndpoint_ListTokens(t *testing.T) { // Lookup the tokens get := &structs.ACLTokenListRequest{ - QueryOptions: structs.QueryOptions{Region: "global"}, + QueryOptions: structs.QueryOptions{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.ACLTokenListResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.ListTokens", get, &resp); err != nil { @@ -663,8 +696,9 @@ func TestACLEndpoint_ListTokens(t *testing.T) { // Lookup the tokens by prefix get = &structs.ACLTokenListRequest{ QueryOptions: structs.QueryOptions{ - Region: "global", - Prefix: "aaaabb", + Region: "global", + Prefix: "aaaabb", + SecretID: root.SecretID, }, } var resp2 structs.ACLTokenListResponse @@ -678,7 +712,8 @@ func TestACLEndpoint_ListTokens(t *testing.T) { get = &structs.ACLTokenListRequest{ GlobalOnly: true, QueryOptions: structs.QueryOptions{ - Region: "global", + Region: "global", + SecretID: root.SecretID, }, } var resp3 structs.ACLTokenListResponse @@ -691,7 +726,7 @@ func TestACLEndpoint_ListTokens(t *testing.T) { func TestACLEndpoint_ListTokens_Blocking(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() state := s1.fsm.State() codec := rpcClient(t, s1) @@ -711,6 +746,7 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) { QueryOptions: structs.QueryOptions{ Region: "global", MinQueryIndex: 2, + SecretID: root.SecretID, }, } start := time.Now() @@ -750,7 +786,7 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) { func TestACLEndpoint_DeleteTokens(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -761,8 +797,11 @@ func TestACLEndpoint_DeleteTokens(t *testing.T) { // Lookup the tokens req := &structs.ACLTokenDeleteRequest{ - AccessorIDs: []string{p1.AccessorID}, - WriteRequest: structs.WriteRequest{Region: "global"}, + AccessorIDs: []string{p1.AccessorID}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.GenericResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.DeleteTokens", req, &resp); err != nil { @@ -808,7 +847,7 @@ func TestACLEndpoint_Bootstrap(t *testing.T) { func TestACLEndpoint_UpsertTokens(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -819,8 +858,11 @@ func TestACLEndpoint_UpsertTokens(t *testing.T) { // Lookup the tokens req := &structs.ACLTokenUpsertRequest{ - Tokens: []*structs.ACLToken{p1}, - WriteRequest: structs.WriteRequest{Region: "global"}, + Tokens: []*structs.ACLToken{p1}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.ACLTokenUpsertResponse if err := msgpackrpc.CallWithCodec(codec, "ACL.UpsertTokens", req, &resp); err != nil { @@ -861,7 +903,7 @@ func TestACLEndpoint_UpsertTokens(t *testing.T) { func TestACLEndpoint_UpsertTokens_Invalid(t *testing.T) { t.Parallel() - s1, _ := testACLServer(t, nil) + s1, root := testACLServer(t, nil) defer s1.Shutdown() codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) @@ -872,8 +914,11 @@ func TestACLEndpoint_UpsertTokens_Invalid(t *testing.T) { // Lookup the tokens req := &structs.ACLTokenUpsertRequest{ - Tokens: []*structs.ACLToken{p1}, - WriteRequest: structs.WriteRequest{Region: "global"}, + Tokens: []*structs.ACLToken{p1}, + WriteRequest: structs.WriteRequest{ + Region: "global", + SecretID: root.SecretID, + }, } var resp structs.GenericResponse err := msgpackrpc.CallWithCodec(codec, "ACL.UpsertTokens", req, &resp)