Files
nomad/.semgrep/rpc_endpoint.yml

127 lines
4.4 KiB
YAML

# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
rules:
# Check potentially RPC endpoints with missing authentication/authorization.
- id: "rpc-potentially-unauthenticated"
patterns:
- pattern: |
if done, err := $A.$B.forward($METHOD, ...); done {
return err
}
# Pattern used by typical endpoints that take an auth token or workload
# identity. Some of these endpoints have no context for Authenticate
- pattern-not-inside: |
authErr := $A.$B.Authenticate(...)
...
if done, err := $A.$B.forward($METHOD, ...); done {
return err
}
...
... := $A.$B.ResolveACL(...)
...
# Pattern used by endpoints that are used only for server-to-server. The
# authentication and authorization check must be done together before
# forwarding to prevent the risk of confused deputy when RPCs are
# forwarded.
- pattern-not-inside: |
aclObj, err := $A.srv.AuthenticateServerOnly($A.ctx, args)
...
if err != nil || !aclObj.AllowServerOp() {
return structs.ErrPermissionDenied
}
if done, err := $A.srv.forward($METHOD, ...); done {
return err
}
...
# Pattern used by endpoints that are used only for client-to-server.
# Authorization can be done after forwarding, but must check the
# AllowClientOp policy; the AllowClientOp condition is left open so that
# additional ACL checks can be made (ex. to scope to a given node/pool).
- pattern-not-inside: |
aclObj, err := $A.$B.AuthenticateClientOnly($A.ctx, args)
...
if done, err := $A.$B.forward($METHOD, ...); done {
return err
}
...
if !aclObj.AllowClientOp() {
return structs.ErrPermissionDenied
}
...
# Pattern used by endpoints that are used only for client-to-server.
# Authorization can be done after forwarding, but must check the
# AllowClientOp policy. This should not be added to any new endpoints.
- pattern-not-inside: |
aclObj, err := $A.$B.AuthenticateClientOnlyLegacy($A.ctx, args)
...
if done, err := $A.$B.forward($METHOD, ...); done {
return err
}
...
if !aclObj.AllowClientOp() {
return structs.ErrPermissionDenied
}
...
# Pattern used by ACL endpoints that need to interact with the token
# directly.
- pattern-not-inside: |
authErr := $A.$B.Authenticate($A.ctx, args)
...
if done, err := $A.$B.forward($METHOD, ...); done {
return err
}
...
... := args.GetIdentity().GetACLToken()
...
- metavariable-pattern:
metavariable: $METHOD
patterns:
# Endpoints that are expected not to have authentication.
- pattern-not: '"ACL.Bootstrap"'
- pattern-not: '"ACL.GetClaimPolicies"'
- pattern-not: '"ACL.ResolveToken"'
- pattern-not: '"ACL.UpsertOneTimeToken"'
- pattern-not: '"ACL.ExchangeOneTimeToken"'
- pattern-not: '"ACL.WhoAmI"'
- pattern-not: 'structs.ACLListAuthMethodsRPCMethod'
- pattern-not: 'structs.ACLOIDCAuthURLRPCMethod'
- pattern-not: 'structs.ACLOIDCCompleteAuthRPCMethod'
- pattern-not: 'structs.ACLLoginRPCMethod'
- pattern-not: '"Status.Leader"'
- pattern-not: '"Status.Peers"'
- pattern-not: '"Status.Version"'
- pattern-not: '"Keyring.ListPublic"'
- pattern-not: '"Keyring.GetConfig"'
message: "RPC method $METHOD appears to be unauthenticated"
languages:
- "go"
severity: "WARNING"
paths:
include:
- "nomad/*_endpoint.go"
# ACL objects should never be nil-checked in RPC handlers before checking
# authorization, as nil ACLs are always programmer errors.
- id: "rpc-authz-bypass"
patterns:
# Pattern that may accidentally bypass authorization checks.
- pattern: |
aclObj == nil
message: "RPC method ACL check $ACL_CHECK appears to bypass authorization by first checking for nil ACLs"
languages:
- "go"
severity: "WARNING"
paths:
include:
- "nomad/*_endpoint.go"