Skip to main content

Deploy ThunderID as an OpenChoreo Resource

This guide walks you through running ThunderID as an OpenChoreo Resource. A platform administrator registers the ResourceType once, and teams then create a Resource from it to provision a ThunderID instance.

Key characteristics of this deployment model:

  • Provisioning is fully declarative. Every ThunderID resource, organization units, user types, applications, flows, themes, and users, comes from a single resources file supplied at Resource creation time.
  • Sensitive values are materialized from the platform secret store by External Secrets Operator.
  • The database backend is a parameter. The default sqlite mode needs no external database; postgres mode targets an externally hosted PostgreSQL instance.

Prerequisites

Before you begin, ensure the following are available:

Infrastructure:

  • A running Kubernetes cluster with OpenChoreo installed (control plane and data plane).
  • External Secrets Operator with a ClusterSecretStore (or namespaced SecretStore) configured for the data plane. The OpenChoreo quick-start installs one backed by OpenBao.
  • For postgres mode only: an accessible PostgreSQL instance, in-cluster or external.

Required Tools:

ToolInstallation GuideVersion Check
GitInstall Gitgit --version
HelmInstall Helmhelm version
kubectlInstall kubectlkubectl version
OpenChoreoInstall OpenChoreokubectl get crd | grep openchoreo

Verify cluster access and the required custom resource definitions before proceeding:

kubectl cluster-info
kubectl get crd | grep -E 'resourcetypes|resources|resourcereleases|resourcereleasebindings'
kubectl get clustersecretstore

How It Works

  1. The thunderid-oc-resourcetype chart registers the thunderid ClusterResourceType, which holds the schema and the Kubernetes templates.
  2. You create a Resource that references the ClusterResourceType and supplies the instance parameters. OpenChoreo cuts a ResourceRelease from it.
  3. A ResourceReleaseBinding pins the ResourceRelease to an environment.
  4. OpenChoreo renders the workload into the data-plane namespace: an ExternalSecret materialized from the platform secret store, the configuration ConfigMap resources, the Deployment, the Service, and the HTTPRoute.

Install the ResourceType

A platform administrator installs the chart once per cluster:

helm install thunderid-type install/openchoreo/thunderid-oc-resourcetype

This registers the thunderid ClusterResourceType. To register a namespaced ResourceType instead, set resourceType.cluster=false.

Generate the Key Material

ThunderID needs an AES encryption key and two token-signing key pairs, one RSA and one ECDSA. The container image ships no key material, so generate it before the first deployment. Self-signed signing pairs are fine here; these keys sign tokens, they do not terminate TLS. The signing certificates must carry a subject alternative name, which the OpenID4VP verifier requires at startup.

# AES encryption key (64 hex characters)
openssl rand -hex 32 > crypto.key

# RSA signing pair
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout signing.key -out signing.cert \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

# ECDSA signing pair
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-signing.key
openssl req -new -x509 -nodes -days 3650 -key ecdsa-signing.key \
-out ecdsa-signing.cert \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

Store the Environment Values

Push the sensitive values, together with the encryption key and signing key files, to the platform secret store as properties of one secret per environment. Scalar values such as ADMIN_PASSWORD become container environment variables; the crypto.key, certificate, and signing-key properties are mounted as files under config/certs/. The following example uses the OpenBao instance installed with the OpenChoreo quick-start:

bao kv put secret/thunderid/dev \
ADMIN_PASSWORD=<admin-password> \
crypto.key="$(cat crypto.key)" \
signing.cert="$(cat signing.cert)" signing.key="$(cat signing.key)" \
ecdsa-signing.cert="$(cat ecdsa-signing.cert)" ecdsa-signing.key="$(cat ecdsa-signing.key)"

bao kv put replaces the entire secret, so include every property in a single command.

note

The quick-start OpenBao is not exposed outside the cluster, so run the bao command inside its pod. In development mode the root token is root:

kubectl exec -n openbao openbao-0 -- env BAO_TOKEN=root \
bao kv put secret/thunderid/dev \
ADMIN_PASSWORD=<admin-password> \
crypto.key="$(cat crypto.key)" \
signing.cert="$(cat signing.cert)" signing.key="$(cat signing.key)" \
ecdsa-signing.cert="$(cat ecdsa-signing.cert)" ecdsa-signing.key="$(cat ecdsa-signing.key)"

The $(cat ...) expansions run in your local shell, so the key files are read locally and their contents passed to bao inside the pod.

For postgres mode, add the database connection properties for each of the four databases:

bao kv put secret/thunderid/dev \
ADMIN_PASSWORD=<admin-password> \
crypto.key="$(cat crypto.key)" \
signing.cert="$(cat signing.cert)" signing.key="$(cat signing.key)" \
ecdsa-signing.cert="$(cat ecdsa-signing.cert)" ecdsa-signing.key="$(cat ecdsa-signing.key)" \
DB_CONFIG_HOSTNAME=<db-host> DB_CONFIG_PORT=5432 DB_CONFIG_NAME=configdb \
DB_CONFIG_USERNAME=<db-user> DB_CONFIG_PASSWORD=<db-password> DB_CONFIG_SSLMODE=require \
# ... repeat the six properties for DB_RUNTIME_TRANSIENT_*, DB_ENTITY_*, and DB_RUNTIME_PERSISTENT_*

The ResourceType renders an ExternalSecret that extracts every property at the store path. Scalar values are injected into the container environment, where ThunderID resolves the {{.VAR}} placeholders in its configuration at startup; the crypto.key, certificate, and signing-key properties are mounted as files under config/certs/.

note

Populate every value before the first deployment. Because the rendered release name is stable, an ExternalSecret created on the first deployment does not pick up later store changes until its refresh interval elapses.

Create the Resource

Use the sample manifests at install/openchoreo/thunderid-oc-resourcetype/samples/resource.yaml as a starting point. Replace the <SERVER_PUBLIC_URL> and <GATEWAY_HOSTNAME> placeholders, then apply:

kubectl apply -f install/openchoreo/thunderid-oc-resourcetype/samples/resource.yaml

A minimal Resource references the secret store and provides the declarative resources file:

apiVersion: openchoreo.dev/v1alpha1
kind: Resource
metadata:
name: thunderid
namespace: default
spec:
owner:
projectName: default
type:
kind: ClusterResourceType
name: thunderid
parameters:
secretStore:
name: default
key: thunderid/dev
declarativeResources: |
# Multi-document resources file — organization units, user types,
# applications, flows, themes, and users. Export one from an existing
# instance through the /export API and paste it here.

Applying the Resource cuts a ResourceRelease automatically.

note

The sample derives both the System resource server identifier and the Console's resource indicator from the server public URL, as <SERVER_PUBLIC_URL>/mcp. Substituting <SERVER_PUBLIC_URL> keeps the two aligned, so the Console's token requests match the registered resource server. Keep them equal if you customize either.

note

Exports strip user credentials. Add a credentials: block to each user that must sign in, and resolve the password from the secret store so it never appears in the manifest:

credentials:
password: "{{.ADMIN_PASSWORD}}"

Deploy to an Environment

Applying the sample manifest in the previous step also created a ResourceReleaseBinding named thunderid-development. This binding deploys the release to the development environment, and stays pending until you pin a release to it.

List the releases cut from the Resource, then pin the one you want to the binding:

kubectl get resourcerelease -n default

kubectl patch resourcereleasebinding thunderid-development -n default \
--type=merge -p '{"spec":{"resourceRelease":"<release-name>"}}'

Use the release name from the kubectl get resourcerelease output as <release-name>.

Verify the Deployment

# Binding status
kubectl get resourcereleasebinding thunderid-development -n default \
-o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'

# Rendered workload in the data-plane namespace
kubectl get all,externalsecret -n <data-plane-namespace>

The ThunderID server is reachable at the external gateway hostname the HTTPRoute generates, in the form <environmentName>-<resourceName>-<resourceNamespace>.<gateway-host>. The <gateway-host> comes from the data plane's gateway.ingress.external.http (or .https) host.

Database Modes

Set the database engine through the runtime.dbType parameter.

SQLite (Default)

sqlite mode uses the database files bundled in the container image. It needs no external database and no DB_* properties in the secret store.

warning

SQLite storage is pod-local and ephemeral. Runtime data, sessions, tokens, and database-backed stores, is lost on pod restart, and replicas must stay at 1. Use SQLite for development and evaluation only.

PostgreSQL

postgres mode targets an externally hosted PostgreSQL instance. Set runtime.dbType: postgres, and provide the DB_* properties in the secret store.

Prepare the PostgreSQL instance before deployment:

  1. Create the four required databases:

    CREATE DATABASE configdb;
    CREATE DATABASE runtime_transient;
    CREATE DATABASE entitydb;
    CREATE DATABASE runtime_persistent;
  2. Load the schema into each database. The scripts ship in the ThunderID repository under backend/dbscripts; clone it if you have not already. Run each script against its database:

    psql -h <db-host> -U <db-user> -d configdb    -f backend/dbscripts/configdb/postgres.sql
    psql -h <db-host> -U <db-user> -d runtime_transient -f backend/dbscripts/runtime_transient/postgres.sql
    psql -h <db-host> -U <db-user> -d entitydb -f backend/dbscripts/entitydb/postgres.sql
    psql -h <db-host> -U <db-user> -d runtime_persistent -f backend/dbscripts/runtime_persistent/postgres.sql
warning

Load the schema scripts from the same version as the container image set in parameters.image. A mismatch between the schema and the running server causes startup or runtime failures.

Certificates, TLS, and Configuration Overrides

The ResourceType exposes further parameters for production hardening:

  • runtime.tls.enabled serves HTTPS from the container and configures the gateway to originate TLS to the backend. It requires the serving pair (server.cert / server.key) as additional properties at the store path.
  • runtime.certs.storeKey reads the certificate and key files from a separate store entry instead of secretStore.key, for independent rotation or access control.
  • configOverrides replaces the generated deployment.yaml, Gate config.js, or Console config.js files when the built-in templates do not cover a setting.

For the full parameter reference, see the chart documentation at install/openchoreo/thunderid-oc-resourcetype/README.md.

Environment Management

OpenChoreo provides built-in environment management with promotion workflows. Three environments are available out of the box:

  1. Development: for active development and testing.
  2. Staging: for pre-production validation.
  3. Production: for live traffic.

To promote ThunderID to another environment, create a ResourceReleaseBinding for that environment, provide its secret store values, and pin the same release.

Next Steps

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.