Deploy with Helm
The helm/main chart deploys Krypton via the helm/krypton subchart, with
templated RBAC scope, restart wiring, access config, and NetworkPolicy.
Minimal values
# Minimal Helm values for Krypton (chart: helm/main, subchart: helm/krypton).
# Secret values (OIDC_CLIENT_SECRET, SESSION_SECRET) belong in a Kubernetes
# Secret referenced by envSecretName — never in values files.
global:
env: {}
krypton:
enabled: true
replicaCount: 2
# kubectl create secret generic krypton-oidc -n krypton \
# --from-literal=OIDC_CLIENT_SECRET='...' \
# --from-literal=SESSION_SECRET="$(openssl rand -hex 32)"
envSecretName: krypton-oidc
env:
KRYPTON_SCOPE: namespace
KRYPTON_ETCD_ENCRYPTED: 'false' # set 'true' once apiserver encryption is on
KRYPTON_ADMIN_GROUPS: krypton-admin
OIDC_ISSUER: https://keycloak.example.com/realms/company
OIDC_CLIENT_ID: krypton
OIDC_SCOPES: openid profile email groups
NEXT_PUBLIC_APP_URL: https://krypton.example.com
# Derives the default KRYPTON_SCOPE (explicit entries must match):
# namespace (default) or cluster. Curated extras add a derived allowlist.
rbac:
create: true
scope: namespace
# "Restart apps" (API + UI + apps RBAC rule). Default false.
restartEnabled: false
# Namespace fallback rules (krypton-access / access.json).
accessConfig:
enabled: false
networkPolicy:
enabled: true
# MUST be true on OpenShift (routes arrive from openshift-ingress).
allowOpenShiftIngress: false
Create the referenced Secret out-of-band (never commit values):
kubectl create secret generic krypton-oidc -n krypton \
--from-literal=OIDC_CLIENT_SECRET='...' \
--from-literal=SESSION_SECRET="$(openssl rand -hex 32)"
Key knobs
| Value | Purpose |
|---|---|
krypton.env | Non-secret Krypton config (full reference: app/.env.example) |
krypton.envSecretName | K8s Secret with OIDC_CLIENT_SECRET + SESSION_SECRET |
krypton.rbac.scope | namespace (default) or cluster — the chart derives KRYPTON_SCOPE from it (explicit entries must match) |
krypton.rbac.extraNamespaces | Curated extra namespaces (namespace scope): Roles plus a derived KRYPTON_NAMESPACES allowlist, no ClusterRole |
krypton.restartEnabled | Single effective value wiring KRYPTON_RESTART_ENABLED and the apps RBAC rule together |
krypton.accessConfig.enabled | Renders the access ConfigMap and derives KRYPTON_ACCESS_CONFIGMAP from its name (access.json key) |
krypton.replicaCount | Stateless — more than one replica is always safe (default 2) |
krypton.ingress.enabled / route.enabled / httpRoute.enabled | Expose via Ingress / OpenShift Route / Gateway API HTTPRoute |
networkPolicy.enabled | Locks down pod traffic (DNS + K8s API egress allowed) |
networkPolicy.allowOpenShiftIngress | Must be true on OpenShift (routes arrive from openshift-ingress) |
See helm/main/values-example.yaml and helm/main/values-k8s-prod.example.yaml
for fuller examples, including topology spread and IdP egress CIDRs.
RBAC scope
One effective configuration (krypton.effectiveConfig in the subchart) derives
the chart-managed runtime env from the RBAC/feature values, so the rendered
Deployment env, Role/ClusterRole scope, subjects, and apps permissions always
agree. Local krypton.env wins over global.env per key. Omit the managed keys
(KRYPTON_SCOPE, KRYPTON_NAMESPACES, KRYPTON_RESTART_ENABLED,
KRYPTON_ACCESS_CONFIGMAP) to use the derived defaults; an explicit entry must
equal the derived effective value, otherwise rendering fails with actionable
guidance instead of deploying disagreeing runtime/RBAC behavior.
namespace: own namespace only (least privilege, the default). The chart derivesKRYPTON_SCOPE=namespaceand renders a Role + RoleBinding.namespace+krypton.rbac.extraNamespaces: [team-a]: curated multi-namespace access. The chart renders a Role + RoleBinding pair per entry (subject stays this release's ServiceAccount) and derivesKRYPTON_SCOPE=clusterwith the bounded allowlistKRYPTON_NAMESPACES=<release-namespace>,team-a— served by these namespaced Roles only, no ClusterRole is rendered. Previously the extra Roles were unusable because the namespace runtime scope only exposed the pod namespace.cluster: centralized dashboard (KRYPTON_SCOPE=cluster); narrow withKRYPTON_NAMESPACES="team-a,team-b"inkrypton.env.extraNamespacesis rejected with cluster scope.rbac.create: false: RBAC is managed out-of-band. SetKRYPTON_SCOPE/KRYPTON_NAMESPACESinkrypton.envyourself to match your external grants (rbac.scope/extraNamespacesare then ignored), and grant theappspermissions yourself to matchkrypton.restartEnabled.
Restart + access config in one place
Unlike the raw manifests (where the flag and the RBAC rule are separate manual
steps), the chart wires them together: krypton.restartEnabled is the single
effective value driving both the KRYPTON_RESTART_ENABLED runtime flag
and the apps RBAC rule (an explicit env entry must parse to the same
value — runtime truthy strings are 1/true/yes/on — or rendering fails).
Similarly, krypton.accessConfig.enabled renders the namespace fallback
ConfigMap instead of applying app/k8s/configmap-access.yaml by hand, and
derives KRYPTON_ACCESS_CONFIGMAP from accessConfig.name so the runtime
policy reader finds it. The key must stay access.json (hardcoded by the
reader); with accessConfig.enabled: false, point KRYPTON_ACCESS_CONFIGMAP
at an externally managed ConfigMap. extraEnv entries must not set managed
keys (they render last and would shadow the derived values).
NetworkPolicy notes
- Pod DNS (UDP/TCP 53) is allowed for both upstream Kubernetes (
kube-system) and OpenShift (openshift-dns). - Egress to the Kubernetes API (
https://kubernetes.default.svc, a ClusterIP) is allowed on ports 443/6443 viaallowKubeApiEgress. - Standard NetworkPolicy cannot match DNS names: add your IdP's CIDR ranges
under
networkPolicy.krypton.idpEgressIpBlockswhen you tighten API egress. - On OpenShift,
allowOpenShiftIngressmust betrueor all ingress is denied.
Installing with the chart Makefile
helm/main/Makefile wraps the standard flow. Secret values live in a
values-<namespace>.secret.yaml file (see values-ai-poc.secret.yaml for the
shape; never commit real ones):
cd helm/main
make upgrade NAMESPACE=krypton IMAGE_TAG=0.1.0 # helm dependency update + upgrade --install
make lint NAMESPACE=krypton IMAGE_TAG=0.1.0 # render + validate
make template NAMESPACE=krypton IMAGE_TAG=0.1.0 # render to template.yaml
The packaged subchart (helm/main/charts/krypton-0.1.0.tgz) is refreshed by the
helm-dep step (helm dependency update) before every install, upgrade, lint,
and template run.
OCI chart releases
Every v*.*.* tag publishes the helm/krypton chart to
oci://ghcr.io/egose/charts/krypton (see the Publish Helm chart workflow).
Chart version and appVersion are both set from the release tag, so chart
1.2.3 deploys app 1.2.3 (ghcr.io/egose/krypton:1.2.3 by default —
override with image.tag).
Install it directly, without the umbrella chart:
helm install krypton oci://ghcr.io/egose/charts/krypton --version 1.2.3 \
--namespace krypton --create-namespace
Or consume it as a dependency (helm registry login ghcr.io first if the
package is private):
# Chart.yaml
dependencies:
- name: krypton
version: '1.2.3'
repository: oci://ghcr.io/egose/charts
# values.yaml
krypton:
enabled: true
env:
KRYPTON_SCOPE: namespace
See helm/krypton/README.md for the full values reference (image, exposure,
autoscaling, PDB, security contexts, extra volumes).
Exposure and health
- One of
ingress,route(OpenShift), orhttpRoute(Gateway API) exposes the ClusterIP Service — enable exactly what your platform provides. - Liveness and readiness probes hit
/api/config: unauthenticated and K8s/OIDC-free, so it is safe to probe (the raw manifests inapp/k8s/probe the same endpoint). - The ServiceAccount token must stay auto-mounted
(
serviceAccount.automount: true) — Krypton talks to the Kubernetes API with its own token.
Chart CI
The Helm workflow lints both charts and templates them on every helm/**
change, including a full-flag matrix for the subchart (cluster scope,
restart, ingress + route + HTTPRoute, access config, autoscaling, PDB,
external ServiceAccount) and both umbrella example values files. It also runs
the rendered-object regressions in helm/krypton/tests/render-regressions.sh
(default, cluster, curated namespaces, local/global conflicts, false
overrides, external RBAC, custom access config, and every contradiction
error). Run the same locally with helm lint / helm template and the
regression script before opening a PR.