WI: allow workloads to use RPCs associated with HTTP API (#15870)
This changeset allows Workload Identities to authenticate to all the RPCs that support HTTP API endpoints, for use with PR #15864. * Extends the work done for pre-forwarding authentication to all RPCs that support a HTTP API endpoint. * Consolidates the auth helpers used by the CSI, Service Registration, and Node endpoints that are currently used to support both tokens and client secrets. Intentionally excluded from this changeset: * The Variables endpoint still has custom handling because of the implicit policies. Ideally we'll figure out an efficient way to resolve those into real policies and then we can get rid of that custom handling. * The RPCs that don't currently support auth tokens (i.e. those that don't support HTTP endpoints) have not been updated with the new pre-forwarding auth We'll be doing this under a separate PR to support RPC rate metrics.
This commit is contained in:
parent
57f8ebfa26
commit
f3f64af821
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
identity: Allow workloads to use RPCs associated with HTTP API
|
||||
```
|
|
@ -68,8 +68,16 @@ rules:
|
|||
authErr := $A.$B.Authenticate($A.ctx, args)
|
||||
...
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
return $C
|
||||
}
|
||||
...
|
||||
- pattern-not-inside: |
|
||||
authErr := $A.$B.Authenticate(nil, args)
|
||||
...
|
||||
if authErr != nil {
|
||||
return $C
|
||||
}
|
||||
...
|
||||
- metavariable-pattern:
|
||||
metavariable: $METHOD
|
||||
patterns:
|
||||
|
|
44
nomad/acl.go
44
nomad/acl.go
|
@ -156,7 +156,33 @@ func (s *Server) remoteIPFromRPCContext(ctx *RPCContext) (net.IP, error) {
|
|||
return nil, structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
func (s *Server) ResolveACL(aclToken *structs.ACLToken) (*acl.ACL, error) {
|
||||
// ResolveACL is an authentication wrapper which handles resolving both ACL
|
||||
// tokens and Workload Identities. If both are provided the ACL token is
|
||||
// preferred, but it is best for the RPC caller to only include the credentials
|
||||
// for the identity they intend the operation to be performed with.
|
||||
func (s *Server) ResolveACL(args structs.RequestWithIdentity) (*acl.ACL, error) {
|
||||
identity := args.GetIdentity()
|
||||
if !s.config.ACLEnabled || identity == nil {
|
||||
return nil, nil
|
||||
}
|
||||
aclToken := identity.GetACLToken()
|
||||
if aclToken != nil {
|
||||
return s.ResolveACLForToken(aclToken)
|
||||
}
|
||||
claims := identity.GetClaims()
|
||||
if claims != nil {
|
||||
return s.ResolveClaims(claims)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ResolveACLForToken resolves an ACL from a token only. It should be used only
|
||||
// by Variables endpoints, which have additional implicit policies for their
|
||||
// claims so we can't wrap them up in ResolveACL.
|
||||
//
|
||||
// TODO: figure out a way to the Variables endpoint implicit policies baked into
|
||||
// their acl.ACL object so that we can avoid using this method.
|
||||
func (s *Server) ResolveACLForToken(aclToken *structs.ACLToken) (*acl.ACL, error) {
|
||||
if !s.config.ACLEnabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -167,6 +193,22 @@ func (s *Server) ResolveACL(aclToken *structs.ACLToken) (*acl.ACL, error) {
|
|||
return resolveACLFromToken(snap, s.aclCache, aclToken)
|
||||
}
|
||||
|
||||
// ResolveClientOrACL resolves an ACL if the identity has a token or claim, and
|
||||
// falls back to verifying the client ID if one has been set
|
||||
func (s *Server) ResolveClientOrACL(args structs.RequestWithIdentity) (*acl.ACL, error) {
|
||||
identity := args.GetIdentity()
|
||||
if !s.config.ACLEnabled || identity == nil || identity.ClientID != "" {
|
||||
return nil, nil
|
||||
}
|
||||
aclObj, err := s.ResolveACL(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Returns either the users aclObj, or nil if ACLs are disabled.
|
||||
return aclObj, nil
|
||||
}
|
||||
|
||||
// ResolveToken is used to translate an ACL Token Secret ID into
|
||||
// an ACL object, nil if ACLs are disabled, or an error.
|
||||
func (s *Server) ResolveToken(secretID string) (*acl.ACL, error) {
|
||||
|
|
|
@ -73,15 +73,19 @@ func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *struct
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
if done, err := a.srv.forward("ACL.UpsertPolicies", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_policies"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -117,15 +121,19 @@ func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *struct
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
if done, err := a.srv.forward("ACL.DeletePolicies", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "delete_policies"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -152,14 +160,17 @@ func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.AC
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("ACL.ListPolicies", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "list_policies"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil {
|
||||
|
@ -242,13 +253,17 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.S
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("ACL.GetPolicy", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_policy"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil {
|
||||
|
@ -513,7 +528,7 @@ func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.A
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
// Validate non-zero set of tokens
|
||||
if len(args.Tokens) == 0 {
|
||||
return structs.NewErrRPCCoded(http.StatusBadRequest, "must specify as least one token")
|
||||
|
@ -545,10 +560,13 @@ func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.A
|
|||
if done, err := a.srv.forward(structs.ACLUpsertTokensRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_tokens"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -674,7 +692,7 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
// Validate non-zero set of tokens
|
||||
if len(args.AccessorIDs) == 0 {
|
||||
return structs.NewErrRPCCoded(400, "must specify as least one token")
|
||||
|
@ -683,10 +701,13 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G
|
|||
if done, err := a.srv.forward("ACL.DeleteTokens", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "delete_tokens"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -753,13 +774,17 @@ func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTo
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("ACL.ListTokens", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "list_tokens"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -837,12 +862,16 @@ func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.Sin
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("ACL.GetToken", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_token"}, time.Now())
|
||||
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -898,13 +927,17 @@ func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLToke
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("ACL.GetTokens", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_tokens"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1134,7 +1167,7 @@ func (a *ACL) UpsertRoles(
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
// This endpoint always forwards to the authoritative region as ACL roles
|
||||
// are global.
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
@ -1142,6 +1175,9 @@ func (a *ACL) UpsertRoles(
|
|||
if done, err := a.srv.forward(structs.ACLUpsertRolesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_roles"}, time.Now())
|
||||
|
||||
// ACL roles can only be used once all servers, in all federated regions
|
||||
|
@ -1151,8 +1187,8 @@ func (a *ACL) UpsertRoles(
|
|||
minACLRoleVersion)
|
||||
}
|
||||
|
||||
// Only tokens with management level permissions can create ACL roles.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
// Only management level permissions can create ACL roles.
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1278,6 +1314,8 @@ func (a *ACL) DeleteRolesByID(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
|
||||
// This endpoint always forwards to the authoritative region as ACL roles
|
||||
// are global.
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
@ -1285,6 +1323,9 @@ func (a *ACL) DeleteRolesByID(
|
|||
if done, err := a.srv.forward(structs.ACLDeleteRolesByIDRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "delete_roles"}, time.Now())
|
||||
|
||||
// ACL roles can only be used once all servers, in all federated regions
|
||||
|
@ -1294,8 +1335,8 @@ func (a *ACL) DeleteRolesByID(
|
|||
minACLRoleVersion)
|
||||
}
|
||||
|
||||
// Only tokens with management level permissions can create ACL roles.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
// Only management level permissions can create ACL roles.
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1330,13 +1371,17 @@ func (a *ACL) ListRoles(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLListRolesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "list_roles"}, time.Now())
|
||||
|
||||
// Resolve the token and ensure it has some form of permissions.
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil {
|
||||
|
@ -1481,13 +1526,17 @@ func (a *ACL) GetRoleByID(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLGetRoleByIDRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_role_id"}, time.Now())
|
||||
|
||||
// Resolve the token and ensure it has some form of permissions.
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil {
|
||||
|
@ -1565,14 +1614,17 @@ func (a *ACL) GetRoleByName(
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLGetRoleByNameRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_role_name"}, time.Now())
|
||||
|
||||
// Resolve the token and ensure it has some form of permissions.
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil {
|
||||
|
@ -1706,11 +1758,15 @@ func (a *ACL) UpsertAuthMethods(
|
|||
if !a.srv.config.ACLEnabled {
|
||||
return aclDisabled
|
||||
}
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
if done, err := a.srv.forward(structs.ACLUpsertAuthMethodsRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_auth_methods"}, time.Now())
|
||||
|
||||
// ACL auth methods can only be used once all servers in all federated
|
||||
|
@ -1721,7 +1777,7 @@ func (a *ACL) UpsertAuthMethods(
|
|||
}
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1810,10 +1866,14 @@ func (a *ACL) DeleteAuthMethods(
|
|||
}
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(
|
||||
structs.ACLDeleteAuthMethodsRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "delete_auth_methods_by_name"}, time.Now())
|
||||
|
||||
// ACL auth methods can only be used once all servers in all federated
|
||||
|
@ -1824,7 +1884,7 @@ func (a *ACL) DeleteAuthMethods(
|
|||
}
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1906,14 +1966,18 @@ func (a *ACL) GetAuthMethod(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(
|
||||
structs.ACLGetAuthMethodRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_auth_method_name"}, time.Now())
|
||||
|
||||
// Resolve the token and ensure it has some form of permissions.
|
||||
acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
acl, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
|
@ -2037,9 +2101,13 @@ func (a *ACL) UpsertBindingRules(
|
|||
}
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLUpsertBindingRulesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_binding_rules"}, time.Now())
|
||||
|
||||
// ACL binding rules can only be used once all servers in all federated
|
||||
|
@ -2050,7 +2118,7 @@ func (a *ACL) UpsertBindingRules(
|
|||
}
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -2164,9 +2232,13 @@ func (a *ACL) DeleteBindingRules(
|
|||
}
|
||||
args.Region = a.srv.config.AuthoritativeRegion
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLDeleteBindingRulesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "delete_binding_rules"}, time.Now())
|
||||
|
||||
// ACL binding rules can only be used once all servers in all federated
|
||||
|
@ -2177,7 +2249,7 @@ func (a *ACL) DeleteBindingRules(
|
|||
}
|
||||
|
||||
// Check management level permissions.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -2213,13 +2285,17 @@ func (a *ACL) ListBindingRules(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLListBindingRulesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "list_binding_rules"}, time.Now())
|
||||
|
||||
// Check management level permissions.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -2267,13 +2343,17 @@ func (a *ACL) GetBindingRules(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLGetBindingRulesRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_rules"}, time.Now())
|
||||
|
||||
// Check management level permissions.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -2317,13 +2397,17 @@ func (a *ACL) GetBindingRule(
|
|||
return aclDisabled
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.ACLGetBindingRuleRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "acl", "get_binding_rule"}, time.Now())
|
||||
|
||||
// Check management level permissions.
|
||||
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl == nil || !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -2234,7 +2234,7 @@ func TestACL_ListRoles(t *testing.T) {
|
|||
}
|
||||
var aclRoleResp1 structs.ACLRolesListResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, structs.ACLListRolesRPCMethod, aclRoleReq1, &aclRoleResp1)
|
||||
require.ErrorContains(t, err, "ACL token not found")
|
||||
require.ErrorContains(t, err, structs.ErrPermissionDenied.Error())
|
||||
|
||||
// Try listing roles with a valid ACL token.
|
||||
aclRoleReq2 := &structs.ACLRolesListRequest{
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
|
@ -31,15 +30,19 @@ func NewAllocEndpoint(srv *Server, ctx *RPCContext) *Alloc {
|
|||
|
||||
// List is used to list the allocations in the system
|
||||
func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListResponse) error {
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("Alloc.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "list"}, time.Now())
|
||||
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Check namespace read-job permissions
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -136,34 +139,20 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
|||
// GetAlloc is used to lookup a particular allocation
|
||||
func (a *Alloc) GetAlloc(args *structs.AllocSpecificRequest,
|
||||
reply *structs.SingleAllocResponse) error {
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("Alloc.GetAlloc", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "get_alloc"}, time.Now())
|
||||
|
||||
// Check namespace read-job permissions before performing blocking query.
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := a.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
// If ResolveToken had an unexpected error return that
|
||||
if err != structs.ErrTokenNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to lookup AuthToken as a Node.SecretID since nodes
|
||||
// call this endpoint and don't have an ACL token.
|
||||
node, stateErr := a.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
|
||||
if stateErr != nil {
|
||||
// Return the original ResolveToken error with this err
|
||||
var merr multierror.Error
|
||||
merr.Errors = append(merr.Errors, err, stateErr)
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// Not a node or a valid ACL token
|
||||
if node == nil {
|
||||
return structs.ErrTokenNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup the blocking query
|
||||
|
@ -276,9 +265,15 @@ func (a *Alloc) GetAllocs(args *structs.AllocsGetRequest,
|
|||
|
||||
// Stop is used to stop an allocation and migrate it to another node.
|
||||
func (a *Alloc) Stop(args *structs.AllocStopRequest, reply *structs.AllocStopResponse) error {
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("Alloc.Stop", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "stop"}, time.Now())
|
||||
|
||||
alloc, err := getAlloc(a.srv.State(), args.AllocID)
|
||||
|
@ -288,7 +283,7 @@ func (a *Alloc) Stop(args *structs.AllocStopRequest, reply *structs.AllocStopRes
|
|||
|
||||
// Check for namespace alloc-lifecycle permissions.
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityAllocLifecycle)
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, alloc.Namespace) {
|
||||
|
@ -335,13 +330,18 @@ func (a *Alloc) Stop(args *structs.AllocStopRequest, reply *structs.AllocStopRes
|
|||
// UpdateDesiredTransition is used to update the desired transitions of an
|
||||
// allocation.
|
||||
func (a *Alloc) UpdateDesiredTransition(args *structs.AllocUpdateDesiredTransitionRequest, reply *structs.GenericResponse) error {
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward("Alloc.UpdateDesiredTransition", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "update_desired_transition"}, time.Now())
|
||||
|
||||
// Check that it is a management token.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -370,14 +370,19 @@ func (a *Alloc) GetServiceRegistrations(
|
|||
args *structs.AllocServiceRegistrationsRequest,
|
||||
reply *structs.AllocServiceRegistrationsResponse) error {
|
||||
|
||||
authErr := a.srv.Authenticate(a.ctx, args)
|
||||
if done, err := a.srv.forward(structs.AllocServiceRegistrationsRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "get_service_registrations"}, time.Now())
|
||||
|
||||
// If ACLs are enabled, ensure the caller has the read-job namespace
|
||||
// capability.
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
|
|
|
@ -1279,7 +1279,7 @@ func TestAllocEndpoint_List_AllNamespaces_ACL_OSS(t *testing.T) {
|
|||
Namespace: "*",
|
||||
Token: uuid.Generate(),
|
||||
Error: true,
|
||||
Message: structs.ErrTokenNotFound.Error(),
|
||||
Message: structs.ErrPermissionDenied.Error(),
|
||||
},
|
||||
{
|
||||
Label: "all namespaces with insufficient token",
|
||||
|
@ -1311,7 +1311,7 @@ func TestAllocEndpoint_List_AllNamespaces_ACL_OSS(t *testing.T) {
|
|||
Namespace: ns1.Name,
|
||||
Token: uuid.Generate(),
|
||||
Error: true,
|
||||
Message: structs.ErrTokenNotFound.Error(),
|
||||
Message: structs.ErrPermissionDenied.Error(),
|
||||
},
|
||||
{
|
||||
Label: "bad namespace with root token",
|
||||
|
|
|
@ -35,8 +35,13 @@ func (a *Agent) register() {
|
|||
}
|
||||
|
||||
func (a *Agent) Profile(args *structs.AgentPprofRequest, reply *structs.AgentPprofResponse) error {
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check ACL for agent write
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowAgentWrite() {
|
||||
|
@ -128,9 +133,14 @@ func (a *Agent) monitor(conn io.ReadWriteCloser) {
|
|||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
authErr := a.srv.Authenticate(nil, &args)
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Check agent read permissions
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(&args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
} else if aclObj != nil && !aclObj.AllowAgentRead() {
|
||||
|
@ -398,8 +408,11 @@ func (a *Agent) forwardProfileClient(args *structs.AgentPprofRequest, reply *str
|
|||
|
||||
// Host returns data about the agent's host system for the `debug` command.
|
||||
func (a *Agent) Host(args *structs.HostDataRequest, reply *structs.HostDataResponse) error {
|
||||
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -39,14 +39,19 @@ func (a *ClientAllocations) GarbageCollectAll(args *structs.NodeSpecificRequest,
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.GarbageCollectAll", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "garbage_collect_all"}, time.Now())
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeWrite() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -85,10 +90,15 @@ func (a *ClientAllocations) Signal(args *structs.AllocSignalRequest, reply *stru
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.Signal", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "signal"}, time.Now())
|
||||
|
||||
// Verify the arguments.
|
||||
|
@ -108,7 +118,7 @@ func (a *ClientAllocations) Signal(args *structs.AllocSignalRequest, reply *stru
|
|||
}
|
||||
|
||||
// Check namespace alloc-lifecycle permission.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityAllocLifecycle) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -137,10 +147,15 @@ func (a *ClientAllocations) GarbageCollect(args *structs.AllocSpecificRequest, r
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.GarbageCollect", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "garbage_collect"}, time.Now())
|
||||
|
||||
// Verify the arguments.
|
||||
|
@ -160,7 +175,7 @@ func (a *ClientAllocations) GarbageCollect(args *structs.AllocSpecificRequest, r
|
|||
}
|
||||
|
||||
// Check namespace submit-job permission.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -189,10 +204,15 @@ func (a *ClientAllocations) Restart(args *structs.AllocRestartRequest, reply *st
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.Restart", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "restart"}, time.Now())
|
||||
|
||||
// Find the allocation
|
||||
|
@ -207,7 +227,7 @@ func (a *ClientAllocations) Restart(args *structs.AllocRestartRequest, reply *st
|
|||
}
|
||||
|
||||
// Check for namespace alloc-lifecycle permissions.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityAllocLifecycle) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -236,10 +256,15 @@ func (a *ClientAllocations) Stats(args *cstructs.AllocStatsRequest, reply *cstru
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.Stats", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "stats"}, time.Now())
|
||||
|
||||
// Find the allocation
|
||||
|
@ -254,7 +279,7 @@ func (a *ClientAllocations) Stats(args *cstructs.AllocStatsRequest, reply *cstru
|
|||
}
|
||||
|
||||
// Check for namespace read-job permissions.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -287,10 +312,15 @@ func (a *ClientAllocations) Checks(args *cstructs.AllocChecksRequest, reply *cst
|
|||
// hop in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := a.srv.forward("ClientAllocations.Checks", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_allocations", "checks"}, time.Now())
|
||||
|
||||
// Grab the state snapshot, as we need this to perform lookups for a number
|
||||
|
@ -308,7 +338,7 @@ func (a *ClientAllocations) Checks(args *cstructs.AllocChecksRequest, reply *cst
|
|||
}
|
||||
|
||||
// Check for namespace read-job permissions.
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -344,12 +374,18 @@ func (a *ClientAllocations) exec(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := a.srv.Authenticate(nil, &args)
|
||||
|
||||
// Check if we need to forward to a different region
|
||||
if r := args.RequestRegion(); r != a.srv.Region() {
|
||||
forwardRegionStreamingRpc(a.srv, conn, encoder, &args, "Allocations.Exec",
|
||||
args.AllocID, &args.QueryOptions)
|
||||
return
|
||||
}
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the arguments.
|
||||
if args.AllocID == "" {
|
||||
|
@ -375,7 +411,7 @@ func (a *ClientAllocations) exec(conn io.ReadWriteCloser) {
|
|||
}
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := a.srv.ResolveACL(&args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityAllocExec) {
|
||||
|
|
|
@ -106,10 +106,15 @@ func (f *FileSystem) List(args *cstructs.FsListRequest, reply *cstructs.FsListRe
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := f.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := f.srv.forward("FileSystem.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "file_system", "list"}, time.Now())
|
||||
|
||||
// Verify the arguments.
|
||||
|
@ -130,7 +135,7 @@ func (f *FileSystem) List(args *cstructs.FsListRequest, reply *cstructs.FsListRe
|
|||
|
||||
// Check namespace filesystem read permissions
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadFS)
|
||||
aclObj, err := f.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := f.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, alloc.Namespace) {
|
||||
|
@ -160,10 +165,15 @@ func (f *FileSystem) Stat(args *cstructs.FsStatRequest, reply *cstructs.FsStatRe
|
|||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
|
||||
authErr := f.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := f.srv.forward("FileSystem.Stat", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "file_system", "stat"}, time.Now())
|
||||
|
||||
// Verify the arguments.
|
||||
|
@ -183,7 +193,7 @@ func (f *FileSystem) Stat(args *cstructs.FsStatRequest, reply *cstructs.FsStatRe
|
|||
}
|
||||
|
||||
// Check filesystem read permissions
|
||||
if aclObj, err := f.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := f.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityReadFS) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -221,12 +231,18 @@ func (f *FileSystem) stream(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := f.srv.Authenticate(nil, &args)
|
||||
|
||||
// Check if we need to forward to a different region
|
||||
if r := args.RequestRegion(); r != f.srv.Region() {
|
||||
forwardRegionStreamingRpc(f.srv, conn, encoder, &args, "FileSystem.Stream",
|
||||
args.AllocID, &args.QueryOptions)
|
||||
return
|
||||
}
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the arguments.
|
||||
if args.AllocID == "" {
|
||||
|
@ -252,7 +268,7 @@ func (f *FileSystem) stream(conn io.ReadWriteCloser) {
|
|||
}
|
||||
|
||||
// Check namespace read-fs permissions.
|
||||
if aclObj, err := f.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := f.srv.ResolveACL(&args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(alloc.Namespace, acl.NamespaceCapabilityReadFS) {
|
||||
|
@ -339,12 +355,18 @@ func (f *FileSystem) logs(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := f.srv.Authenticate(nil, &args)
|
||||
|
||||
// Check if we need to forward to a different region
|
||||
if r := args.RequestRegion(); r != f.srv.Region() {
|
||||
forwardRegionStreamingRpc(f.srv, conn, encoder, &args, "FileSystem.Logs",
|
||||
args.AllocID, &args.QueryOptions)
|
||||
return
|
||||
}
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the arguments.
|
||||
if args.AllocID == "" {
|
||||
|
@ -372,7 +394,7 @@ func (f *FileSystem) logs(conn io.ReadWriteCloser) {
|
|||
// Check namespace read-logs *or* read-fs permissions.
|
||||
allowNsOp := acl.NamespaceValidator(
|
||||
acl.NamespaceCapabilityReadFS, acl.NamespaceCapabilityReadLogs)
|
||||
aclObj, err := f.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := f.srv.ResolveACL(&args)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
|
|
|
@ -23,19 +23,24 @@ func NewClientStatsEndpoint(srv *Server) *ClientStats {
|
|||
}
|
||||
|
||||
func (s *ClientStats) Stats(args *nstructs.NodeSpecificRequest, reply *structs.ClientStatsResponse) error {
|
||||
|
||||
// We only allow stale reads since the only potentially stale information is
|
||||
// the Node registration and the cost is fairly high for adding another hope
|
||||
// in the forwarding chain.
|
||||
args.QueryOptions.AllowStale = true
|
||||
authErr := s.srv.Authenticate(nil, args)
|
||||
|
||||
// Potentially forward to a different region.
|
||||
if done, err := s.srv.forward("ClientStats.Stats", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return nstructs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client_stats", "stats"}, time.Now())
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj, err := s.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := s.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return nstructs.ErrPermissionDenied
|
||||
|
|
|
@ -29,54 +29,6 @@ func NewCSIVolumeEndpoint(srv *Server, ctx *RPCContext) *CSIVolume {
|
|||
return &CSIVolume{srv: srv, ctx: ctx, logger: srv.logger.Named("csi_volume")}
|
||||
}
|
||||
|
||||
// QueryACLObj looks up the ACL token in the request and returns the acl.ACL object
|
||||
// - fallback to node secret ids
|
||||
func (s *Server) QueryACLObj(args *structs.QueryOptions, allowNodeAccess bool) (*acl.ACL, error) {
|
||||
// Lookup the token
|
||||
aclObj, err := s.ResolveToken(args.AuthToken)
|
||||
if err != nil {
|
||||
// If ResolveToken had an unexpected error return that
|
||||
if !structs.IsErrTokenNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we don't allow access to this endpoint from Nodes, then return token
|
||||
// not found.
|
||||
if !allowNodeAccess {
|
||||
return nil, structs.ErrTokenNotFound
|
||||
}
|
||||
|
||||
ws := memdb.NewWatchSet()
|
||||
// Attempt to lookup AuthToken as a Node.SecretID since nodes may call
|
||||
// call this endpoint and don't have an ACL token.
|
||||
node, stateErr := s.fsm.State().NodeBySecretID(ws, args.AuthToken)
|
||||
if stateErr != nil {
|
||||
// Return the original ResolveToken error with this err
|
||||
var merr multierror.Error
|
||||
merr.Errors = append(merr.Errors, err, stateErr)
|
||||
return nil, merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// We did not find a Node for this ID, so return Token Not Found.
|
||||
if node == nil {
|
||||
return nil, structs.ErrTokenNotFound
|
||||
}
|
||||
}
|
||||
|
||||
// Return either the users aclObj, or nil if ACLs are disabled.
|
||||
return aclObj, nil
|
||||
}
|
||||
|
||||
// WriteACLObj calls QueryACLObj for a WriteRequest
|
||||
func (s *Server) WriteACLObj(args *structs.WriteRequest, allowNodeAccess bool) (*acl.ACL, error) {
|
||||
opts := &structs.QueryOptions{
|
||||
Region: args.RequestRegion(),
|
||||
Namespace: args.RequestNamespace(),
|
||||
AuthToken: args.AuthToken,
|
||||
}
|
||||
return s.QueryACLObj(opts, allowNodeAccess)
|
||||
}
|
||||
|
||||
const (
|
||||
csiVolumeTable = "csi_volumes"
|
||||
csiPluginTable = "csi_plugins"
|
||||
|
@ -99,15 +51,20 @@ func (s *Server) replySetIndex(table string, reply *structs.QueryMeta) error {
|
|||
|
||||
// List replies with CSIVolumes, filtered by ACL access
|
||||
func (v *CSIVolume) List(args *structs.CSIVolumeListRequest, reply *structs.CSIVolumeListResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIListVolume,
|
||||
acl.NamespaceCapabilityCSIReadVolume,
|
||||
acl.NamespaceCapabilityCSIMountVolume,
|
||||
acl.NamespaceCapabilityListJobs)
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -211,14 +168,19 @@ func (v *CSIVolume) List(args *structs.CSIVolumeListRequest, reply *structs.CSIV
|
|||
|
||||
// Get fetches detailed information about a specific volume
|
||||
func (v *CSIVolume) Get(args *structs.CSIVolumeGetRequest, reply *structs.CSIVolumeGetResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Get", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allowCSIAccess := acl.NamespaceValidator(acl.NamespaceCapabilityCSIReadVolume,
|
||||
acl.NamespaceCapabilityCSIMountVolume,
|
||||
acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, true)
|
||||
aclObj, err := v.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -309,12 +271,17 @@ func (v *CSIVolume) controllerValidateVolume(req *structs.CSIVolumeRegisterReque
|
|||
// again with the right settings. This lets us be as strict with
|
||||
// validation here as the CreateVolume CSI RPC is expected to be.
|
||||
func (v *CSIVolume) Register(args *structs.CSIVolumeRegisterRequest, reply *structs.CSIVolumeRegisterResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Register", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -400,12 +367,17 @@ func (v *CSIVolume) Register(args *structs.CSIVolumeRegisterRequest, reply *stru
|
|||
|
||||
// Deregister removes a set of volumes
|
||||
func (v *CSIVolume) Deregister(args *structs.CSIVolumeDeregisterRequest, reply *structs.CSIVolumeDeregisterResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Deregister", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -437,12 +409,17 @@ func (v *CSIVolume) Deregister(args *structs.CSIVolumeDeregisterRequest, reply *
|
|||
|
||||
// Claim submits a change to a volume claim
|
||||
func (v *CSIVolume) Claim(args *structs.CSIVolumeClaimRequest, reply *structs.CSIVolumeClaimResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Claim", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIMountVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, true)
|
||||
aclObj, err := v.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -619,14 +596,19 @@ func allowCSIMount(aclObj *acl.ACL, namespace string) bool {
|
|||
// ControllerUnpublish RPCs to the client. It handles errors according to the
|
||||
// current claim state.
|
||||
func (v *CSIVolume) Unpublish(args *structs.CSIVolumeUnpublishRequest, reply *structs.CSIVolumeUnpublishResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Unpublish", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "unpublish"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIMountVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, true)
|
||||
aclObj, err := v.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -957,14 +939,17 @@ func (v *CSIVolume) checkpointClaim(vol *structs.CSIVolume, claim *structs.CSIVo
|
|||
|
||||
func (v *CSIVolume) Create(args *structs.CSIVolumeCreateRequest, reply *structs.CSIVolumeCreateResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Create", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "create"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1081,14 +1066,18 @@ func (v *CSIVolume) createVolume(vol *structs.CSIVolume, plugin *structs.CSIPlug
|
|||
}
|
||||
|
||||
func (v *CSIVolume) Delete(args *structs.CSIVolumeDeleteRequest, reply *structs.CSIVolumeDeleteResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.Delete", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "delete"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1161,16 +1150,20 @@ func (v *CSIVolume) deleteVolume(vol *structs.CSIVolume, plugin *structs.CSIPlug
|
|||
|
||||
func (v *CSIVolume) ListExternal(args *structs.CSIVolumeExternalListRequest, reply *structs.CSIVolumeExternalListResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.ListExternal", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "list_external"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIListVolume,
|
||||
acl.NamespaceCapabilityCSIReadVolume,
|
||||
acl.NamespaceCapabilityCSIMountVolume,
|
||||
acl.NamespaceCapabilityListJobs)
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1221,13 +1214,17 @@ func (v *CSIVolume) ListExternal(args *structs.CSIVolumeExternalListRequest, rep
|
|||
|
||||
func (v *CSIVolume) CreateSnapshot(args *structs.CSISnapshotCreateRequest, reply *structs.CSISnapshotCreateResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.CreateSnapshot", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "create_snapshot"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1312,13 +1309,17 @@ func (v *CSIVolume) CreateSnapshot(args *structs.CSISnapshotCreateRequest, reply
|
|||
|
||||
func (v *CSIVolume) DeleteSnapshot(args *structs.CSISnapshotDeleteRequest, reply *structs.CSISnapshotDeleteResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.DeleteSnapshot", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "delete_snapshot"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIWriteVolume)
|
||||
aclObj, err := v.srv.WriteACLObj(&args.WriteRequest, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1372,16 +1373,20 @@ func (v *CSIVolume) DeleteSnapshot(args *structs.CSISnapshotDeleteRequest, reply
|
|||
|
||||
func (v *CSIVolume) ListSnapshots(args *structs.CSISnapshotListRequest, reply *structs.CSISnapshotListResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIVolume.ListSnapshots", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "volume", "list_snapshots"}, time.Now())
|
||||
|
||||
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIListVolume,
|
||||
acl.NamespaceCapabilityCSIReadVolume,
|
||||
acl.NamespaceCapabilityCSIMountVolume,
|
||||
acl.NamespaceCapabilityListJobs)
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1444,21 +1449,24 @@ func NewCSIPluginEndpoint(srv *Server, ctx *RPCContext) *CSIPlugin {
|
|||
|
||||
// List replies with CSIPlugins, filtered by ACL access
|
||||
func (v *CSIPlugin) List(args *structs.CSIPluginListRequest, reply *structs.CSIPluginListResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIPlugin.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "list"}, time.Now())
|
||||
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !aclObj.AllowPluginList() {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "list"}, time.Now())
|
||||
|
||||
opts := blockingOptions{
|
||||
queryOpts: &args.QueryOptions,
|
||||
queryMeta: &reply.QueryMeta,
|
||||
|
@ -1499,15 +1507,20 @@ func (v *CSIPlugin) List(args *structs.CSIPluginListRequest, reply *structs.CSIP
|
|||
|
||||
// Get fetches detailed information about a specific plugin
|
||||
func (v *CSIPlugin) Get(args *structs.CSIPluginGetRequest, reply *structs.CSIPluginGetResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIPlugin.Get", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "get"}, time.Now())
|
||||
|
||||
aclObj, err := v.srv.QueryACLObj(&args.QueryOptions, false)
|
||||
aclObj, err := v.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !aclObj.AllowPluginRead() {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
@ -1515,8 +1528,6 @@ func (v *CSIPlugin) Get(args *structs.CSIPluginGetRequest, reply *structs.CSIPlu
|
|||
withAllocs := aclObj == nil ||
|
||||
aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob)
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "get"}, time.Now())
|
||||
|
||||
if args.ID == "" {
|
||||
return fmt.Errorf("missing plugin ID")
|
||||
}
|
||||
|
@ -1564,19 +1575,23 @@ func (v *CSIPlugin) Get(args *structs.CSIPluginGetRequest, reply *structs.CSIPlu
|
|||
|
||||
// Delete deletes a plugin if it is unused
|
||||
func (v *CSIPlugin) Delete(args *structs.CSIPluginDeleteRequest, reply *structs.CSIPluginDeleteResponse) error {
|
||||
|
||||
authErr := v.srv.Authenticate(v.ctx, args)
|
||||
if done, err := v.srv.forward("CSIPlugin.Delete", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "delete"}, time.Now())
|
||||
|
||||
// Check that it is a management token.
|
||||
if aclObj, err := v.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := v.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "plugin", "delete"}, time.Now())
|
||||
|
||||
if args.ID == "" {
|
||||
return fmt.Errorf("missing plugin ID")
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func (d *Deployment) GetDeployment(args *structs.DeploymentSpecificRequest,
|
|||
|
||||
// Check namespace read-job permissions
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := d.srv.ResolveACL(args.GetIdentity().GetACLToken())
|
||||
aclObj, err := d.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, args.RequestNamespace()) {
|
||||
|
@ -93,9 +93,14 @@ func (d *Deployment) GetDeployment(args *structs.DeploymentSpecificRequest,
|
|||
|
||||
// Fail is used to force fail a deployment
|
||||
func (d *Deployment) Fail(args *structs.DeploymentFailRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Fail", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "fail"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -119,7 +124,7 @@ func (d *Deployment) Fail(args *structs.DeploymentFailRequest, reply *structs.De
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -135,9 +140,13 @@ func (d *Deployment) Fail(args *structs.DeploymentFailRequest, reply *structs.De
|
|||
|
||||
// Pause is used to pause a deployment
|
||||
func (d *Deployment) Pause(args *structs.DeploymentPauseRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Pause", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "pause"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -161,7 +170,7 @@ func (d *Deployment) Pause(args *structs.DeploymentPauseRequest, reply *structs.
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -181,9 +190,13 @@ func (d *Deployment) Pause(args *structs.DeploymentPauseRequest, reply *structs.
|
|||
|
||||
// Promote is used to promote canaries in a deployment
|
||||
func (d *Deployment) Promote(args *structs.DeploymentPromoteRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Promote", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "promote"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -207,7 +220,7 @@ func (d *Deployment) Promote(args *structs.DeploymentPromoteRequest, reply *stru
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -223,9 +236,13 @@ func (d *Deployment) Promote(args *structs.DeploymentPromoteRequest, reply *stru
|
|||
|
||||
// Run is used to start a pending deployment
|
||||
func (d *Deployment) Run(args *structs.DeploymentRunRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Run", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "run"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -249,7 +266,7 @@ func (d *Deployment) Run(args *structs.DeploymentRunRequest, reply *structs.Depl
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -265,9 +282,13 @@ func (d *Deployment) Run(args *structs.DeploymentRunRequest, reply *structs.Depl
|
|||
|
||||
// Unblock is used to unblock a deployment
|
||||
func (d *Deployment) Unblock(args *structs.DeploymentUnblockRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Unblock", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "unblock"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -291,7 +312,7 @@ func (d *Deployment) Unblock(args *structs.DeploymentUnblockRequest, reply *stru
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -307,9 +328,13 @@ func (d *Deployment) Unblock(args *structs.DeploymentUnblockRequest, reply *stru
|
|||
|
||||
// Cancel is used to cancel a deployment
|
||||
func (d *Deployment) Cancel(args *structs.DeploymentCancelRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Cancel", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "cancel"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -333,7 +358,7 @@ func (d *Deployment) Cancel(args *structs.DeploymentCancelRequest, reply *struct
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -350,9 +375,13 @@ func (d *Deployment) Cancel(args *structs.DeploymentCancelRequest, reply *struct
|
|||
// SetAllocHealth is used to set the health of allocations that are part of the
|
||||
// deployment.
|
||||
func (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest, reply *structs.DeploymentUpdateResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.SetAllocHealth", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "set_alloc_health"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -380,7 +409,7 @@ func (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest,
|
|||
}
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(deploy.Namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -396,16 +425,20 @@ func (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest,
|
|||
|
||||
// List returns the list of deployments in the system
|
||||
func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.DeploymentListResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "list"}, time.Now())
|
||||
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Check namespace read-job permissions against request namespace since
|
||||
// results are filtered by request namespace.
|
||||
if aclObj, err := d.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := d.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -483,16 +516,20 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De
|
|||
|
||||
// Allocations returns the list of allocations that are a part of the deployment
|
||||
func (d *Deployment) Allocations(args *structs.DeploymentSpecificRequest, reply *structs.AllocListResponse) error {
|
||||
authErr := d.srv.Authenticate(d.ctx, args)
|
||||
if done, err := d.srv.forward("Deployment.Allocations", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "deployment", "allocations"}, time.Now())
|
||||
|
||||
// Check namespace read-job permissions against the request namespace.
|
||||
// Must re-check against the alloc namespace when they return to ensure
|
||||
// there's no namespace mismatch.
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := d.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := d.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, args.RequestNamespace()) {
|
||||
|
|
|
@ -41,14 +41,19 @@ func NewEvalEndpoint(srv *Server, ctx *RPCContext) *Eval {
|
|||
// GetEval is used to request information about a specific evaluation
|
||||
func (e *Eval) GetEval(args *structs.EvalSpecificRequest,
|
||||
reply *structs.SingleEvalResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if done, err := e.srv.forward("Eval.GetEval", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "get_eval"}, time.Now())
|
||||
|
||||
// Check for read-job permissions before performing blocking query.
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := e.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := e.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, args.RequestNamespace()) {
|
||||
|
@ -112,9 +117,6 @@ func (e *Eval) Dequeue(args *structs.EvalDequeueRequest,
|
|||
reply *structs.EvalDequeueResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
}
|
||||
|
||||
// Ensure the connection was initiated by another server if TLS is used.
|
||||
err := validateTLSCertificateLevel(e.srv, e.ctx, tlsCertificateLevelServer)
|
||||
|
@ -125,6 +127,10 @@ func (e *Eval) Dequeue(args *structs.EvalDequeueRequest,
|
|||
if done, err := e.srv.forward("Eval.Dequeue", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "dequeue"}, time.Now())
|
||||
|
||||
// Ensure there is at least one scheduler
|
||||
|
@ -443,14 +449,18 @@ func (e *Eval) Delete(
|
|||
args *structs.EvalDeleteRequest,
|
||||
reply *structs.EvalDeleteResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if done, err := e.srv.forward(structs.EvalDeleteRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "delete"}, time.Now())
|
||||
|
||||
// This RPC endpoint is very destructive and alters Nomad's core state,
|
||||
// meaning only those with management tokens can call it.
|
||||
if aclObj, err := e.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := e.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -629,15 +639,20 @@ func (e *Eval) deleteEvalsByFilter(args *structs.EvalDeleteRequest) (int, uint64
|
|||
|
||||
// List is used to get a list of the evaluations in the system
|
||||
func (e *Eval) List(args *structs.EvalListRequest, reply *structs.EvalListResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if done, err := e.srv.forward("Eval.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "list"}, time.Now())
|
||||
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Check for read-job permissions
|
||||
aclObj, err := e.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := e.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -748,14 +763,19 @@ func (e *Eval) List(args *structs.EvalListRequest, reply *structs.EvalListRespon
|
|||
|
||||
// Count is used to get a list of the evaluations in the system
|
||||
func (e *Eval) Count(args *structs.EvalCountRequest, reply *structs.EvalCountResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if done, err := e.srv.forward("Eval.Count", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "count"}, time.Now())
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Check for read-job permissions
|
||||
aclObj, err := e.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := e.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -847,14 +867,19 @@ func (e *Eval) Count(args *structs.EvalCountRequest, reply *structs.EvalCountRes
|
|||
// Allocations is used to list the allocations for an evaluation
|
||||
func (e *Eval) Allocations(args *structs.EvalSpecificRequest,
|
||||
reply *structs.EvalAllocationsResponse) error {
|
||||
|
||||
authErr := e.srv.Authenticate(e.ctx, args)
|
||||
if done, err := e.srv.forward("Eval.Allocations", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "eval", "allocations"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
allowNsOp := acl.NamespaceValidator(acl.NamespaceCapabilityReadJob)
|
||||
aclObj, err := e.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := e.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !allowNsOp(aclObj, args.RequestNamespace()) {
|
||||
|
|
|
@ -37,6 +37,8 @@ func (e *Event) stream(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := e.srv.Authenticate(nil, &args)
|
||||
|
||||
// forward to appropriate region
|
||||
if args.Region != e.srv.config.Region {
|
||||
err := e.forwardStreamingRPC(args.Region, "Event.Stream", args, conn)
|
||||
|
@ -46,6 +48,10 @@ func (e *Event) stream(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
handleJsonResultError(structs.ErrPermissionDenied, pointer.Of(int64(403)), encoder)
|
||||
}
|
||||
|
||||
// Generate the subscription request
|
||||
subReq := &stream.SubscribeRequest{
|
||||
Token: args.AuthToken,
|
||||
|
|
|
@ -84,9 +84,13 @@ func NewJobEndpoints(s *Server, ctx *RPCContext) *Job {
|
|||
|
||||
// Register is used to upsert a job for scheduling
|
||||
func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Register", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "register"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -120,7 +124,7 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
|
|||
reply.Warnings = structs.MergeMultierrorWarnings(warnings...)
|
||||
|
||||
// Check job submission permissions
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
|
@ -483,13 +487,17 @@ func getSignalConstraint(signals []string) *structs.Constraint {
|
|||
|
||||
// Summary retrieves the summary of a job.
|
||||
func (j *Job) Summary(args *structs.JobSummaryRequest, reply *structs.JobSummaryResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Summary", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job_summary", "get_job_summary"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -531,9 +539,13 @@ func (j *Job) Summary(args *structs.JobSummaryRequest, reply *structs.JobSummary
|
|||
// Must forward to the leader, because only the leader will have a live Vault
|
||||
// client with which to validate vault tokens.
|
||||
func (j *Job) Validate(args *structs.JobValidateRequest, reply *structs.JobValidateResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Validate", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "validate"}, time.Now())
|
||||
|
||||
// defensive check; http layer and RPC requester should ensure namespaces are set consistently
|
||||
|
@ -548,7 +560,7 @@ func (j *Job) Validate(args *structs.JobValidateRequest, reply *structs.JobValid
|
|||
args.Job = job
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -578,13 +590,17 @@ func (j *Job) Validate(args *structs.JobValidateRequest, reply *structs.JobValid
|
|||
|
||||
// Revert is used to revert the job to a prior version
|
||||
func (j *Job) Revert(args *structs.JobRevertRequest, reply *structs.JobRegisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Revert", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "revert"}, time.Now())
|
||||
|
||||
// Check for submit-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -646,13 +662,17 @@ func (j *Job) Revert(args *structs.JobRevertRequest, reply *structs.JobRegisterR
|
|||
|
||||
// Stable is used to mark the job version as stable
|
||||
func (j *Job) Stable(args *structs.JobStabilityRequest, reply *structs.JobStabilityResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Stable", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "stable"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -692,13 +712,17 @@ func (j *Job) Stable(args *structs.JobStabilityRequest, reply *structs.JobStabil
|
|||
|
||||
// Evaluate is used to force a job for re-evaluation
|
||||
func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Evaluate", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "evaluate"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -788,13 +812,17 @@ func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegis
|
|||
|
||||
// Deregister is used to remove a job the cluster.
|
||||
func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobDeregisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Deregister", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "deregister"}, time.Now())
|
||||
|
||||
// Check for submit-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -901,13 +929,17 @@ func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobD
|
|||
|
||||
// BatchDeregister is used to remove a set of jobs from the cluster.
|
||||
func (j *Job) BatchDeregister(args *structs.JobBatchDeregisterRequest, reply *structs.JobBatchDeregisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.BatchDeregister", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "batch_deregister"}, time.Now())
|
||||
|
||||
// Resolve the ACL token
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -986,15 +1018,19 @@ func (j *Job) BatchDeregister(args *structs.JobBatchDeregisterRequest, reply *st
|
|||
|
||||
// Scale is used to modify one of the scaling targets in the job
|
||||
func (j *Job) Scale(args *structs.JobScaleRequest, reply *structs.JobRegisterResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Scale", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "scale"}, time.Now())
|
||||
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Authorize request
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1168,13 +1204,17 @@ func (j *Job) Scale(args *structs.JobScaleRequest, reply *structs.JobRegisterRes
|
|||
// GetJob is used to request information about a specific job
|
||||
func (j *Job) GetJob(args *structs.JobSpecificRequest,
|
||||
reply *structs.SingleJobResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.GetJob", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "get_job"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1214,13 +1254,17 @@ func (j *Job) GetJob(args *structs.JobSpecificRequest,
|
|||
// GetJobVersions is used to retrieve all tracked versions of a job.
|
||||
func (j *Job) GetJobVersions(args *structs.JobVersionsRequest,
|
||||
reply *structs.JobVersionsResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.GetJobVersions", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "get_job_versions"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1316,15 +1360,19 @@ func registrationsAreAllowed(aclObj *acl.ACL, state *state.StateStore) (bool, er
|
|||
|
||||
// List is used to list the jobs registered in the system
|
||||
func (j *Job) List(args *structs.JobListRequest, reply *structs.JobListResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "list"}, time.Now())
|
||||
|
||||
namespace := args.RequestNamespace()
|
||||
|
||||
// Check for list-job permissions
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1422,13 +1470,17 @@ func (j *Job) List(args *structs.JobListRequest, reply *structs.JobListResponse)
|
|||
// Allocations is used to list the allocations for a job
|
||||
func (j *Job) Allocations(args *structs.JobSpecificRequest,
|
||||
reply *structs.JobAllocationsResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Allocations", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "allocations"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1477,13 +1529,17 @@ func (j *Job) Allocations(args *structs.JobSpecificRequest,
|
|||
// Evaluations is used to list the evaluations for a job
|
||||
func (j *Job) Evaluations(args *structs.JobSpecificRequest,
|
||||
reply *structs.JobEvaluationsResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Evaluations", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "evaluations"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1519,13 +1575,17 @@ func (j *Job) Evaluations(args *structs.JobSpecificRequest,
|
|||
// Deployments is used to list the deployments for a job
|
||||
func (j *Job) Deployments(args *structs.JobSpecificRequest,
|
||||
reply *structs.DeploymentListResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Deployments", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "deployments"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1561,13 +1621,17 @@ func (j *Job) Deployments(args *structs.JobSpecificRequest,
|
|||
// LatestDeployment is used to retrieve the latest deployment for a job
|
||||
func (j *Job) LatestDeployment(args *structs.JobSpecificRequest,
|
||||
reply *structs.SingleDeploymentResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.LatestDeployment", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "latest_deployment"}, time.Now())
|
||||
|
||||
// Check for read-job permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -1608,9 +1672,13 @@ func (j *Job) LatestDeployment(args *structs.JobSpecificRequest,
|
|||
// Plan is used to cause a dry-run evaluation of the Job and return the results
|
||||
// with a potential diff containing annotations.
|
||||
func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Plan", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "plan"}, time.Now())
|
||||
|
||||
// Validate the arguments
|
||||
|
@ -1629,7 +1697,7 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
|
|||
reply.Warnings = structs.MergeMultierrorWarnings(warnings...)
|
||||
|
||||
// Check job submission permissions, which we assume is the same for plan
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
if !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
|
||||
|
@ -1823,13 +1891,17 @@ func validateJobUpdate(old, new *structs.Job) error {
|
|||
|
||||
// Dispatch a parameterized job.
|
||||
func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispatchResponse) error {
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.Dispatch", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "dispatch"}, time.Now())
|
||||
|
||||
// Check for submit-job permissions
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityDispatchJob) {
|
||||
|
@ -2058,13 +2130,17 @@ func validateDispatchRequest(req *structs.JobDispatchRequest, job *structs.Job)
|
|||
func (j *Job) ScaleStatus(args *structs.JobScaleStatusRequest,
|
||||
reply *structs.JobScaleStatusResponse) error {
|
||||
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward("Job.ScaleStatus", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "scale_status"}, time.Now())
|
||||
|
||||
// Check for autoscaler permissions
|
||||
if aclObj, err := j.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := j.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
hasReadJob := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob)
|
||||
|
@ -2173,14 +2249,18 @@ func (j *Job) GetServiceRegistrations(
|
|||
args *structs.JobServiceRegistrationsRequest,
|
||||
reply *structs.JobServiceRegistrationsResponse) error {
|
||||
|
||||
authErr := j.srv.Authenticate(j.ctx, args)
|
||||
if done, err := j.srv.forward(structs.JobServiceRegistrationsRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "job", "get_service_registrations"}, time.Now())
|
||||
|
||||
// If ACLs are enabled, ensure the caller has the read-job namespace
|
||||
// capability.
|
||||
aclObj, err := j.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := j.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
|
|
|
@ -27,13 +27,17 @@ func NewKeyringEndpoint(srv *Server, ctx *RPCContext, enc *Encrypter) *Keyring {
|
|||
}
|
||||
|
||||
func (k *Keyring) Rotate(args *structs.KeyringRotateRootKeyRequest, reply *structs.KeyringRotateRootKeyResponse) error {
|
||||
|
||||
authErr := k.srv.Authenticate(k.ctx, args)
|
||||
if done, err := k.srv.forward("Keyring.Rotate", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "keyring", "rotate"}, time.Now())
|
||||
|
||||
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := k.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -95,9 +99,14 @@ func (k *Keyring) Rotate(args *structs.KeyringRotateRootKeyRequest, reply *struc
|
|||
}
|
||||
|
||||
func (k *Keyring) List(args *structs.KeyringListRootKeyMetaRequest, reply *structs.KeyringListRootKeyMetaResponse) error {
|
||||
|
||||
authErr := k.srv.Authenticate(k.ctx, args)
|
||||
if done, err := k.srv.forward("Keyring.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "keyring", "list"}, time.Now())
|
||||
|
||||
|
@ -106,7 +115,7 @@ func (k *Keyring) List(args *structs.KeyringListRootKeyMetaRequest, reply *struc
|
|||
// replication
|
||||
err := validateTLSCertificateLevel(k.srv, k.ctx, tlsCertificateLevelServer)
|
||||
if err != nil {
|
||||
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := k.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -148,13 +157,18 @@ func (k *Keyring) List(args *structs.KeyringListRootKeyMetaRequest, reply *struc
|
|||
// Update updates an existing key in the keyring, including both the
|
||||
// key material and metadata.
|
||||
func (k *Keyring) Update(args *structs.KeyringUpdateRootKeyRequest, reply *structs.KeyringUpdateRootKeyResponse) error {
|
||||
|
||||
authErr := k.srv.Authenticate(k.ctx, args)
|
||||
if done, err := k.srv.forward("Keyring.Update", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "keyring", "update"}, time.Now())
|
||||
|
||||
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := k.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -290,13 +304,18 @@ func (k *Keyring) Get(args *structs.KeyringGetRootKeyRequest, reply *structs.Key
|
|||
}
|
||||
|
||||
func (k *Keyring) Delete(args *structs.KeyringDeleteRootKeyRequest, reply *structs.KeyringDeleteRootKeyResponse) error {
|
||||
|
||||
authErr := k.srv.Authenticate(k.ctx, args)
|
||||
if done, err := k.srv.forward("Keyring.Delete", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "keyring", "delete"}, time.Now())
|
||||
|
||||
if aclObj, err := k.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := k.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -32,13 +32,13 @@ func (n *Namespace) UpsertNamespaces(args *structs.NamespaceUpsertRequest,
|
|||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "namespace", "upsert_namespaces"}, time.Now())
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := n.srv.ResolveACL(args.GetIdentity().GetACLToken()); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -76,14 +76,19 @@ func (n *Namespace) UpsertNamespaces(args *structs.NamespaceUpsertRequest,
|
|||
|
||||
// DeleteNamespaces is used to delete a namespace
|
||||
func (n *Namespace) DeleteNamespaces(args *structs.NamespaceDeleteRequest, reply *structs.GenericResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
args.Region = n.srv.config.AuthoritativeRegion
|
||||
if done, err := n.srv.forward("Namespace.DeleteNamespaces", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "namespace", "delete_namespaces"}, time.Now())
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -225,13 +230,18 @@ func (n *Namespace) namespaceTerminalInRegion(authToken, namespace, region strin
|
|||
|
||||
// ListNamespaces is used to list the namespaces
|
||||
func (n *Namespace) ListNamespaces(args *structs.NamespaceListRequest, reply *structs.NamespaceListResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Namespace.ListNamespaces", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "namespace", "list_namespace"}, time.Now())
|
||||
|
||||
// Resolve token to acl to filter namespace list
|
||||
aclObj, err := n.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := n.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -286,13 +296,18 @@ func (n *Namespace) ListNamespaces(args *structs.NamespaceListRequest, reply *st
|
|||
|
||||
// GetNamespace is used to get a specific namespace
|
||||
func (n *Namespace) GetNamespace(args *structs.NamespaceSpecificRequest, reply *structs.SingleNamespaceResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Namespace.GetNamespace", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "namespace", "get_namespace"}, time.Now())
|
||||
|
||||
// Check capabilities for the given namespace permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNamespace(args.Name) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -334,13 +349,18 @@ func (n *Namespace) GetNamespace(args *structs.NamespaceSpecificRequest, reply *
|
|||
|
||||
// GetNamespaces is used to get a set of namespaces
|
||||
func (n *Namespace) GetNamespaces(args *structs.NamespaceSetRequest, reply *structs.NamespaceSetResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Namespace.GetNamespaces", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "namespace", "get_namespaces"}, time.Now())
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -319,9 +319,13 @@ func (n *Node) constructNodeServerInfoResponse(nodeID string, snap *state.StateS
|
|||
// Deregister is used to remove a client from the cluster. If a client should
|
||||
// just be made unavailable for scheduling, a status update is preferred.
|
||||
func (n *Node) Deregister(args *structs.NodeDeregisterRequest, reply *structs.NodeUpdateResponse) error {
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.Deregister", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "deregister"}, time.Now())
|
||||
|
||||
if args.NodeID == "" {
|
||||
|
@ -341,9 +345,13 @@ func (n *Node) Deregister(args *structs.NodeDeregisterRequest, reply *structs.No
|
|||
|
||||
// BatchDeregister is used to remove client nodes from the cluster.
|
||||
func (n *Node) BatchDeregister(args *structs.NodeBatchDeregisterRequest, reply *structs.NodeUpdateResponse) error {
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.BatchDeregister", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "batch_deregister"}, time.Now())
|
||||
|
||||
if len(args.NodeIDs) == 0 {
|
||||
|
@ -361,7 +369,7 @@ func (n *Node) deregister(args *structs.NodeBatchDeregisterRequest,
|
|||
raftApplyFn func() (interface{}, uint64, error),
|
||||
) error {
|
||||
// Check request permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeWrite() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -614,13 +622,18 @@ func nodeStatusTransitionRequiresEval(newStatus, oldStatus string) bool {
|
|||
// UpdateDrain is used to update the drain mode of a client node
|
||||
func (n *Node) UpdateDrain(args *structs.NodeUpdateDrainRequest,
|
||||
reply *structs.NodeDrainUpdateResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.UpdateDrain", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "update_drain"}, time.Now())
|
||||
|
||||
// Check node write permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeWrite() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -708,13 +721,18 @@ func (n *Node) UpdateDrain(args *structs.NodeUpdateDrainRequest,
|
|||
// UpdateEligibility is used to update the scheduling eligibility of a node
|
||||
func (n *Node) UpdateEligibility(args *structs.NodeUpdateEligibilityRequest,
|
||||
reply *structs.NodeEligibilityUpdateResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.UpdateEligibility", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "update_eligibility"}, time.Now())
|
||||
|
||||
// Check node write permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeWrite() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -805,13 +823,18 @@ func (n *Node) UpdateEligibility(args *structs.NodeUpdateEligibilityRequest,
|
|||
|
||||
// Evaluate is used to force a re-evaluation of the node
|
||||
func (n *Node) Evaluate(args *structs.NodeEvaluateRequest, reply *structs.NodeUpdateResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.Evaluate", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "evaluate"}, time.Now())
|
||||
|
||||
// Check node write permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeWrite() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -860,33 +883,22 @@ func (n *Node) Evaluate(args *structs.NodeEvaluateRequest, reply *structs.NodeUp
|
|||
// GetNode is used to request information about a specific node
|
||||
func (n *Node) GetNode(args *structs.NodeSpecificRequest,
|
||||
reply *structs.SingleNodeResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.GetNode", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "get_node"}, time.Now())
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
// If ResolveToken had an unexpected error return that
|
||||
if err != structs.ErrTokenNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to lookup AuthToken as a Node.SecretID since nodes
|
||||
// call this endpoint and don't have an ACL token.
|
||||
node, stateErr := n.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
|
||||
if stateErr != nil {
|
||||
// Return the original ResolveToken error with this err
|
||||
var merr multierror.Error
|
||||
merr.Errors = append(merr.Errors, err, stateErr)
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// Not a node or a valid ACL token
|
||||
if node == nil {
|
||||
return structs.ErrTokenNotFound
|
||||
}
|
||||
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
aclObj, err := n.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -931,13 +943,18 @@ func (n *Node) GetNode(args *structs.NodeSpecificRequest,
|
|||
// GetAllocs is used to request allocations for a specific node
|
||||
func (n *Node) GetAllocs(args *structs.NodeSpecificRequest,
|
||||
reply *structs.NodeAllocsResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.GetAllocs", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "get_allocs"}, time.Now())
|
||||
|
||||
// Check node read and namespace job read permissions
|
||||
aclObj, err := n.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := n.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1431,13 +1448,18 @@ func (n *Node) batchUpdate(future *structs.BatchFuture, updates []*structs.Alloc
|
|||
// List is used to list the available nodes
|
||||
func (n *Node) List(args *structs.NodeListRequest,
|
||||
reply *structs.NodeListResponse) error {
|
||||
|
||||
authErr := n.srv.Authenticate(n.ctx, args)
|
||||
if done, err := n.srv.forward("Node.List", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "client", "list"}, time.Now())
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := n.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -35,12 +35,17 @@ func (op *Operator) register() {
|
|||
|
||||
// RaftGetConfiguration is used to retrieve the current Raft configuration.
|
||||
func (op *Operator) RaftGetConfiguration(args *structs.GenericRequest, reply *structs.RaftConfigurationResponse) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.RaftGetConfiguration", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := op.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := op.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -96,12 +101,17 @@ func (op *Operator) RaftGetConfiguration(args *structs.GenericRequest, reply *st
|
|||
// "IP:port". The reply argument is not used, but it required to fulfill the RPC
|
||||
// interface.
|
||||
func (op *Operator) RaftRemovePeerByAddress(args *structs.RaftPeerByAddressRequest, reply *struct{}) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.RaftRemovePeerByAddress", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := op.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := op.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -148,12 +158,17 @@ REMOVE:
|
|||
// "IP:port". The reply argument is not used, but is required to fulfill the RPC
|
||||
// interface.
|
||||
func (op *Operator) RaftRemovePeerByID(args *structs.RaftPeerByIDRequest, reply *struct{}) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.RaftRemovePeerByID", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check management permissions
|
||||
if aclObj, err := op.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := op.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -209,12 +224,17 @@ REMOVE:
|
|||
|
||||
// AutopilotGetConfiguration is used to retrieve the current Autopilot configuration.
|
||||
func (op *Operator) AutopilotGetConfiguration(args *structs.GenericRequest, reply *structs.AutopilotConfig) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.AutopilotGetConfiguration", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// This action requires operator read access.
|
||||
rule, err := op.srv.ResolveToken(args.AuthToken)
|
||||
rule, err := op.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -238,12 +258,17 @@ func (op *Operator) AutopilotGetConfiguration(args *structs.GenericRequest, repl
|
|||
|
||||
// AutopilotSetConfiguration is used to set the current Autopilot configuration.
|
||||
func (op *Operator) AutopilotSetConfiguration(args *structs.AutopilotSetConfigRequest, reply *bool) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.AutopilotSetConfiguration", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// This action requires operator write access.
|
||||
rule, err := op.srv.ResolveToken(args.AuthToken)
|
||||
rule, err := op.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -275,15 +300,20 @@ func (op *Operator) AutopilotSetConfiguration(args *structs.AutopilotSetConfigRe
|
|||
|
||||
// ServerHealth is used to get the current health of the servers.
|
||||
func (op *Operator) ServerHealth(args *structs.GenericRequest, reply *structs.OperatorHealthReply) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
// This must be sent to the leader, so we fix the args since we are
|
||||
// re-using a structure where we don't support all the options.
|
||||
args.AllowStale = false
|
||||
if done, err := op.srv.forward("Operator.ServerHealth", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// This action requires operator read access.
|
||||
rule, err := op.srv.ResolveToken(args.AuthToken)
|
||||
rule, err := op.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -307,12 +337,17 @@ func (op *Operator) ServerHealth(args *structs.GenericRequest, reply *structs.Op
|
|||
|
||||
// SchedulerSetConfiguration is used to set the current Scheduler configuration.
|
||||
func (op *Operator) SchedulerSetConfiguration(args *structs.SchedulerSetConfigRequest, reply *structs.SchedulerSetConfigurationResponse) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.SchedulerSetConfiguration", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// This action requires operator write access.
|
||||
rule, err := op.srv.ResolveToken(args.AuthToken)
|
||||
rule, err := op.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if rule != nil && !rule.AllowOperatorWrite() {
|
||||
|
@ -356,12 +391,17 @@ func (op *Operator) SchedulerSetConfiguration(args *structs.SchedulerSetConfigRe
|
|||
|
||||
// SchedulerGetConfiguration is used to retrieve the current Scheduler configuration.
|
||||
func (op *Operator) SchedulerGetConfiguration(args *structs.GenericRequest, reply *structs.SchedulerConfigurationResponse) error {
|
||||
|
||||
authErr := op.srv.Authenticate(op.ctx, args)
|
||||
if done, err := op.srv.forward("Operator.SchedulerGetConfiguration", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// This action requires operator read access.
|
||||
rule, err := op.srv.ResolveToken(args.AuthToken)
|
||||
rule, err := op.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if rule != nil && !rule.AllowOperatorRead() {
|
||||
|
@ -429,6 +469,8 @@ func (op *Operator) snapshotSave(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := op.srv.Authenticate(nil, &args)
|
||||
|
||||
// Forward to appropriate region
|
||||
if args.Region != op.srv.Region() {
|
||||
err := op.forwardStreamingRPC(args.Region, "Operator.SnapshotSave", args, conn)
|
||||
|
@ -455,8 +497,12 @@ func (op *Operator) snapshotSave(conn io.ReadWriteCloser) {
|
|||
}
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
handleFailure(403, structs.ErrPermissionDenied)
|
||||
}
|
||||
|
||||
// Check agent permissions
|
||||
if aclObj, err := op.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := op.srv.ResolveACL(&args); err != nil {
|
||||
code := 500
|
||||
if err == structs.ErrTokenNotFound {
|
||||
code = 400
|
||||
|
@ -511,6 +557,8 @@ func (op *Operator) snapshotRestore(conn io.ReadWriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
authErr := op.srv.Authenticate(nil, &args)
|
||||
|
||||
// Forward to appropriate region
|
||||
if args.Region != op.srv.Region() {
|
||||
err := op.forwardStreamingRPC(args.Region, "Operator.SnapshotRestore", args, conn)
|
||||
|
@ -535,8 +583,12 @@ func (op *Operator) snapshotRestore(conn io.ReadWriteCloser) {
|
|||
|
||||
}
|
||||
|
||||
if authErr != nil {
|
||||
handleFailure(403, structs.ErrPermissionDenied)
|
||||
}
|
||||
|
||||
// Check agent permissions
|
||||
if aclObj, err := op.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := op.srv.ResolveACL(&args); err != nil {
|
||||
code := 500
|
||||
if err == structs.ErrTokenNotFound {
|
||||
code = 400
|
||||
|
|
|
@ -664,7 +664,7 @@ func TestOperator_SnapshotSave_ACL(t *testing.T) {
|
|||
}{
|
||||
{"root", root.SecretID, 0, nil},
|
||||
{"no_permission_token", deniedToken.SecretID, 403, structs.ErrPermissionDenied},
|
||||
{"invalid token", uuid.Generate(), 400, structs.ErrTokenNotFound},
|
||||
{"invalid token", uuid.Generate(), 403, structs.ErrPermissionDenied},
|
||||
{"unauthenticated", "", 403, structs.ErrPermissionDenied},
|
||||
}
|
||||
|
||||
|
@ -886,7 +886,7 @@ func TestOperator_SnapshotRestore_ACL(t *testing.T) {
|
|||
}{
|
||||
{"root", 0, nil},
|
||||
{"no_permission_token", 403, structs.ErrPermissionDenied},
|
||||
{"invalid token", 400, structs.ErrTokenNotFound},
|
||||
{"invalid token", 403, structs.ErrPermissionDenied},
|
||||
{"unauthenticated", 403, structs.ErrPermissionDenied},
|
||||
}
|
||||
|
||||
|
|
|
@ -25,13 +25,18 @@ func NewPeriodicEndpoint(srv *Server, ctx *RPCContext) *Periodic {
|
|||
|
||||
// Force is used to force a new instance of a periodic job
|
||||
func (p *Periodic) Force(args *structs.PeriodicForceRequest, reply *structs.PeriodicForceResponse) error {
|
||||
|
||||
authErr := p.srv.Authenticate(p.ctx, args)
|
||||
if done, err := p.srv.forward("Periodic.Force", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "periodic", "force"}, time.Now())
|
||||
|
||||
// Check for write-job permissions
|
||||
if aclObj, err := p.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := p.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityDispatchJob) && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -28,16 +28,20 @@ func NewScalingEndpoint(srv *Server, ctx *RPCContext) *Scaling {
|
|||
// ListPolicies is used to list the policies
|
||||
func (p *Scaling) ListPolicies(args *structs.ScalingPolicyListRequest, reply *structs.ScalingPolicyListResponse) error {
|
||||
|
||||
authErr := p.srv.Authenticate(p.ctx, args)
|
||||
if done, err := p.srv.forward("Scaling.ListPolicies", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "scaling", "list_policies"}, time.Now())
|
||||
|
||||
if args.RequestNamespace() == structs.AllNamespacesSentinel {
|
||||
return p.listAllNamespaces(args, reply)
|
||||
}
|
||||
|
||||
if aclObj, err := p.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := p.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
hasListScalingPolicies := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityListScalingPolicies)
|
||||
|
@ -95,13 +99,17 @@ func (p *Scaling) ListPolicies(args *structs.ScalingPolicyListRequest, reply *st
|
|||
func (p *Scaling) GetPolicy(args *structs.ScalingPolicySpecificRequest,
|
||||
reply *structs.SingleScalingPolicyResponse) error {
|
||||
|
||||
authErr := p.srv.Authenticate(p.ctx, args)
|
||||
if done, err := p.srv.forward("Scaling.GetPolicy", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "scaling", "get_policy"}, time.Now())
|
||||
|
||||
// Check for list-job permissions
|
||||
if aclObj, err := p.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := p.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
hasReadScalingPolicy := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadScalingPolicy)
|
||||
|
@ -144,7 +152,7 @@ func (p *Scaling) GetPolicy(args *structs.ScalingPolicySpecificRequest,
|
|||
|
||||
func (p *Scaling) listAllNamespaces(args *structs.ScalingPolicyListRequest, reply *structs.ScalingPolicyListResponse) error {
|
||||
// Check for list-job permissions
|
||||
aclObj, err := p.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := p.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -548,12 +548,17 @@ func (*Search) silenceError(err error) bool {
|
|||
// PrefixSearch is used to list matches for a given prefix, and returns
|
||||
// matching jobs, evaluations, allocations, and/or nodes.
|
||||
func (s *Search) PrefixSearch(args *structs.SearchRequest, reply *structs.SearchResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward("Search.PrefixSearch", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "search", "prefix_search"}, time.Now())
|
||||
|
||||
aclObj, err := s.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := s.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -676,12 +681,17 @@ func sufficientSearchPerms(aclObj *acl.ACL, namespace string, context structs.Co
|
|||
//
|
||||
// The results are in descending order starting with strongest match, per Context type.
|
||||
func (s *Search) FuzzySearch(args *structs.FuzzySearchRequest, reply *structs.FuzzySearchResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward("Search.FuzzySearch", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "search", "fuzzy_search"}, time.Now())
|
||||
|
||||
aclObj, err := s.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := s.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/hashicorp/go-set"
|
||||
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/state"
|
||||
"github.com/hashicorp/nomad/nomad/state/paginator"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
|
@ -105,9 +104,13 @@ func (s *ServiceRegistration) DeleteByID(
|
|||
args *structs.ServiceRegistrationDeleteByIDRequest,
|
||||
reply *structs.ServiceRegistrationDeleteByIDResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward(structs.ServiceRegistrationDeleteByIDRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "service_registration", "delete_id"}, time.Now())
|
||||
|
||||
// Nomad service registrations can only be used once all servers, in the
|
||||
|
@ -118,7 +121,7 @@ func (s *ServiceRegistration) DeleteByID(
|
|||
}
|
||||
|
||||
// Perform the ACL token resolution.
|
||||
aclObj, err := s.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := s.srv.ResolveACL(args)
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
|
@ -198,9 +201,13 @@ func (s *ServiceRegistration) List(
|
|||
args *structs.ServiceRegistrationListRequest,
|
||||
reply *structs.ServiceRegistrationListResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward(structs.ServiceRegistrationListRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "service_registration", "list"}, time.Now())
|
||||
|
||||
// If the caller has requested to list services across all namespaces, use
|
||||
|
@ -209,9 +216,12 @@ func (s *ServiceRegistration) List(
|
|||
return s.listAllServiceRegistrations(args, reply)
|
||||
}
|
||||
|
||||
// Perform our mixed auth handling.
|
||||
if err := s.handleMixedAuthEndpoint(args.QueryOptions, acl.NamespaceCapabilityReadJob); err != nil {
|
||||
return err
|
||||
aclObj, err := s.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
if !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Set up and return the blocking query.
|
||||
|
@ -270,9 +280,9 @@ func (s *ServiceRegistration) listAllServiceRegistrations(
|
|||
args *structs.ServiceRegistrationListRequest,
|
||||
reply *structs.ServiceRegistrationListResponse) error {
|
||||
|
||||
// Perform token resolution. The request already goes through forwarding
|
||||
// Perform ACL resolution. The request already goes through forwarding
|
||||
// and metrics setup before being called.
|
||||
aclObj, err := s.srv.ResolveToken(args.AuthToken)
|
||||
aclObj, err := s.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -360,14 +370,21 @@ func (s *ServiceRegistration) GetService(
|
|||
args *structs.ServiceRegistrationByNameRequest,
|
||||
reply *structs.ServiceRegistrationByNameResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward(structs.ServiceRegistrationGetServiceRPCMethod, args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "service_registration", "get_service"}, time.Now())
|
||||
|
||||
// Perform our mixed auth handling.
|
||||
if err := s.handleMixedAuthEndpoint(args.QueryOptions, acl.NamespaceCapabilityReadJob); err != nil {
|
||||
return err
|
||||
aclObj, err := s.srv.ResolveClientOrACL(args)
|
||||
if err != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
if !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Set up the blocking query.
|
||||
|
@ -498,64 +515,3 @@ func (*ServiceRegistration) choose(services []*structs.ServiceRegistration, para
|
|||
|
||||
return chosen, nil
|
||||
}
|
||||
|
||||
// handleMixedAuthEndpoint is a helper to handle auth on RPC endpoints that can
|
||||
// either be called by Nomad nodes, or by external clients.
|
||||
func (s *ServiceRegistration) handleMixedAuthEndpoint(args structs.QueryOptions, cap string) error {
|
||||
|
||||
// Perform the initial token resolution.
|
||||
aclObj, err := s.srv.ResolveToken(args.AuthToken)
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
// Perform our ACL validation. If the object is nil, this means ACLs
|
||||
// are not enabled, otherwise trigger the allowed namespace function.
|
||||
if aclObj != nil {
|
||||
if !aclObj.AllowNsOp(args.RequestNamespace(), cap) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Attempt to verify the token as a JWT with a workload
|
||||
// identity claim if it's not a secret ID.
|
||||
// COMPAT(1.4.0): we can remove this conditional in 1.5.0
|
||||
if !helper.IsUUID(args.AuthToken) {
|
||||
claims, err := s.srv.VerifyClaim(args.AuthToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if claims == nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// COMPAT(1.4.0): Nomad 1.3.0 shipped with authentication by
|
||||
// node secret but that's been replaced with workload identity
|
||||
// in 1.4.0. Leave this here for backwards compatibility
|
||||
// between clients and servers during cluster upgrades, but
|
||||
// remove for 1.5.0
|
||||
|
||||
// In the event we got any error other than ErrTokenNotFound, consider this
|
||||
// terminal.
|
||||
if err != structs.ErrTokenNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to lookup AuthToken as a Node.SecretID and
|
||||
// return any error wrapped along with the original.
|
||||
node, stateErr := s.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
|
||||
if stateErr != nil {
|
||||
var mErr multierror.Error
|
||||
mErr.Errors = append(mErr.Errors, err, stateErr)
|
||||
return mErr.ErrorOrNil()
|
||||
}
|
||||
// At this point, we do not have a valid ACL token, nor are we being
|
||||
// called, or able to confirm via the state store, by a node.
|
||||
if node == nil {
|
||||
return structs.ErrTokenNotFound
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -70,8 +70,12 @@ func (s *Status) Peers(args *structs.GenericRequest, reply *[]string) error {
|
|||
// Members return the list of servers in a cluster that a particular server is
|
||||
// aware of
|
||||
func (s *Status) Members(args *structs.GenericRequest, reply *structs.ServerMembersResponse) error {
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
// Check node read permissions
|
||||
if aclObj, err := s.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if aclObj, err := s.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -22,12 +22,17 @@ func NewSystemEndpoint(srv *Server, ctx *RPCContext) *System {
|
|||
// GarbageCollect is used to trigger the system to immediately garbage collect nodes, evals
|
||||
// and jobs.
|
||||
func (s *System) GarbageCollect(args *structs.GenericRequest, reply *structs.GenericResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward("System.GarbageCollect", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := s.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := s.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl != nil && !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
@ -46,12 +51,17 @@ func (s *System) GarbageCollect(args *structs.GenericRequest, reply *structs.Gen
|
|||
// ReconcileJobSummaries reconciles the summaries of all the jobs in the state
|
||||
// store
|
||||
func (s *System) ReconcileJobSummaries(args *structs.GenericRequest, reply *structs.GenericResponse) error {
|
||||
|
||||
authErr := s.srv.Authenticate(s.ctx, args)
|
||||
if done, err := s.srv.forward("System.ReconcileJobSummaries", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check management level permissions
|
||||
if acl, err := s.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
if acl, err := s.srv.ResolveACL(args); err != nil {
|
||||
return err
|
||||
} else if acl != nil && !acl.IsManagement() {
|
||||
return structs.ErrPermissionDenied
|
||||
|
|
|
@ -40,7 +40,7 @@ func (sv *Variables) Apply(args *structs.VariablesApplyRequest, reply *structs.V
|
|||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{
|
||||
|
@ -114,8 +114,8 @@ func svePreApply(sv *Variables, args *structs.VariablesApplyRequest, vd *structs
|
|||
canRead = false
|
||||
var aclObj *acl.ACL
|
||||
|
||||
// Perform the ACL token resolution.
|
||||
if aclObj, err = sv.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
// Perform the ACL resolution.
|
||||
if aclObj, err = sv.srv.ResolveACL(args); err != nil {
|
||||
return
|
||||
} else if aclObj != nil {
|
||||
hasPerm := func(perm string) bool {
|
||||
|
@ -230,7 +230,7 @@ func (sv *Variables) Read(args *structs.VariablesReadRequest, reply *structs.Var
|
|||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "variables", "read"}, time.Now())
|
||||
|
@ -280,7 +280,7 @@ func (sv *Variables) List(
|
|||
return err
|
||||
}
|
||||
if authErr != nil {
|
||||
return authErr
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
defer metrics.MeasureSince([]string{"nomad", "variables", "list"}, time.Now())
|
||||
|
@ -295,7 +295,7 @@ func (sv *Variables) List(
|
|||
var err error
|
||||
aclToken := args.GetIdentity().GetACLToken()
|
||||
if aclToken != nil {
|
||||
aclObj, err = sv.srv.ResolveACL(args.GetIdentity().GetACLToken())
|
||||
aclObj, err = sv.srv.ResolveACLForToken(aclToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ func (sv *Variables) listAllVariables(
|
|||
var err error
|
||||
aclToken := args.GetIdentity().GetACLToken()
|
||||
if aclToken != nil {
|
||||
aclObj, err = sv.srv.ResolveACL(args.GetIdentity().GetACLToken())
|
||||
aclObj, err = sv.srv.ResolveACLForToken(aclToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -499,7 +499,7 @@ func (sv *Variables) handleMixedAuthEndpoint(args structs.QueryOptions, cap, pat
|
|||
var err error
|
||||
aclToken := args.GetIdentity().GetACLToken()
|
||||
if aclToken != nil {
|
||||
aclObj, err = sv.srv.ResolveACL(args.GetIdentity().GetACLToken())
|
||||
aclObj, err = sv.srv.ResolveACLForToken(aclToken)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue