Quick Start: Run Nauthera on kind
Docs version v1 (main)Nauthera is pre-1.0, in a design-partner programUpdated
This guide installs the published Helm chart with the server and its operator, then
creates an account, registers a client with an OIDCClient manifest and opens the admin
dashboard. It needs no source checkout and no registry credentials: the chart and both
images are public on GHCR. Run it on a local kind cluster with a throwaway PostgreSQL,
or on a cluster you already run.
Prerequisites
kubectland Helm 3 or 4,curlandjq- On a laptop: Docker and kind
- On your own cluster: what that section lists
1. Start the stack
On a laptop, follow On kind. If you already run a cluster, skip to On a cluster you already run.
On kind
Create a cluster that publishes the server's NodePort on https://localhost:8443:
# kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: nauthera
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30443
hostPort: 8443
listenAddress: "127.0.0.1"kind create cluster --config kind.yaml
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager -n cert-manager \
--create-namespace --set crds.enabled=true --wait
kubectl create namespace nautheraThen a PostgreSQL whose data lives in an emptyDir, and the Secret the chart reads its
connection from (nauthera-pg-owner, key uri):
# postgres.yaml — throwaway: the data is gone with the pod
apiVersion: v1
kind: Secret
metadata:
name: nauthera-pg-owner
namespace: nauthera
stringData:
password: local-only
uri: postgres://nauthera:local-only@postgres.nauthera.svc:5432/nauthera?sslmode=disable
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: nauthera
spec:
selector:
matchLabels: { app: postgres }
template:
metadata:
labels: { app: postgres }
spec:
containers:
- name: postgres
image: postgres:17
env:
- { name: POSTGRES_USER, value: nauthera }
- { name: POSTGRES_DB, value: nauthera }
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef: { name: nauthera-pg-owner, key: password }
ports:
- containerPort: 5432
volumeMounts:
- { name: data, mountPath: /var/lib/postgresql/data }
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: nauthera
spec:
selector: { app: postgres }
ports:
- port: 5432kubectl apply -f postgres.yaml
kubectl -n nauthera rollout status deploy/postgresInstall the chart with its operator.yaml profile: one replica that migrates the
database on start, no Redis, TLS from a CA the chart creates, self-registration on, and
the operator talking to the server over mutual TLS. The profile is written for images
loaded into kind (pullPolicy: Never), so let the kubelet pull the released ones, which
the chart pins by digest:
VERSION=<version> # the newest is at the top of https://www.nauthera.io/downloads
helm pull oci://ghcr.io/nauthera/charts/nauthera-server --version "$VERSION" --untar
helm install nauthera oci://ghcr.io/nauthera/charts/nauthera-server --version "$VERSION" \
-n nauthera -f nauthera-server/profiles/operator.yaml \
--set image.pullPolicy=IfNotPresent --set operator.image.pullPolicy=IfNotPresent
kubectl -n nauthera rollout status deploy/nauthera-nauthera-server| Service | URL | Notes |
|---|---|---|
| nauthera-server | https://localhost:8443 | Public OAuth 2.0 / OIDC surface and sign-in UI |
| admin dashboard | https: | For an account that holds an admin role; step 5 grants one |
The certificate comes from the chart's own CA, which nothing trusts yet. Save it, so
curl and your applications can trust it, and add it to your browser or click through
the warning:
kubectl -n nauthera get secret nauthera-nauthera-server-ca \
-o jsonpath='{.data.tls\.crt}' | base64 -d > nauthera-local-ca.crtThe chart's install notes (helm get notes nauthera -n nauthera) show how to add it to
the macOS and Debian trust stores, and how to remove it again.
On a cluster you already run
Nauthera needs a few things beside it that this guide does not install, because most clusters already have their own way of running them:
| Dependency | What Nauthera needs | We suggest |
|---|---|---|
| cert-manager | Issues the token signing keys, the control plane's mutual TLS and, with an issuer of yours, the public certificate | cert-manager with a ClusterIssuer, such as Let's Encrypt |
| PostgreSQL | The only database; a Secret with its connection URI | CloudNativePG: its Cluster publishes the <cluster>-app Secret with a uri key |
| Redis | Required for any issuer that is not localhost; must not evict keys (maxmemory-policy noeviction) | The OT-Container-Kit Redis operator, with a password Secret |
| Ingress or Gateway | Routes the public hostname to the server | The ingress controller or Gateway API implementation you already run |
Setting these up is out of scope here; Installation and
Operational requirements say what each
must provide in production. With them in place, create the namespace and the two
encryption keys the server needs on an issuer that is not localhost:
kubectl create namespace nauthera
kubectl -n nauthera create secret generic nauthera-keys \
--from-literal=mfa-encryption-key="$(openssl rand -hex 32)" \
--from-literal=keys-encryption-key="$(openssl rand -hex 32)"Back up nauthera-keys: without it, the MFA secrets and every organization's sealed keys
cannot be read again. Then write the values that name your hostname and the Secrets.
They go on top of the chart's production profile:
# my-values.yaml
ingress:
host: auth.example.com # becomes the issuer: https://auth.example.com
className: nginx # your ingress class
certManager:
issuerRef:
name: letsencrypt # your ClusterIssuer, for the public certificate
kind: ClusterIssuer
grpcTLS:
enabled: true # the operator calls the control plane over mutual TLS
operator:
enabled: true
mainOrganizationNamespaces: [nauthera]
config:
cache:
address: nauthera-redis:6379 # your Redis Service
tls: false # true when your Redis serves TLS
server:
grpc:
enabled: true
auth:
registration:
enabled: true # for this guide's step 3; turn it off afterwards
secrets:
databaseDsn:
existingSecret: nauthera-pg-app # CloudNativePG's <cluster>-app Secret
existingSecretKey: uri
cachePassword:
existingSecret: nauthera-redis # the Secret holding your Redis password
existingSecretKey: password
mfaEncryptionKey:
existingSecret: nauthera-keys
keysEncryptionKey:
existingSecret: nauthera-keysBehind a Gateway instead of an ingress, replace the ingress block:
ingress:
enabled: false
httpRoute:
enabled: true
parentRefs:
- name: public # your Gateway
namespace: gateway
hostnames: [auth.example.com]
config:
issuer:
url: https://auth.example.comThe production profile runs three replicas, trusts proxies in the private address
ranges, and turns on a NetworkPolicy, a ServiceMonitor and alerting rules; the
ServiceMonitor and rules need the Prometheus Operator's CRDs, so set
metrics. and metrics. to false without
them. Install:
VERSION=<version> # the newest is at the top of https://www.nauthera.io/downloads
helm pull oci://ghcr.io/nauthera/charts/nauthera-server --version "$VERSION" --untar
helm install nauthera oci://ghcr.io/nauthera/charts/nauthera-server --version "$VERSION" \
-n nauthera -f nauthera-server/profiles/production.yaml -f my-values.yaml
kubectl -n nauthera rollout status deploy/nauthera-nauthera-serverIn the steps below, use your issuer, https://auth.example.com, wherever they say
https://localhost:8443, and leave out --cacert nauthera-local-ca.: your
ClusterIssuer serves a certificate your machine already trusts. For step 5, add
-f my-values.yaml to the helm upgrade, or keep --reuse-values.
2. Check the issuer
curl -s --cacert nauthera-local-ca.crt https://localhost:8443/.well-known/openid-configuration \
| jq '{issuer, grant_types_supported}'The issuer is https://localhost:8443. The advertised grant types are
authorization_code, refresh_token, client_credentials and
urn:.
3. Create an account
Open https: and register. Registration asks for the
built-in proof-of-work captcha, which the browser solves on its own. You then land on
the hosted sign-in pages, signed in to the main organization.
4. Register your own client
The operator registers each OIDCClient with the server and writes the generated
secret into a Secret beside it. The profile lets manifests in the nauthera namespace
configure the main organization:
# my-app.yaml
apiVersion: nauthera.io/v1alpha1
kind: OIDCClient
metadata:
name: my-app
namespace: nauthera
spec:
clientId: my-app
displayName: My App
redirectUris:
- http://localhost:5173/callback
grantTypes:
- authorization_code
- refresh_token
responseTypes:
- code
scopes:
- openid
- profile
- email
- offline_access
tokenEndpointAuthMethod: client_secret_basic
pkcePolicy: required_s256
allowRefresh: true
secret:
generate: truekubectl apply -f my-app.yaml
kubectl -n nauthera get oidcclients # wait for Ready
kubectl -n nauthera get secret my-app -o jsonpath='{.data.client-secret}' | base64 -dThe Secret my-app holds client-id, client-secret and issuer; mount it into the
application, or read it as above. Re-applying the manifest never rotates the secret.
For a public client, set tokenEndpointAuthMethod: and leave out secret. Then
point any OpenID Connect library at the issuer https://localhost:8443, trusting
nauthera-local-ca.crt. OIDCClient has every field.
5. Open the admin dashboard
The dashboard admits an account that holds an admin role, and the server grants one to
an account that already exists and is named in auth.admin.subjects. Name the account
you registered in step 3:
helm upgrade nauthera oci://ghcr.io/nauthera/charts/nauthera-server --version "$VERSION" \
-n nauthera --reuse-values --set 'config.auth.admin.subjects={you@example.com}'
kubectl -n nauthera rollout status deploy/nauthera-nauthera-serverThe new pod grants that account platform-owner, and https:
opens for it (The first administrator).
6. Clean up
On kind, delete the cluster:
kind delete cluster --name nautheraOn your own cluster, turn self-registration off again
(config.) or remove the release with
helm uninstall nauthera -n nauthera. The resource definitions stay, so the
OIDCClients you declared are kept.
Next steps
- Grafana sign-in: an application signing in through
Nauthera, with its
OIDCClientand the Secret the operator writes. - Installation: the production profile, with Redis, replicas, an ingress and cert-manager issuing from your own CA.
- Clients: every way to register an application.
- Organizations: add tenants with their own issuers.