OpenShift Deployment¶
Audience: Administrators deploying Sentari on Red Hat OpenShift
What you need: the OpenShift bundle (download from your Sentari release),
oc + helm 3.8+, a Sentari license key, and pull access to the published
chart and images (or an internal mirror). No source repository required —
the chart is pulled from the OCI registry.
Kubernetes users: See Kubernetes Deployment instead.
Sentari's Helm chart includes OpenShift-specific support: a Route resource
(replacing Ingress) and a pod SecurityContext that omits fixed UIDs so
OpenShift's default restricted-v2 Security Context Constraints (SCC) can run
the workloads without any cluster-admin action.
Contents of this bundle¶
| File | Purpose |
|---|---|
values.yaml |
Evaluation values — bundled TimescaleDB + Redis. |
values-external-db.yaml |
Production values — external managed DB + Redis, NetworkPolicy + PDB on. |
Which values file? Use
values.yamlfor a quick evaluation. Usevalues-external-db.yamlfor production — running stateful DB/Redis images under OpenShift's restricted SCC adds UID complexity that external services avoid.
Prerequisites¶
- OpenShift 4.12+ with
oclogged in as a user who can create a project. - Helm 3.8+ (OCI support).
- A Sentari license key.
- Pull access to
ghcr.io/sentari-dev/sentari/*(or mirror them — see Air-gap / internal registry). - Production: a managed PostgreSQL (TimescaleDB extension recommended) and Redis.
Step 1 — Create the project¶
oc new-project sentari
Step 2 — Edit your values file¶
Open values.yaml (evaluation) or values-external-db.yaml (production) and
replace every REPLACE-... placeholder. Generate the cryptographic values:
# secretKey (64 hex)
python3 -c "import secrets; print(secrets.token_hex(32))"
# configKey (base64, 32 bytes)
python3 -c "import os,base64; print(base64.b64encode(os.urandom(32)).decode())"
# enrollmentToken / passwords
python3 -c "import secrets; print(secrets.token_hex(16))"
Set route.host to your OpenShift apps domain, e.g.
sentari.apps.cluster.example.com.
Step 3 — Authenticate to the chart registry¶
echo "<PULL_TOKEN>" | helm registry login ghcr.io -u <your-username> --password-stdin
(Skip if you mirror the chart internally — see Air-gap / internal registry.)
Step 4 — Install¶
Replace <release> with the Sentari version you are deploying (e.g. 1.0.0).
Evaluation (bundled DB):
helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
--version <release> \
-f values.yaml \
-n sentari
Production (external DB):
helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
--version <release> \
-f values-external-db.yaml \
-n sentari
Production: keep secrets out of Helm history¶
Helm stores -f values in-cluster (helm get values can expose them).
For production, pre-create the Secret and set secrets.existingSecretName
in values-external-db.yaml:
oc create secret generic sentari-credentials -n sentari \
--from-literal=database-url="postgresql+psycopg2://sentari:<pw>@<host>:5432/sentari" \
--from-literal=redis-url="redis://:<pw>@<host>:6379/0" \
--from-literal=secret-key="<64-hex>" \
--from-literal=config-key="<base64-32-byte>" \
--from-literal=enrollment-token="<token>" \
--from-literal=bootstrap-admin-email="admin@example.com" \
--from-literal=bootstrap-admin-password="<pw>" \
--from-literal=license-key="<license>"
Then set secrets.existingSecretName: sentari-credentials and re-run the install.
Using external managed database (alternative install form)¶
helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
--version 1.0.0 \
--set openshift.enabled=true \
--set route.host=sentari.apps.<cluster-domain> \
--set database.external=true \
--set redis.external=true \
--set secrets.databaseUrl="postgresql+psycopg2://sentari:<password>@<host>:5432/sentari" \
--set secrets.redisUrl="redis://:<password>@<host>:6379/0" \
--set secrets.secretKey="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" \
--set secrets.enrollmentToken="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
--set secrets.bootstrapAdminEmail="admin@yourorganisation.com" \
--set secrets.bootstrapAdminPassword="ChangeMe123!" \
--namespace sentari \
--create-namespace
The chart creates the Kubernetes Secret for you from these values — no separate step required.
Step 5 — Verify the deployment¶
oc get pods -n sentari -w # wait for all pods Ready
ROUTE=$(oc get route sentari-api -n sentari -o jsonpath='{.spec.host}')
curl -sf https://$ROUTE/api/health
# Expected: {"status": "ok", "version": "..."}
Open https://$ROUTE and log in with the bootstrap admin credentials from your
values file. Change the password after first login.
Step 6 — Enroll your first agent¶
Install the agent on an endpoint pointed at https://$ROUTE with your
enrollment token — see the Agent deployment guide.
UID / Security Context Constraints¶
When openshift.enabled=true, the chart omits all fixed UIDs and fsGroup from
the application pod SecurityContext (runAsUser, runAsGroup, and fsGroup are
rendered only when openshift.enabled=false). OpenShift's default restricted-v2
SCC then assigns a UID and fsGroup from the namespace's allocated range, and the
image is built to run under any injected UID.
This is the supported posture: no namespace annotation, ServiceAccount SCC annotation,
or other cluster-admin action is required. A plain helm install --set openshift.enabled=true
is admitted by restricted-v2 out of the box.
Bundled StatefulSets: If you deploy the bundled TimescaleDB and Redis StatefulSets, those images expect specific UIDs and may need additional SCC handling. For production OpenShift deployments, using external managed database and Redis services avoids all UID complexity and is the recommended approach.
Proxy configuration¶
If your OpenShift cluster routes outbound traffic through a corporate proxy, configure
the proxy section in your Helm values. This is required for Sentari to reach:
- License server (
license.sentari.dev) — license validation - CVE feeds (
osv-vulnerabilities.storage.googleapis.com) — vulnerability database sync - SIEM webhooks — if configured for alert delivery to an external endpoint
Helm values¶
proxy:
httpProxy: "http://proxy.corp.internal:3128"
httpsProxy: "http://proxy.corp.internal:3128"
noProxy: "localhost,127.0.0.1,.cluster.local"
Or via --set flags:
helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
--version 1.0.0 \
--namespace sentari \
--set proxy.httpProxy="http://proxy.corp.internal:3128" \
--set proxy.httpsProxy="http://proxy.corp.internal:3128" \
...
The chart automatically appends the release-prefixed service names (e.g. sentari-db,
sentari-redis) to NO_PROXY so that internal pod-to-pod traffic is never sent through
the proxy.
If your proxy requires authentication, include credentials in the URL:
proxy:
httpsProxy: "http://username:password@proxy.corp.internal:3128"
Verifying proxy connectivity¶
After deployment, verify the server can reach the license endpoint through the proxy:
oc exec deploy/sentari-api -n sentari -- \
python3 -c "import httpx; print(httpx.get('https://license.sentari.dev/health').status_code)"
Expected: 200.
Key Helm values reference¶
| Value | Default | Description |
|---|---|---|
openshift.enabled |
false |
Enable OpenShift-specific resources (Route; omits fixed UIDs/fsGroup so restricted-v2 admits the pods) |
route.host |
"" |
OpenShift Route hostname |
route.tlsTermination |
edge |
TLS termination: edge, passthrough, or reencrypt |
secrets.existingSecretName |
"" |
Use a pre-created Secret instead of chart values |
secrets.dbPassword |
"" |
PostgreSQL password (bundled DB mode) |
secrets.redisPassword |
"" |
Redis password (bundled Redis mode) |
secrets.secretKey |
"" |
JWT signing key (always required) |
database.external |
false |
Use external DB instead of bundled TimescaleDB |
redis.external |
false |
Use external Redis instead of bundled |
image.registry |
ghcr.io |
Registry for the server image |
api.replicas |
1 |
Number of API pod replicas |
worker.replicas |
1 |
Number of Celery worker replicas |
proxy.httpProxy |
"" |
HTTP proxy URL for outbound connections |
proxy.httpsProxy |
"" |
HTTPS proxy URL for outbound connections |
proxy.noProxy |
"localhost,127.0.0.1,.cluster.local" |
Hosts to bypass the proxy |
Air-gap / internal registry¶
Mirror the chart and images into your internal registry, then override the image registries in your values file:
image:
registry: registry.corp.internal
database:
image: { registry: registry.corp.internal }
redis:
image: { registry: registry.corp.internal }
Images to mirror: ghcr.io/sentari-dev/sentari/server,
ghcr.io/sentari-dev/sentari/docs, docker.io/timescale/timescaledb:2.14.2-pg16,
docker.io/redis:7.2-alpine. Pull the chart with
helm pull oci://ghcr.io/sentari-dev/sentari/charts/sentari --version <release>
and re-push it to your registry, or install from the local .tgz.
Air-gap licensing uses a license key with a signed max_offline_days claim.
Upgrading¶
helm upgrade sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
--version <new-release> \
-f values.yaml \
-n sentari
Database migrations run automatically on startup. Downgrading is not supported — always back up the database before upgrading.
Troubleshooting¶
Pods Pending / SCC errors. Confirm openshift.enabled=true is set. For the
bundled DB path, prefer external DB/Redis if the StatefulSet pods fail admission.
oc describe pod -n sentari <pod-name>
API not healthy. Check logs for migration or configuration errors:
oc logs deploy/sentari-api -n sentari | grep -i "migration\|alembic\|error"
Common causes: placeholder secretKey; missing licenseKey (server boots read-only,
writes return HTTP 402); wrong external DB/Redis URL.
Agent can't connect. Verify the Route is accessible: curl https://<route-host>/api/health.
Check the agent log at /var/log/sentari/agent.log. If certificates are corrupted,
re-enroll: delete /var/lib/sentari/certs/ and run with --enroll-token.
For additional assistance, contact your Sentari representative or visit the support portal.