permissions operator

The component is deployed as stp-controller as part of the Helm chart shturval-backend. There is no separate Helm chart just for the rights operator: along with it, the chart includes backend API, Envoy, OPA and Swagger UI.

Contents

  1. Review
  2. Architecture
  3. Installation/deployment
  4. Diagnostics
  5. Lifecycle
  6. Operator parameters
  7. CRD/API reference

1. Review

Purpose: the operator coordinates objects Permission, Scope, UserRole, GroupRole, Tenant with actual rights: aggregating and client ClusterRole, subject bindings through plugins (RBAC, Argo CD, Kubeflow, tenant), child areas for tenants and in-memory model for policies. HTTP endpoint /bundle (typical port 8082) sends data to OPA; when webhook is enabled, UserRole, GroupRole, Tenant are validated.

Features

ContourIntended use
Areas (Scope)Calculation of paths, cluster annotations, list of names Permission, statuses of bound objects
Custom roles (Permission)Aggregating ClusterRole on the platform and ClusterRole in client clusters by granted_cluster_rules (via kubeconfig from Secret on the platform)
Bindings (UserRole, GroupRole)Plugins enforce subject rights in scope; idempotency is reflected in plugin statuses
Tenant (Tenant)Create/update child Scope, platform Role for rights tenant-get / tenant-edit
AdmissionValidation UserRole, GroupRole, Tenant with webhook enabled
Bundle HTTPIssue revision OPA-bundle; periodic reassembly by interval (default 60 s)

Initiation scripts

What’s going onWho initiatesObserved effect
CR matchingControl plane Kubernetes (CR, relist, watch events)Update related ClusterRole, statuses, annotations, model for bundle
Binding ValidationAPI server on Update UserRole / GroupRoleReject invalid changes to key spec fields
Tenant ValidationAPI server when Create/Update Tenant along the path webhookReject Invalid Changes
Reassembly bundleTimer and HTTP consumer requests /bundleCurrent revision in response
Response to clustersCAPI events Cluster (control plane ready, delete, pause)Renegotiation of affected Permission, Scope, roles

Limitations and implications

LimitConsequence for the administrator
Once UserRole is created, spec.username cannot be changedThe change is blocked by the validating webhook during Update
Removing Permission with UserRole / GroupRole attachedThe finalizer is not removed until released; retry at about 1 minute intervals
Removing Tenant if there are child tenantsDelete negotiation fails before hierarchy is resolved
Field open_search_roles in PermissionMarked deprecated in the CRD; new configurations are better not to rely on it

Place in the chain of command

High level chaining: Tenant objects spawn/update the associated Scope; reconciliation Scope updates the storage model and statuses (including cluster annotations); UserRole / GroupRole require the existence of a corresponding Scope and set the owner’s link; Permission updates platform and client ClusterRole, changes to Permission via watch affect further negotiation of Scope.

Control chain: CR, operator, client clusters and bundle

Management chain: CR, operator, client clusters and bundle

The user or CI applies CR via the Kubernetes API. The API server stores the Tenant, Scope, Permission, UserRole and GroupRole objects, after which the permissions operator reads them, performs negotiations and accesses the external loops: RBAC plugins, Argo CD, Kubeflow, client clusters and CAPI Cluster objects.

Key Features

CharacteristicsDescription
API grouppermissions.shturval.tech, version v1beta1
Scope CRDNamespaced
kubectl shortNamesIn the generated CRDs for the listed species not specified
Namespace of a typical installation (Kustomize for development)stp-system, resource prefix stp-
Namespace of the process when setting chart to shturval-backendSame as the namespace of the Helm release; namespace and serviceAccountName from the Pod fields are sent to the container
Default value namespace for StorageEngine in binaryshturval-backend (overridden by environment variable)
Leader election IDpermissions.shturval.tech
MetricsParameters are set by deployment patches and process flags (see §6)
WebhookPort 9443; shutdown via ENABLE_WEBHOOKS=false
HTTP/2Disabled by default for TLS metrics and webhook servers
BundleDefault binding address :8082, path /bundle

Components and controllers

Name/RolePurpose
CRDpermissions, scopes, userroles, grouproles, tenants (cluster-wide definitions)
Deployment ManagerOne Pod with container manager
RBACClusterRole/bindings for manager access to API
PermissionReconcilerSync Permission with ClusterRole, finalizer, watch Cluster
ScopeReconcilerArea model, statuses, annotations; periodic requeue for cluster/namespace levels
UserRoleReconciler / GroupRoleReconcilerArea Rights Plugins
TenantReconcilerScope and tenant roles; deletion restrictions

2. Architecture

Workload Components

ComponentKindScopeDescription
CRDCustomResourceDefinitionClusterAPI Group Resources permissions.shturval.tech
ManagerDeploymentNamespacedOne Pod with container manager
RBACClusterRole, RoleBinding, etc.Per manifestsOperator access to required API resources

Runtime architecture: Deployment manager, RBAC, webhook, bundle

Illustration: Runtime architecture: Deployment manager, RBAC, webhook, bundle

Runtime architecture: Deployment manager, RBAC, webhook, bundle

Graph of connections between CR and the rights storage engine

Graph of connections between CR and rights storage engine

The diagram describes how the CRs are related to each other and to the in-memory StorageEngine. Tenant owns the associated Scope and creates the roles tenant-get / tenant-edit; UserRole and GroupRole refer to Scope and the names Permission; Permission populates the rights model and generates ClusterRole on the platform and in client clusters.

Communication with external objects

Object/systemRole
CAPI ClusterDefining Client Clusters and Ready/Delete Events
Secret kubeconfigName <cluster-name>-kubeconfig, key value - expected format for accessing the client clusters API
OPA ConsumersHTTP GET (and associated bundle update wait logic) to /bundle
Integration pluginsArgo CD, Kubeflow, RBAC Kubernetes, tenant - by type of rights in spec

RBAC is detailed by delivery manifests; The list of exact rules is not duplicated in this document.


3. Installation / deployment

Platform and Helm

The component is included in chart shturval-backend: Deployment stp-controller, the service for the bundle and related objects are specified by chart templates. There is no separate Helm chart only for permissions-operator ** - a common chart backend is used.

A typical installation on a management cluster is performed through cluster-manager: ShturvalServiceConfig (SSC) objects are created and updated with the chart name shturval-backend, namespace shturval-backend, dependencies on shturval-backend-crds and shturval-ingress-controller, chart version from the platform release parameters and user values.

Local installation of CRD/RBAC via Kustomize for development uses namespace stp-system; in operation through the platform, the typical release namespace is shturval-backend - this must be consistent with the process environment variable namespace.

Dependencies for client rules

For Permission with granted_cluster_rules (policies in client clusters), kubeconfig Secrets must exist on the platform according to the name and key agreement (see §2). Separate SSH keys to nodes in the described getter chain are not used.

Configuration via SSP

Any Helm values.yaml changes to an operator component in production must be submitted via ShturvalServicePatch (SSP) on top of the corresponding SSC, without directly editing the release outside of platform practices.

If you need to fine-tune container arguments and resources, check the actual chart and the platform release documentation for the version you are using.


4. Diagnostics

Standard checks (namespace nleave the actual one for release, usually shturval-backend):

  • state of Pods Deployment stp-controller;
  • Pod events and container logs manager for negotiation or webhook errors;
  • availability of a health sample at the address specified by the arguments (typically :8081);
  • for OPA policies - availability of the bundle service (port 8082 in the standard flag configuration).

Readiness and statuses (Ready, plugins)

ResourceField/structureInterpretation
Permissionstatus.readySummary status of application of rules
Scopestatus.ready, status.objectsStatusReadiness of the area and condition of objects
UserRole, GroupRolestatus (RoleStatus)Map permissions → clusters/platform → plugins → PluginObject
Tenantstatus.conditionSee terms table below

Plugin statuses (PluginApplyStatus)

ValueOperational impact
appliedPlugin successfully applied
not appliedNot yet applied
not requiredNot required (for example, when deleting)
undefinedNetwork or other error; records can be cleared on successful pass

Tenant Conditions (status.condition)

ConditionTrueFalse / reasons
TenantSynced—Error creating/patch Scope, receiving Scope (CreateOrPatchTenantFailed, GetScopeFailed)
TenantCreatedTenant and scope are synchronized (Created)—
TenantRoleCreated—Error creating Role tenant-get / tenant-edit
TenantDeleted—Child tenants or error deleting Scope/Role/parent scope

Typical symptoms

SymptomPossible reasonAction
Permission stuck in deletionThere are UserRole/GroupRole referencing the name permissionRemove or change bindings; wait for re-negotiation (~1 min)
No rights in the client clusterNo/broken kubeconfig Secret, pause/remove CAPI ClusterCheck Secret <cluster>-kubeconfig, key value, status Cluster
Webhook not respondingNo certificates / incorrect configuration when ENABLE_WEBHOOKS is enabledCheck TLS and webhook configuration; during emergency diagnostics - webhook disable mode (see §6)
Long update of cluster/namespace level scopeScheduled intervalsre-entry ala reconcileConsider frequency (up to ~30 min for cluster, ~5 min for namespace in the described logic)
Error deleting TenantChild tenantsFirst delete/move child objects

5. Life cycle

General circuit interaction diagram:

StepActorAction
1Negotiation TenantCreates or updates Scope with the same name, owner reference to Tenant
2Negotiation ScopeThe storage model and statuses are updated; cluster annotations
3Negotiation UserRole / GroupRoleRequires existing Scope; controller reference is set
4Negotiation PermissionCluster roles are updated; changes to Permission trigger further negotiation of Scope via watch

Simplified end-to-end loop: Kubernetes API and resource negotiation

Illustration: Simplified end-to-end loop: Kubernetes API and resource negotiation

Simplified end-to-end loop: Kubernetes API and resource negotiation

Permission

  • Finalizer: permissions.shturval.tech/finalizer.
  • Create/Update: clear status.applied_to_clusters, write to model; for platform rules - updating the aggregating ClusterRole; for client - selecting clusters and updating ClusterRole in each; setting Ready on success.
  • Removal: if there are role bindings to this permission - reschedule the retry in about a minute; otherwise, removing platform and client ClusterRole, removing the finalizer.
  • CAPI events: when the control plane is ready or a cluster is deleted - renegotiation of the affected Permission (with a filter for applied clusters).

Scope

  • Delete: if the model still has users/groups for the name scope - repeat after ~5 s; otherwise, removal from the model and removal of the finalizer.
  • Update: recalculation of clusters, annotations, namespace statuses; for the namespace level - checking the existence of namespace in target clusters; periodic repetitions for cluster levels (up to ~30 min) and namespace (~5 min) atnecessary.

UserRole and GroupRole

  • Delete: resetting plugin statuses, calling deletion for all plugins, calculating readiness, removing the finalizer and deleting from the model.
  • Update: finalizer, binding to Scope, model update, sequential application of plugins; in case of errors - immediate requeue; if successful, clear “undefined” plugin statuses.
  • Observation: changes to associated Scope; for UserRole - also a change to Cluster.Spec.Paused.

Tenant

  • Creation / update: when adding a finalizer for the first time - immediate re-entry; generation and application of Scope, creation of roles tenant-get / tenant-edit, registration in the model, condition TenantCreated.
  • Removal: checking for the absence of child tenants; delete Scope; while Scope exists - repeat after ~5 s; then deleting roles and records in the model, clearing the parent scope, removing the finalizer.

Name prefixes for created ClusterRole: shturval: for user roles, platform: for platform ones.

Transition diagrams during coordination

Below are diagrams aimed at understanding the states of an object in a cluster: readiness, deletion and finalizers.

Negotiate Permission: create/update ClusterRole, delete

Reconcile <code>Permission</code>: create/update <code>ClusterRole</code>, delete

If there is no Permission object, reconcile ends with an empty result. For an existing object, the operator separates the deletion path and the update path: when deleting, waits until UserRole and GroupRole no longer refer to permission, then deletes the platform and client ClusterRole, the engine entry and the finalizer; when updating, it adds a finalizer, updates the model, synchronizes the necessary ClusterRole and sets successful readiness.

UserRole negotiation: binding to Scope, plugins, readiness

Reconcile <code>UserRole</code>: binding to <code>Scope</code>, plugins, ready

When updating UserRole, the operator adds a finalizer, gets Scope, sets the controller reference, updates the entry in the engine, and applies permissions through plugins. Then the readiness is recalculated: if the application is incomplete, the object is put into reprocessing, and if it is successful, the undefined results are cleared and the reconcile is completed without restarting; when removing plugins callare sent for cleaning, readiness for removal is recalculated and the finalizer is removed.

GroupRole negotiation: symmetrical to UserRole

Conciliation <code>GroupRole</code>: symmetrical <code>UserRole</code>

The flow for GroupRole repeats the logic of UserRole, but the subject is the claim group. The operator adds the finalizer, reads the associated Scope, sets the owner reference, updates the model and applies permissions via plugins; when uninstalling, it removes plugin rights, checks the readiness of cleaning, and only after that removes the finalizer.

Scope negotiation: clusters, namespace, retries

<code>Scope</code> negotiation: clusters, namespace, retries

For active Scope, the operator adds a finalizer, filters clusters by identifiers and annotations, updates paths in StorageEngine, writes permissions, paths and objectsStatus to the status, and then sets it to ready. If the platform or cluster/namespace level scope requires rechecking, reconcile is rescheduled at cluster and namespace intervals; when deleting, the operator waits until there are no users or groups left in the engine, then removes the scope from the model and removes the finalizer.

Tenant negotiation: child Scope, roles tenant-get/tenant-edit

Reconcile <code>Tenant</code>: child <code>Scope</code>, roles tenant-get/tenant-edit

When creating or updating Tenant, the operator adds a finalizer, generates and applies the associated Scope, creates the roles tenant-get and tenant-edit, adds a tenant to the engine, and sets the TenantCreated=True condition. When deleting, the absence of child tenants is first checked; then the operator removes Scope, waits for it to disappear, removes the roles, removes the tenant from the model, clears the reference in the parent scope and removes the finalizer.


6. Operator parameters

Process arguments are specified by flags at the entry point. In delivery, they are transmitted through the Deployment chart template and SSP; the administrator can override them only through mechanisms consistent with the platform, without improvising names:

FlagDefaultPurpose
--metrics-bind-address0 (disabled in code; patch may have 127.0.0.1:8080)Metrics address
--health-probe-bind-address:8081Health test
--leader-electfalseLeader-election (in a typical deployment, aarguments)
--metrics-securetrueTLS for metrics
--enable-http2falseHTTP/2
--bundle-bind-address:8082Listening bundle
--bundle-path/bundleHTTP path bundle
--bundle-rebuild-duration60 (seconds)Reassembly period bundle
--max-reconciles5Concurrency of approvals
--webhook-port9443webhook port
--webhook-cert-dir/tmp/k8s-webhook-server/serving-certs/webhook certificate directory

zap logger options are also passed through the standard flags mechanism.

Environment Variables

VariablePurpose
ENABLE_WEBHOOKSSetting false disables webhook logging (useful for debugging without matched certificates)
namespaceModel storage area namespace (override default shturval-backend)
serviceAccountNameAccount for tenant webhook (default stp-tenant-webhook)
oidcClientRefLink to auth client (default shturval-backend/backend)
DEBUGWith true — development log mode

Note: with ENABLE_WEBHOOKS=false admission validation for the listed types is not performed on the operator side; Use only in controlled diagnostic scenarios.


7. CRD/API reference

General information: group permissions.shturval.tech, version v1beta1, scope of all CRs - Namespaced.

Permission

spec

PathTypeRequiredDescription
scope_levelstring (ScopeLevel)YesScope level to define Permission
granted_cluster_rules[]PolicyRuleNoRBAC rules for ClusterRole in client clusters
granted_platform_permissions[]stringNoPlatform permission names for aggregation ClusterRole
argo_cd_roles[]stringNoRole of Argo CD
kubeflow_roles[]stringNoKubeflow access role
open_search_roles[]stringNoDeprecated

status

PathTypeDescription
applied_to_clusters[]stringClusters where client ClusterRole is set
readyReadySummary status (ready, status, reason, message)

There are no separate conditions like metav1.Condition ** - only the ready field.

Scope

spec

PathRequiredDescription
spec.objectsYesLevel map → names (ScopeObjects): platform, clustergroups, clusters, namespaces, tenants, custom

status

PathDescription
pathsPaths scope
permissionsNames Permission available at scope level
readyReady
objectsStatusStatus of clusters and namespace

UserRole

spec

PathRequired
spec.scopeYes

status: RoleStatus - ready, nested map permissions → clusters/platform → plugins → PluginObject.

GroupRole

spec

PathRequired
spec.scopeYes

status: same as UserRole.

Tenant

spec

PathDescription
spec.objectsScopeObjects tenant boundaries
spec.parentRefParent tenant (name, namespace)
spec.immutableProhibition of spec changes

status

PathDescription
objectsStatusCopy from linked Scope on successful reconciliation
conditionOne metav1.Condition (see §4)