387 lines
13 KiB
YAML
387 lines
13 KiB
YAML
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. 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`
|
|
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: 8080
|
|
name: auth
|
|
protocol: TCP
|
|
env:
|
|
- name: AUTH_LISTEN_ADDR
|
|
value: ":8080"
|
|
- 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: 8080
|
|
initialDelaySeconds: 2
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8080
|
|
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`
|
|
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: 8080
|
|
name: auth
|
|
protocol: TCP
|
|
env:
|
|
- name: AUTH_MODE
|
|
value: "oidc"
|
|
- name: AUTH_LISTEN_ADDR
|
|
value: ":8080"
|
|
- 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: 8080
|
|
initialDelaySeconds: 2
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8080
|
|
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`
|
|
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: 8080
|
|
name: auth
|
|
protocol: TCP
|
|
env:
|
|
- name: AUTH_MODE
|
|
value: "mcp"
|
|
- name: AUTH_LISTEN_ADDR
|
|
value: ":8080"
|
|
- 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: 8080
|
|
initialDelaySeconds: 2
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8080
|
|
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
|
|
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: 8080
|
|
protocol: TCP
|