rbac

package
v1.17.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 19 Imported by: 13

README

pkg/rbac

This package computes effective authority inside UNI after authentication and identity propagation have already happened.

Intent

The package is the platform's effective-authority engine.

It takes authenticated actor context and delegated identity context, resolves roles and group membership from identity storage, and produces ACLs that the rest of the system can enforce.

Its main responsibilities are:

  • resolve permissions for users, service accounts, and system accounts
  • apply global, organization, and project scope
  • prevent confused-deputy behaviour when a service acts as an impersonated principal
  • constrain administrative delegation so callers cannot grant authority they do not themselves hold

This package is not just a convenience layer for handler checks. It is part of the security model.

Security Model

The package enforces several important security rules:

  • authority is derived from roles via group membership and actor type
  • permissions are additive within the allowed role set
  • protected roles are internal-only roles and are never user-facing
  • a caller may only grant a role if the caller already holds all permissions contained in that role
  • when a system service acts as an impersonated principal, the effective ACL is the intersection of the principal's ACL and the service's ACL

Those rules prevent several different forms of privilege escalation:

  • user-facing exposure of internal platform roles
  • granting permissions the caller does not personally hold
  • confused-deputy expansion through service-to-service calls

Scope Model

The package works with the same three logical scope levels defined by identity roles:

  • global
  • organization
  • project

ACL construction and handler enforcement both follow that structure. Global permissions can satisfy organization and project checks, organization permissions can satisfy some project checks, and project permissions remain the narrowest scope.

This scoped structure is used both for direct authorization decisions and for query limiting in list operations.

Each scope check comes in three argument flavours so callers pass whatever they already hold, without re-deriving it:

  • AllowOrganizationScope / AllowProjectScope / AllowProjectScopeCreate take plain string IDs. These are deprecated (marked // Deprecated: so tooling flags new use) but retained for backwards compatibility while callers that still deal in plain strings (IDs sourced from API response bodies, and repos that pre-date the typed ID types) migrate; they will be removed once that is done.
  • …ID variants (AllowOrganizationScopeID, AllowProjectScopeID, AllowProjectScopeCreateID) take typed ids.OrganizationID / ids.ProjectID. API handlers use these, since the IDs arrive already decoded from URL path parameters.
  • …Reader variants (AllowOrganizationScopeReader, AllowProjectScopeReader, AllowProjectScopeCreateReader) take a resource implementing ids.OrganizationScopeReader / ids.ProjectScopeReader and recover the IDs from it. Callers holding a CRD use these — the label-read-and-parse happens in one place behind the interface rather than at every call.

Rule of thumb: path-parameter handler → …ID; you have a CRD object in hand → …Reader.

Actor Model

The package distinguishes three important actor classes:

  • users
  • service accounts
  • system accounts

Users derive access from organization membership, groups, and roles.

Service accounts derive access from their bound organization and group membership.

System accounts derive access from configured platform roles mapped from their authenticated service identity, typically an mTLS certificate common name.

When a system account carries an impersonated principal, RBAC does not simply switch to the principal's ACL. Instead, it intersects the principal ACL with the system account ACL so the service cannot exercise permissions that either side lacks.

Invariants

  • Effective authority is computed from stored identity state, not invented ad hoc in handlers.
  • Protected roles are not part of normal user-facing role administration.
  • Role grantability is bounded by the caller's own effective permissions.
  • ACL intersection for impersonated system-account calls is deliberate least-privilege behaviour.
  • Service accounts are organization-bound and their scoped access must remain consistent with that binding.
  • Group membership is the main route from actors to roles.
  • The ACL output is both an enforcement artifact and a visibility artifact, so incorrect ACL construction affects both authorization and UX.

Caveats

  • The package is tightly coupled to the identity storage and scoping model, including groups, projects, organization mappings, and label-based queries.
  • Some migration-era behaviour is still present, especially compatibility with the deprecated Group.UserIDs field alongside the newer Subjects model.
  • The package contains a mix of ACL construction, scope filtering, and handler-facing convenience checks, so it is broader than a pure policy-definition layer.
  • Some pragmatic compatibility behaviour exists around scoped lookups and transition paths, so security-sensitive changes here should be reviewed in terms of end-to-end actor behaviour rather than local code shape alone.
  • Role permission sets must be distributed consistently across the role hierarchy. Because grantability requires the caller to hold every permission a role contains (with project-scoped endpoints promoted to an organization-scope check), granting a service's endpoints to a lower role such as user or reader without also granting them to the organization administrator silently makes that lower role non-grantable and invisible to administrators. Any new service endpoint added to the roles in charts/identity/values.yaml must be added to every role that should be able to grant it — not just the leaf roles that consume it.
  • The application:* endpoints (application:applications, application:applicationsets) were removed because the application service was never implemented and never will be — they were dead configuration. The removal also fixed a live bug: they were present on platform-administrator, user, and reader but absent from the organization administrator, which broke administrator grantability of user/reader. They are gone for good; there is no service to grant access to.

TODO

  • Re-check places where globally scoped callers are allowed to skip existence verification for user-supplied scoped resource identifiers, especially create paths that accept project IDs in the request body.
  • Add test coverage for role grantability/visibility across the role hierarchy. The application:* regression went unnoticed because nothing asserts that every non-protected role remains grantable by the organization administrator; a guard test over the role definitions would have caught it.

Relationship To Other Packages

  • pkg/oauth2 establishes actor identity and session/token validity
  • pkg/principal carries delegated identity and impersonation signals
  • pkg/rbac converts those inputs into effective local authority
  • middleware and handlers consume the resulting ACLs to enforce access and shape responses
  • pkg/oauth2, which establishes actor identity, session validity, and local admission before RBAC resolution
  • pkg/userdb, which shields RBAC from the raw local identity storage model when resolving users, organization memberships, and service accounts
  • pkg/principal, which carries delegated identity and impersonation signals consumed here
  • pkg/apis/unikorn/v1alpha1, which defines the stored role, group, organization, project, user, and service-account resources this package resolves

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrResourceReference      = goerrors.New("resource reference error")
	ErrNoAuthz                = goerrors.New("no authorization data in userinfo")
	ErrWrongOrganizationCount = goerrors.New("expected exactly one organization ID")
	ErrNotInOrganization      = goerrors.New("subject not a member of organization")
	ErrInvalidPrincipalType   = goerrors.New("invalid impersonated principal type")
)
View Source
var (
	ErrNoMatches = goerrors.New("selector would select nothing")
)

Functions

func AddOrganizationAndProjectIDQuery added in v1.11.0

func AddOrganizationAndProjectIDQuery(ctx context.Context, selector labels.Selector, organizationQuery []string, projectQuery []string) (labels.Selector, error)

AddOrganizationAndProjectIDQuery gets all organizationIDs the user can access (or has requested explicit and has access to).

func AddOrganizationIDQuery added in v1.11.0

func AddOrganizationIDQuery(ctx context.Context, selector labels.Selector, query []string) (labels.Selector, error)

AddOrganizationIDQuery adds an organizational query selector that limits resources to be listed to those available in the ACL and optionally constrained to those in the request query using a boolean intersection.

func AddQuery added in v1.11.0

func AddQuery(selector labels.Selector, label string, vals []string) (labels.Selector, error)

AddQuery adds a set of query values to a label selector.

func AllowGlobalScope added in v0.2.20

func AllowGlobalScope(ctx context.Context, endpoint string, operation openapi.AclOperation) error

AllowGlobalScope tries to allow the requested operation at the global scope.

func AllowOrganizationScope deprecated added in v0.2.20

func AllowOrganizationScope(ctx context.Context, endpoint string, operation openapi.AclOperation, organizationID string) error

AllowOrganizationScope tries to allow the requested operation at the global scope, then the organization scope.

Deprecated: prefer the typed AllowOrganizationScopeID (for path-parameter IDs) or AllowOrganizationScopeReader (for a resource implementing ids.OrganizationScopeReader). This string overload is retained for backwards compatibility with callers that still deal in plain strings (e.g. IDs from API response bodies or pre-typed-ID repositories) and will be removed once those callers have migrated.

func AllowOrganizationScopeID added in v1.17.5

func AllowOrganizationScopeID(ctx context.Context, endpoint string, operation openapi.AclOperation, organizationID ids.OrganizationID) error

AllowOrganizationScopeID is the typed variant of AllowOrganizationScope. Use this when the caller holds an ids.OrganizationID; the string overload is retained for callers in other repos that already deal in plain strings.

func AllowOrganizationScopeReader added in v1.17.7

func AllowOrganizationScopeReader(ctx context.Context, endpoint string, operation openapi.AclOperation, scope ids.OrganizationScopeReader) error

AllowOrganizationScopeReader is the variant of AllowOrganizationScope for callers that hold a resource implementing ids.OrganizationScopeReader (e.g. a region CRD). It recovers the organization ID from the resource. API handlers holding a path-parameter ID should use AllowOrganizationScopeID instead.

func AllowProjectScope deprecated added in v0.2.20

func AllowProjectScope(ctx context.Context, endpoint string, operation openapi.AclOperation, organizationID, projectID string) error

AllowProjectScope tries to allow the requested operation at the global scope, then the organization scope, and finally at the project scope.

Deprecated: prefer the typed AllowProjectScopeID (for path-parameter IDs) or AllowProjectScopeReader (for a resource implementing ids.ProjectScopeReader). This string overload is retained for backwards compatibility with callers that still deal in plain strings (e.g. IDs from API response bodies or pre-typed-ID repositories) and will be removed once those callers have migrated.

func AllowProjectScopeCreate deprecated added in v1.16.0

func AllowProjectScopeCreate(ctx context.Context, client openapi.ClientWithResponsesInterface, endpoint string, operation openapi.AclOperation, organizationID, projectID string) error

AllowProjectScopeCreate is like AllowProjectScope but intended for v2 create operations where the project ID is supplied in the request body rather than the URL path. When access is granted via an organization-scoped ACL the project ID is untrusted user input, so this function additionally verifies the project exists via the identity API before returning nil. Global-scope callers (platform administrators) are exempt from this check and their supplied project ID is trusted directly.

Deprecated: prefer the typed AllowProjectScopeCreateID (for path-parameter IDs) or AllowProjectScopeCreateReader (for a resource implementing ids.ProjectScopeReader). This string overload is retained for backwards compatibility with callers that pre-date the typed ID types and will be removed once those callers have migrated; the typed variants delegate here after converting to strings.

func AllowProjectScopeCreateID added in v1.17.5

func AllowProjectScopeCreateID(ctx context.Context, client openapi.ClientWithResponsesInterface, endpoint string, operation openapi.AclOperation, organizationID ids.OrganizationID, projectID ids.ProjectID) error

AllowProjectScopeCreateID is the typed variant of AllowProjectScopeCreate.

func AllowProjectScopeCreateReader added in v1.17.7

func AllowProjectScopeCreateReader(ctx context.Context, client openapi.ClientWithResponsesInterface, endpoint string, operation openapi.AclOperation, scope ids.ProjectScopeReader) error

AllowProjectScopeCreateReader is the variant of AllowProjectScopeCreate for callers that hold a resource implementing ids.ProjectScopeReader. It recovers the organization and project IDs from the resource.

func AllowProjectScopeID added in v1.17.5

func AllowProjectScopeID(ctx context.Context, endpoint string, operation openapi.AclOperation, organizationID ids.OrganizationID, projectID ids.ProjectID) error

AllowProjectScopeID is the typed variant of AllowProjectScope.

func AllowProjectScopeReader added in v1.17.7

func AllowProjectScopeReader(ctx context.Context, endpoint string, operation openapi.AclOperation, scope ids.ProjectScopeReader) error

AllowProjectScopeReader is the variant of AllowProjectScope for callers that hold a resource implementing ids.ProjectScopeReader (e.g. a region CRD). It recovers the organization and project IDs from the resource. API handlers holding path-parameter IDs should use AllowProjectScopeID instead.

func AllowRole added in v0.2.21

func AllowRole(ctx context.Context, role *unikornv1.Role, organizationID ids.OrganizationID) error

AllowRole determines whether your ACL contains the same or higher privileges than the role, which is then used to determine role visibility and limit privilege escalation.

func FromContext added in v0.2.20

func FromContext(ctx context.Context) *openapi.Acl

func HasNoMatches added in v1.12.0

func HasNoMatches(err error) bool

HasNoMatches is a short cut when nothing would be matched e.g. the user has no matching organization ID for the provided selector, and a list handler can just return an empty array directly.

func NewContext added in v0.2.20

func NewContext(ctx context.Context, acl *openapi.Acl) context.Context

func OrganizationIDs added in v1.11.0

func OrganizationIDs(ctx context.Context) []string

OrganizationIDs returns a list of all organization IDs from the ACL for the purposes of limiting list type API operations.

Types

type Options added in v0.2.54

type Options struct {
	PlatformAdministratorRoleIDs  []string
	PlatformAdministratorSubjects []string
	SystemAccountRoleIDs          map[string]string
}

func (*Options) AddFlags added in v0.2.54

func (o *Options) AddFlags(f *pflag.FlagSet)

type RBAC

type RBAC struct {
	// contains filtered or unexported fields
}

RBAC contains all the scoping rules for services across the platform.

func New

func New(client client.Client, namespace string, options *Options) *RBAC

New creates a new RBAC client.

func (*RBAC) GetACL added in v0.1.23

func (r *RBAC) GetACL(ctx context.Context, organizationID string) (*openapi.Acl, error)

GetACL returns a granular set of permissions for a user based on their scope. This is used for API level access control and UX.

func (*RBAC) NewSuperContext added in v0.2.59

func (r *RBAC) NewSuperContext(ctx context.Context) (context.Context, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL