Skip to content

Security operator

Purpose

The Security Operator automates security configuration across your platform mesh environment. It watches for new workspaces and automatically sets up everything needed to manage access control in Platform Mesh: creating authorization stores in OpenFGA, provisioning realms and clients in Keycloak, and keeping authorization models in sync as your APIs evolve.

Instead of manually configuring authentication and authorization for each new workspace, the Security Operator handles it all for you — ensuring consistent security posture across all organizations within your Platform Mesh deployment.

Runtime role

The Security Operator automates security configuration across Platform Mesh:

Workspace Initialization — Security Operator initializes workspaces of org and account type. For an org workspace, the operator creates a Store resource with authorization model and tuples, an IdentityProviderConfiguration resource that provisions Keycloak realm and OIDC clients, and a WorkspaceAuthenticationConfiguration resource linking realms to KCP workspaces. For an account type workspace, the operator creates tuples with account creator information.

OpenFGA and Keycloak Management — Maintains authorization stores (one per organization) with fine-grained access control, writes authorization tuples mapping users to roles and resources, provisions isolated Keycloak realms with OIDC clients, and dynamically extends authorization models when APIs are bound.

API Export Binding Control — Enforces deny-by-default binding policy through ApiExportPolicy resources, writes authorization tuples to OpenFGA enabling permitted workspaces to create ApiBinding resources, and automatically creates AuthorizationModel resources in provider workspaces to extend consumer authorization models.

Repository

Core Concepts

The Security Operator manages several custom resources that work together to provide comprehensive security automation.

Store

The Store resource represents an OpenFGA authorization store within the Platform Mesh. Each organization gets its own Store, which serves as the foundation for all authorization decisions within that organization's workspaces.

The Store resource bridges Kubernetes and OpenFGA by:

  • Maintaining the core authorization model that defines permission relationships
  • Managing tuples that map users to roles and resources
  • Automatically extending the authorization model when new APIs are bound

Store resources are created during workspace initialization when a new organization is provisioned or during the Platform Mesh installation phase. The Store controller watches these resources and creates corresponding stores in the OpenFGA service, then keeps the authorization model and tuples synchronized.

Example Store resource:

yaml
apiVersion: core.platform-mesh.io/v1alpha1
kind: Store
metadata:
  name: test
  finalizers:
  - core.platform-mesh.io/fga-store
  - core.platform-mesh.io/fga-tuples
spec:
  coreModule: |
    module core

    type user

    type role
      relations
        define assignee: [user,user:*]

    type core_platform-mesh_io_account
      relations
        define parent: [core_platform-mesh_io_account]

        define owner: [role#assignee] or owner from parent
        define member: [role#assignee] or owner

        define get: member
        define update: member
        define patch: member
        define delete: owner

        define create_core_platform-mesh_io_accounts: member
        define list_core_platform-mesh_io_accounts: member
        define watch_core_platform-mesh_io_accounts: member

        # org and account specific
        define watch: member

        define create_core_platform-mesh_io_accountinfos: member
        define list_core_platform-mesh_io_accountinfos: member
        define watch_core_platform-mesh_io_accountinfos: member

        define list_core_kcp_io_logicalclusters: member
        define watch_core_kcp_io_logicalclusters: member

        # IAM specific
        define manage_iam_roles: owner
        define get_iam_roles: member
        define get_iam_users: member

        # APIExport binding control
        define bind_inherited: [apis_kcp_io_apiexport] or bind_inherited from parent
        define bind: [apis_kcp_io_apiexport] or bind_inherited

    type core_platform-mesh_io_accountinfo
      relations
        define parent: [core_platform-mesh_io_account]

        define member: member from parent
        define owner: owner from parent

        define get: member
        define watch: member

        # IAM specific
        define manage_iam_roles: owner
        define get_iam_roles: member
        define get_iam_users: member

    type core_kcp_io_logicalcluster
      relations
        define parent: [core_platform-mesh_io_account]

        define member: member from parent

        define get: member
        define watch: member
  tuples:
  - object: role:core_platform-mesh_io_account/<logical-cluster-name>/<account-name>/owner
    relation: assignee
    user: user:<user-email>
  - object: core_platform-mesh_io_account:<logical-cluster-name>/<account-name>
    relation: owner
    user: role:core_platform-mesh_io_account/<logical-cluster-name>/<account-name>/owner#assignee
status:
  storeId: 01KQF7C00X593KN79515TFA4VG
  authorizationModelId: 01KQS6VBH2DWWEQDZJ3Z55RB31
  conditions:
  - type: Ready
    status: "True"
    reason: Complete
    message: all subroutines completed successfully
  managedTuples:
  - object: role:core_platform-mesh_io_account/<logical-cluster-name>/<account-name>/owner
    relation: assignee
    user: user:<user-email>
  - object: core_platform-mesh_io_account:<logical-cluster-name>/<account-name>
    relation: owner
    user: role:core_platform-mesh_io_account/<logical-cluster-name>/<account-name>/owner#assignee

Key fields:

  • spec.coreModule: The OpenFGA authorization model in DSL format, defining permission relationships for Platform Mesh core resources. The same core model is used across all organizations. See the security-operator Helm chart values for the configuration source.

  • spec.tuples: Initial authorization tuples that map users to roles. Tuple identifiers include the logical cluster name (the name of the kcp cluster where the account is created) and the account name, forming a globally unique reference like core_platform-mesh_io_account:<logical-cluster-name>/<account-name>.

  • status.storeId: The OpenFGA store ID assigned when the store is created in OpenFGA.

  • status.authorizationModelId: The OpenFGA model ID for the current authorization schema version.

  • status.managedTuples: Tuples that were successfully written to OpenFGA, mirroring the spec tuples once reconciliation completes. Note that OpenFGA contains additional tuples created during account initialization and other operations — managedTuples only tracks the tuples explicitly declared in the Store resource spec.

AuthorizationModel

An AuthorizationModel defines the permission schema for a specific API within an OpenFGA store. The common authorization model for every organization in OpenFGA consists of two parts: the core module from the Store resource and auto-generated schemas based on Kubernetes resources, generated by the security-operator during Store reconciliation. AuthorizationModel resources extend this base model with provider API-specific permissions.

When somebody creates an ApiBinding to consume a provider's API, the Security Operator automatically:

  1. Creates an AuthorizationModel resource in the provider's workspace
  2. Updates the consumer organization's Store with the extended authorization model
  3. Enables fine-grained access control for the newly bound API's resources

This dynamic model extension means providers don't need to manually configure authorization for each consumer — the Security Operator handles it automatically as APIs are bound and unbound.

Example AuthorizationModel resource:

yaml
apiVersion: core.platform-mesh.io/v1alpha1
kind: AuthorizationModel
metadata:
  name: orchestrate-platform-mesh-io-httpbins-<organization name>
  finalizers:
  - core.platform-mesh.io/fga-tuples
spec:
  model: |
    module httpbins

    extend type core_namespace
      relations
        define create_orchestrate_platform-mesh_io_httpbins: owner
        define list_orchestrate_platform-mesh_io_httpbins: member
        define watch_orchestrate_platform-mesh_io_httpbins: member

    type orchestrate_platform-mesh_io_httpbin
      relations
        define parent: [core_namespace]
        define member: [role#assignee] or owner or member from parent
        define owner: [role#assignee] or owner from parent
        
        define get: member
        define update: member
        define delete: member
        define patch: member
        define watch: member

        define manage_iam_roles: owner
        define get_iam_roles: member
        define get_iam_users: member
  storeRef:
    cluster: <logical-cluster-name>
    name: <organization-name>

Key fields:

  • spec.model: The OpenFGA authorization model extension in DSL format. This model extends the core authorization model with provider API-specific types and permissions. In this example, it defines permissions for the orchestrate_platform-mesh_io_httpbin resource type and extends core_namespace to include httpbin-related operations.

  • spec.storeRef: Reference to the consumer organization's Store resource. The cluster field contains the logical cluster name, and name contains the organization name. The Security Operator uses this reference to merge the authorization model extension into the consumer's OpenFGA store.

IdentityProviderConfiguration (IDP)

The IdentityProviderConfiguration resource configures Keycloak as the identity provider for a workspace. Each organization gets its own Keycloak realm, isolated from other organizations, with OIDC clients pre-configured for common authentication flows.

An IdentityProviderConfiguration resource is created during workspace initialization when a new organization is created and defines:

  • The Keycloak realm name
  • OIDC clients for web applications and CLI tools
  • Redirect URLs for each client
  • Token lifespan and authentication policies

The IDP controller provisions these resources in Keycloak and stores client credentials in Kubernetes secrets, making them available to applications that need to authenticate users within the workspace.

Client Types:

ClientTypePurpose
Workspace clientConfidentialWeb applications and services that can securely store secrets
kubectl clientPublicCLI authentication via OIDC device flow or local callback server

TIP

The kubectl client is automatically configured with localhost redirect URLs to support kubectl oidc-login and similar CLI authentication plugins.

Example IdentityProviderConfiguration resource:

yaml
apiVersion: core.platform-mesh.io/v1alpha1
kind: IdentityProviderConfiguration
metadata:
  name: <organization-name>
  finalizers:
  - core.platform-mesh.io/idp-finalizer
spec:
  registrationAllowed: true
  clients:
  - clientName: <organization-name>
    clientType: confidential
    redirectUris:
    - http://localhost:8000/callback*
    - http://localhost:4300/callback*
    - http://sub.localhost:4300/callback*
    - https://<organization-name>.portal.localhost:8443/*
    postLogoutRedirectUris:
    - https://<organization-name>.portal.localhost:8443/logout*
    secretRef:
      name: portal-client-secret-<organization-name>-<organization-name>
      namespace: default
  - clientName: kubectl
    clientType: public
    redirectUris:
    - http://localhost:8000
    - http://localhost:18000
    secretRef:
      name: portal-client-secret-<organization-name>-kubectl
      namespace: default
status:
  conditions:
  - type: Ready
    status: "True"
    reason: Complete
    message: all subroutines completed successfully
  managedClients:
    <organization-name>:
      clientId: acce553c-4644-497e-a089-05a7c856370c
      registrationClientUri: https://portal.localhost:8443/keycloak/realms/<organization-name>/clients-registrations/openid-connect/acce553c-4644-497e-a089-05a7c856370c
      secretRef:
        name: portal-client-secret-<organization-name>-<organization-name>
        namespace: default
    kubectl:
      clientId: 11e0418a-af32-48c5-837e-9581343e4249
      registrationClientUri: https://portal.localhost:8443/keycloak/realms/<organization-name>/clients-registrations/openid-connect/11e0418a-af32-48c5-837e-9581343e4249
      secretRef:
        name: portal-client-secret-<organization-name>-kubectl
        namespace: default

Key fields:

  • spec.registrationAllowed: Controls whether self-registration is enabled in the Keycloak realm.

  • spec.clients: List of OIDC clients to provision in the Keycloak realm. Each client specifies its type (confidential or public), redirect URIs for authentication callbacks, post-logout redirect URIs, and a reference to the Kubernetes secret where credentials are stored.

  • status.managedClients: Map of successfully provisioned clients in Keycloak. For each client, the status includes the Keycloak-assigned clientId, the registrationClientUri used for OIDC Dynamic Client Registration updates, and the secret reference. The Security Operator uses this information to manage client lifecycle through the OIDC protocol.

Invite

The Invite resource provides a declarative way to invite users into an organization via email. Rather than manually managing user accounts in Keycloak, administrators can create Invite resources that trigger automated invitation workflows.

When an Invite is created, the Security Operator:

  • Sends an email invitation to the specified address
  • Provisions a pending user account in the workspace's Keycloak realm
  • Grants appropriate permissions once the user accepts the invitation

This enables self-service user onboarding while maintaining security boundaries between organizations.

Example Invite resource:

yaml
apiVersion: core.platform-mesh.io/v1alpha1
kind: Invite
metadata:
  name: <invite-name>
spec:
  email: <user-email>
status:
  conditions:
  - type: Ready
    status: "True"
    reason: Complete
    message: all subroutines completed successfully

Key fields:

  • spec.email: The email address of the user to invite. The Security Operator sends an invitation email to this address and provisions a pending user account in the organization's Keycloak realm.

ApiExportPolicy

By default, all API binding in the Platform Mesh is denied — workspaces cannot bind to a provider's APIExport without explicit permission. The ApiExportPolicy resource grants these binding permissions at scale using workspace path expressions.

An ApiExportPolicy consists of two parts:

FieldPurpose
apiExportRefIdentifies the APIExport to grant access to (name and cluster path)
allowPathExpressionsList of workspace path patterns that should receive bind permissions

Path Expression Syntax:

  • :root:orgs:example — Grants permission to exactly this workspace
  • :root:orgs:example:* — Grants permission to this workspace and all descendants

ApiExportPolicy resources are typically created by Platform Mesh administrators when publishing APIs for broader consumption. The Security Operator watches these policies and writes the corresponding authorization tuples to OpenFGA, enabling the permitted workspaces to create ApiBinding resources.

Example APIExportPolicy resource:

yaml
apiVersion: core.platform-mesh.io/v1alpha1
kind: APIExportPolicy
metadata:
  name: orchestrate.platform-mesh.io
  finalizers:
  - system.platform-mesh.io/apiexportpolicy-finalizer
spec:
  apiExportRef:
    name: orchestrate.platform-mesh.io
    clusterPath: root:providers:httpbin-provider
  allowPathExpressions:
  - :root:orgs:*
status:
  conditions:
  - type: Ready
    status: "True"
    reason: Complete
    message: all subroutines completed successfully
  managedAllowExpressions:
  - :root:orgs:*

Configuration

OpenFGA Settings

Configure how the Security Operator connects to and manages OpenFGA stores. The --fga-target must point to your OpenFGA instance, while the other flags control the authorization model structure and caching behavior.

FlagDefaultDescription
--fga-targetOpenFGA API target address
--fga-store-id-cache-ttl24hTTL for the OpenFGA store ID cache
--fga-object-typecore_platform-mesh_io_accountOpenFGA object type for account tuples
--fga-parent-relationparentOpenFGA parent relation name
--fga-creator-relationownerOpenFGA creator relation name
--core-module-pathPath to the core module FGA model file
--migrate-authorization-modelsfalseEnable one-time authorization model migration
--allow-member-tuples-enabledfalseEnable allow-member tuples management

TIP

Use --migrate-authorization-models=true only during initial setup or major version upgrades. This flag performs a one-time migration and should not be left enabled in production.

Keycloak & Identity Provider Settings

Configure Keycloak connection and identity provider behavior. The base URL and client credentials are required for operator authentication, while SMTP settings enable email-based user invitations.

FlagDefaultDescription
--keycloak-base-urlKeycloak base URL
--keycloak-client-idsecurity-operatorKeycloak client ID for operator authentication
--idp-realm-deny-listComma-separated list of Keycloak realms to ignore
--idp-access-token-lifespan28800Keycloak access token lifespan in seconds (8 hours)
--idp-registration-allowedfalseEnable Keycloak self-registration
--idp-additional-redirect-urlsAdditional redirect URLs for Keycloak clients
--idp-kubectl-client-redirect-urlshttp://localhost:8000, http://localhost:18000Redirect URLs for kubectl Keycloak client
--set-default-passwordfalseEnable setting default password for IDP users
--http-client-timeout-seconds30HTTP client timeout in seconds

INFO

The Keycloak client secret is read from the KEYCLOAK_CLIENT_SECRET environment variable.

SMTP Configuration

Configure email delivery for user invitations and notifications. All SMTP settings are optional — if not configured, email-based features will be disabled.

FlagDefaultDescription
--idp-smtp-serverKeycloak SMTP server host
--idp-smtp-port0Keycloak SMTP server port
--idp-from-addressSMTP from address for Keycloak emails
--idp-smtp-userSMTP username
--idp-smtp-passwordSMTP password
--idp-smtp-sslfalseEnable SMTP SSL
--idp-smtp-starttlsfalseEnable SMTP STARTTLS

KCP Integration

Configure how the Security Operator connects to KCP workspaces and discovers API exports. These settings control the operator's view of the workspace hierarchy and API surface.

FlagDefaultDescription
--kcp-kubeconfig/api-kubeconfig/kubeconfigPath to KCP kubeconfig
--api-export-endpoint-slice-namecore.platform-mesh.ioCore APIExportEndpointSlice name
--system-api-export-endpoint-slice-namesystem.platform-mesh.ioSystem APIExportEndpointSlice name
--workspace-pathrootindicates in which workspace workspace type lives. It's used by Initializer and Terminator for providers setup
--workspace-type-namesecurityWorkspace type name

Workspace & Authentication Settings

Configure workspace initialization behavior and JWT token validation. The base domain is used to construct issuer URLs, while claim names determine how user identity is extracted from tokens.

FlagDefaultDescription
--base-domainportal.dev.local:8443Base domain for constructing issuer URLs
--group-claimgroupsID token group claim name
--user-claimemailID token user claim name
--additional-audiencesAdditional audiences to trust in workspace JWT authentication
--domain-ca-lookupfalseEnable lookup of domain CA from Kubernetes secret
--development-allow-unverified-emailsfalseAllow unverified emails in development mode

WARNING

Only enable --development-allow-unverified-emails in non-production environments. This flag bypasses email verification and should never be used in production.

Initializers

Control which initialization components are active. Disabling an initializer prevents the Security Operator from automatically creating resources of that type during workspace provisioning.

FlagDefaultDescription
--initializer-workspace-enabledtrueEnable workspace initialization
--initializer-idp-enabledtrueEnable IDP initialization
--initializer-invite-enabledtrueEnable invite initialization
--initializer-workspace-auth-enabledtrueEnable workspace auth initialization

Webhooks

Configure the validating webhook server for CRD validation. Webhooks are disabled by default — enable them to add admission control for Security Operator resources.

FlagDefaultDescription
--webhooks-enabledfalseEnable validating webhooks
--webhooks-port9443Webhook server port
--webhooks-cert-dir/tmp/k8s-webhook-server/serving-certsWebhook certificate directory

INFO

Refer to the component repository for the full configuration reference and environment variable options.

Architecture Decision Records

EU and German government funding logos

Funded by the European Union – NextGenerationEU.

The views and opinions expressed are solely those of the author(s) and do not necessarily reflect the views of the European Union or the European Commission. Neither the European Union nor the European Commission can be held responsible for them.