ProficientNowTechRFCs

8. Operator Identity

RFC-WORKLOAD-IDENTITY-0001                                      Section 8
Category: Standards Track                               Operator Identity

8. Operator Identity

← Previous: GitOps Identity | Index | Next: AI Agent Identity →


8.1 Kubernetes Operator Patterns

8.1.1 Operator Categories

CategoryExamplesIdentity Scope
Platform Operatorscert-manager, ESO, Vault OperatorCluster-wide
Storage OperatorsRook-Ceph, LonghornCluster-wide
Database OperatorsPostgreSQL Operator, MongoDBNamespace or cluster
Application OperatorsCustom CRDsNamespace-scoped
Monitoring OperatorsPrometheus OperatorCluster-wide (read)

8.1.2 Operator Identity Requirements

RequirementImplementation
Kubernetes API accessServiceAccount + RBAC
External resource accessVault credentials
Cross-namespace watchClusterRole (if needed)
Leader electionLease or ConfigMap access

8.1.3 Operator RBAC Pattern

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-operator
  namespace: my-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-operator
rules:
  # Manage custom resources
  - apiGroups: ["myoperator.example.com"]
    resources: ["myresources"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  # Watch for resources to reconcile
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
  # Create managed resources
  - apiGroups: ["apps"]
    resources: ["deployments", "statefulsets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  # Leader election
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-operator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: my-operator
subjects:
  - kind: ServiceAccount
    name: my-operator
    namespace: my-operator-system

8.2 Controller Service Accounts

8.2.1 Service Account Guidelines

GuidelineRationale
One SA per controllerFine-grained audit
Descriptive namingClear identification
Minimal RBACLeast privilege
No token automount by defaultExplicit volume mounting

8.2.2 Controller Deployment Pattern

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-controller
  namespace: my-operator-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-controller
  template:
    metadata:
      labels:
        app: my-controller
    spec:
      serviceAccountName: my-controller
      automountServiceAccountToken: false
      containers:
        - name: controller
          image: myorg/my-controller:v1.0.0
          volumeMounts:
            - name: sa-token
              mountPath: /var/run/secrets/kubernetes.io/serviceaccount
              readOnly: true
      volumes:
        - name: sa-token
          projected:
            sources:
              - serviceAccountToken:
                  path: token
                  expirationSeconds: 3600
                  audience: kubernetes.default.svc
              - configMap:
                  name: kube-root-ca.crt
                  items:
                    - key: ca.crt
                      path: ca.crt
              - downwardAPI:
                  items:
                    - path: namespace
                      fieldRef:
                        fieldPath: metadata.namespace

8.2.3 Cross-Namespace Authority

When operators need cross-namespace access:

PatternUse CaseConfiguration
ClusterRoleRead all namespacesClusterRoleBinding
Role per namespaceWrite to specific namespacesMultiple RoleBindings
Aggregated ClusterRoleExtensible permissionsLabel-based aggregation

Example: Operator that manages resources in specific namespaces only:

# ClusterRole for cross-namespace read
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-operator-reader
rules:
  - apiGroups: ["myoperator.example.com"]
    resources: ["myresources"]
    verbs: ["get", "list", "watch"]
---
# Role for namespace-scoped write
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: my-operator-writer
  namespace: team-a
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-operator-writer
  namespace: team-a
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: my-operator-writer
subjects:
  - kind: ServiceAccount
    name: my-operator
    namespace: my-operator-system

8.3 CronJob Identity

8.3.1 CronJob Identity Challenges

ChallengeSolution
Short-lived podsPre-provisioned SA tokens
Batch natureVault Agent init container pattern
No persistent stateStateless authentication
Time-sensitiveMinimal bootstrap latency

8.3.2 CronJob Pattern

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-report
  namespace: analytics
spec:
  schedule: "0 6 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: daily-report
          restartPolicy: OnFailure
          initContainers:
            - name: vault-agent
              image: hashicorp/vault:1.15
              args:
                - agent
                - -config=/etc/vault/config.hcl
                - -exit-after-auth
              volumeMounts:
                - name: vault-config
                  mountPath: /etc/vault
                - name: secrets
                  mountPath: /vault/secrets
          containers:
            - name: report
              image: myorg/daily-report:v1.0.0
              volumeMounts:
                - name: secrets
                  mountPath: /vault/secrets
                  readOnly: true
          volumes:
            - name: vault-config
              configMap:
                name: daily-report-vault-config
            - name: secrets
              emptyDir:
                medium: Memory
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: daily-report
  namespace: analytics

8.3.3 CronJob Vault Configuration

# Vault Agent config for CronJob
auto_auth {
  method "kubernetes" {
    mount_path = "auth/kubernetes-prod"
    config = {
      role = "daily-report"
    }
  }
 
  sink "file" {
    config = {
      path = "/vault/secrets/.vault-token"
    }
  }
}
 
template {
  source      = "/etc/vault/templates/db-creds.ctmpl"
  destination = "/vault/secrets/db-creds.json"
}

8.3.4 CronJob Identity Flow


8.4 Platform Operator Identity

8.4.1 cert-manager Identity

cert-manager needs special identity considerations:

AccessPurpose
Kubernetes SecretsStore issued certificates
Kubernetes CRDsManage Certificate, Issuer resources
ACME DNSDNS-01 challenge (cloud API access)
Vault PKIIssue certificates from Vault

Cloud access for DNS-01:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: cert-manager
  namespace: cert-manager
  annotations:
    # AWS IRSA for Route53 access
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/cert-manager-dns

8.4.2 External Secrets Operator Identity

ESO needs Vault access:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-secrets
  namespace: external-secrets
---
# ClusterSecretStore uses this SA
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault
spec:
  provider:
    vault:
      server: https://vault.vault.svc.cluster.local:8200
      path: secret
      auth:
        kubernetes:
          mountPath: kubernetes-prod
          role: external-secrets
          serviceAccountRef:
            name: external-secrets
            namespace: external-secrets

8.4.3 Rook-Ceph Operator Identity

Rook-Ceph requires extensive cluster access:

ComponentRBAC Scope
rook-ceph-operatorClusterRole (pods, nodes, PVs)
rook-ceph-osdNode-level access (hostPath)
rook-ceph-mgrClusterRole (metrics)

8.5 Multi-Tenant Operator Patterns

8.5.1 Namespace-as-a-Service Operators

Operators that provision per-tenant namespaces:

# Operator creates namespace and binds tenant identity
apiVersion: v1
kind: Namespace
metadata:
  name: tenant-acme
  labels:
    tenant: acme
    managed-by: tenant-operator
---
# Tenant-scoped Vault policy created by operator
# Path: secret/data/tenants/acme/*

8.5.2 Tenant Identity Isolation

IsolationImplementation
NamespaceKubernetes namespace per tenant
RBACTenant-scoped Roles, no cluster access
SecretsTenant-prefixed Vault paths
NetworkNetworkPolicies isolate tenants
SPIFFETenant in SPIFFE ID path

8.5.3 Operator-Created Identity

When operators create resources that need identity:


8.6 Security Considerations

8.6.1 Privilege Escalation Prevention

Operators must not grant more permissions than they have:

RuleEnforcement
RBAC escalate verb restrictedAdmission webhook
bind verb restrictedAdmission webhook
ClusterRoleBinding creation restrictedOPA/Kyverno policy

8.6.2 Operator Security Posture

Security ControlImplementation
Pod securityRestricted PSS
Network isolationNetworkPolicies
Resource limitsResourceQuotas
AuditKubernetes audit + Vault audit

8.6.3 Compromised Operator Impact

Operator TypeBlast RadiusMitigation
Cluster-wideAll namespacesDefense in depth, audit
Namespace-scopedSingle namespaceLimit scope
Multi-tenantAll tenantsTenant isolation boundaries

8.7 Compliance Mapping

8.7.1 Invariant Enforcement

InvariantOperator Implementation
INV-1ServiceAccount with projected token
INV-2Vault tokens ≤ 1h, renewed
INV-4All Vault access via Kubernetes auth
INV-7RBAC scoped to required namespaces
INV-9Operators cannot escalate privileges

8.7.2 Audit Trail

EventAudit Source
Operator reconciliationController logs
Resource creationKubernetes audit
Secret accessVault audit
Cross-namespace accessKubernetes audit

Document Navigation


End of Section 8