apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: inject-auth-sidecar annotations: pod-policies.kyverno.io/autogen-controllers: none policies.kyverno.io/title: Inject Auth Sidecar policies.kyverno.io/minversion: 1.6.0 policies.kyverno.io/category: Security policies.kyverno.io/severity: medium policies.kyverno.io/subject: Pod policies.kyverno.io/description: >- Injects an auth sidecar container into Pods annotated with policies.forteapps.io/auth: "true". Supports three auth modes controlled by the policies.forteapps.io/auth-type annotation: "token" (default), "oidc", and "mcp". In token mode the sidecar reads credentials from a mounted Secret volume. In OIDC mode the sidecar uses OpenID Connect with authority and client-id provided via required annotations (policies.forteapps.io/auth-oidc-authority and policies.forteapps.io/auth-oidc-client-id) and secrets from an auth-oidc Secret. In MCP mode the sidecar implements OAuth 2.0 for MCP servers per RFC 9728 (Protected Resource Metadata) and RFC 7591 (Dynamic Client Registration), configured via policies.forteapps.io/auth-mcp-resource and policies.forteapps.io/auth-mcp-authority annotations. The sidecar port defaults to 8080 and can be overridden via the policies.forteapps.io/auth-port annotation. A NetworkPolicy is generated to restrict ingress to the sidecar port only. spec: background: false rules: - name: generate-auth-tokens-secret skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring preconditions: all: - key: "{{ request.operation }}" operator: In value: - CREATE - key: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-type\" || 'token' }}" operator: Equals value: "token" generate: synchronize: false apiVersion: v1 kind: Secret name: auth-tokens namespace: "{{ request.namespace }}" data: metadata: labels: app.kubernetes.io/managed-by: kyverno app.kubernetes.io/created-by: inject-auth-sidecar type: Opaque data: {} - name: generate-auth-oidc-secret skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" policies.forteapps.io/auth-type: "oidc" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring preconditions: all: - key: "{{ request.operation }}" operator: In value: - CREATE generate: synchronize: false apiVersion: v1 kind: Secret name: auth-oidc namespace: "{{ request.namespace }}" data: metadata: labels: app.kubernetes.io/managed-by: kyverno app.kubernetes.io/created-by: inject-auth-sidecar type: Opaque data: {} - name: inject-sidecar-token skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring preconditions: all: - key: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-type\" || 'token' }}" operator: Equals value: "token" context: - name: appPort variable: jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000` - name: sidecarPort variable: jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '8080') mutate: patchStrategicMerge: spec: containers: - name: authn image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/fortedigital/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}" ports: - containerPort: "{{ sidecarPort }}" name: auth protocol: TCP env: - name: AUTH_LISTEN_ADDR value: ":{{ sidecarPort }}" - name: AUTH_UPSTREAM_URL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}" - name: AUTH_PUBLIC_PATHS value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}" - name: AUTH_TOKEN_FILE value: "/etc/auth/tokens" - name: AUTH_MODE value: "token" volumeMounts: - name: auth-tokens mountPath: /etc/auth readOnly: true resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi readinessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 2 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 5 periodSeconds: 10 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumes: - name: auth-tokens secret: secretName: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-token-secret-name\" || 'auth-tokens' }}" optional: true - name: inject-sidecar-oidc skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" policies.forteapps.io/auth-type: "oidc" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring context: - name: appPort variable: jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000` - name: sidecarPort variable: jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '8080') mutate: patchStrategicMerge: spec: containers: - name: authn image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/fortedigital/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}" imagePullPolicy: Always ports: - containerPort: "{{ sidecarPort }}" name: auth protocol: TCP env: - name: AUTH_MODE value: "oidc" - name: AUTH_LISTEN_ADDR value: ":{{ sidecarPort }}" - name: AUTH_LOG_LEVEL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}" - name: AUTH_UPSTREAM_URL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}" - name: AUTH_OIDC_AUTHORITY value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-authority\" }}" - name: AUTH_OIDC_CLIENT_ID value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-client-id\" }}" - name: AUTH_OIDC_CALLBACK_URL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-callback-path\" }}" - name: AUTH_OIDC_CALLBACK_PATH value: "{{ regex_replace_all('https?://[^/]*', request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-callback-path\", '') }}" - name: AUTH_OIDC_SCOPES value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-scopes\" || 'openid,profile,email' }}" - name: AUTH_PUBLIC_PATHS value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}" - name: AUTH_OIDC_COOKIE_SECRET valueFrom: secretKeyRef: name: auth-oidc key: cookie-secret - name: AUTH_OIDC_CLIENT_SECRET valueFrom: secretKeyRef: name: auth-oidc key: client-secret resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi readinessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 2 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 5 periodSeconds: 10 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL - name: inject-sidecar-mcp skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" policies.forteapps.io/auth-type: "mcp" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring context: - name: appPort variable: jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000` - name: sidecarPort variable: jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '8080') mutate: patchStrategicMerge: spec: containers: - name: authn image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/fortedigital/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}" imagePullPolicy: Always ports: - containerPort: "{{ sidecarPort }}" name: auth protocol: TCP env: - name: AUTH_MODE value: "mcp" - name: AUTH_LISTEN_ADDR value: ":{{ sidecarPort }}" - name: AUTH_LOG_LEVEL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}" - name: AUTH_UPSTREAM_URL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}" - name: AUTH_MCP_RESOURCE value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-resource\" }}" - name: AUTH_MCP_AUTHORIZATION_SERVERS value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-authority\" }}" - name: AUTH_PUBLIC_PATHS value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}" - name: AUTH_MCP_SCOPES_SUPPORTED value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-scopes\" || 'read,write' }}" resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi readinessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 2 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 5 periodSeconds: 10 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL - name: inject-sidecar-oauth skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" policies.forteapps.io/auth-type: "oauth" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring context: - name: appPort variable: jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000` - name: sidecarPort variable: jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '8080') mutate: patchStrategicMerge: spec: containers: - name: authn image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/fortedigital/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}" imagePullPolicy: Always ports: - containerPort: "{{ sidecarPort }}" name: auth protocol: TCP env: - name: AUTH_MODE value: "oauth" - name: AUTH_LISTEN_ADDR value: ":{{ sidecarPort }}" - name: AUTH_LOG_LEVEL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}" - name: AUTH_UPSTREAM_URL value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}" - name: AUTH_OAUTH_AUTHORITY value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-authority\" }}" - name: AUTH_OAUTH_CLIENT_ID value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-client-id\" }}" - name: AUTH_OAUTH_SCOPES value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-scopes\" || 'openid,profile,email' }}" - name: AUTH_OAUTH_DELEGATION_ENABLED value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-enabled\" || 'false' }}" - name: AUTH_OAUTH_DELEGATION_CLIENT_ID value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-client-id\" || '' }}" - name: AUTH_OAUTH_DELEGATION_SCOPES value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-scopes\" || '' }}" - name: AUTH_OAUTH_CLIENT_SECRET valueFrom: secretKeyRef: name: auth-oauth key: client-secret - name: AUTH_OAUTH_DELEGATION_CLIENT_SECRET valueFrom: secretKeyRef: name: auth-oauth key: delegation-client-secret resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi readinessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 2 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: "{{ sidecarPort }}" initialDelaySeconds: 5 periodSeconds: 10 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL - name: generate-auth-network-policy skipBackgroundRequests: true match: any: - resources: kinds: - Pod annotations: policies.forteapps.io/auth: "true" exclude: any: - resources: namespaces: - kube-system - kyverno - argocd - cert-manager - monitoring preconditions: all: - key: "{{ request.operation }}" operator: In value: - CREATE context: - name: sidecarPort variable: jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '8080') generate: synchronize: false apiVersion: networking.k8s.io/v1 kind: NetworkPolicy name: "{{ request.object.metadata.name }}-auth-ingress" namespace: "{{ request.namespace }}" data: metadata: labels: app.kubernetes.io/managed-by: kyverno app.kubernetes.io/created-by: inject-auth-sidecar spec: podSelector: matchLabels: "{{ request.object.metadata.labels }}" policyTypes: - Ingress ingress: - ports: - port: "{{ sidecarPort }}" protocol: TCP