Skip to main content

Configure the TLS certificates

This page sets up the certificates of the Navin Platform: the public certificate of the API and the portal, and the internal certificates of the data and security services. In production the API refuses a database URL without TLS, sends HSTS and requires rediss:// for Redis (Production rules).

Kubernetes with cert-manager​

Issuers​

deploy/cert-manager/cluster-issuers.yaml declares the issuers, applied by the infra layer:

ClusterIssuerRole
eai-selfsigned-bootstrapSigns the internal root CA once
eai-internal-caInternal CA (secret eai-root-ca, ECDSA P-384, 10 years): certificates of the data and security services
eai-openbao-pkiPKI engine of OpenBao (https://openbao.openbao.svc:8200)
letsencrypt-prodACME issuer for the public certificate

Steps​

  1. Choose the issuer of the public certificate. deploy/cert-manager/certificates.yaml issues api-gateway-tls (namespace apisix, names <control-plane-host> and <portal-host>) with letsencrypt-prod. On a site without ACME, point issuerRef to the issuer of your company PKI, and set the real host names.

    Expected result: kubectl -n apisix get certificate api-gateway-tls is Ready=True.

    If it fails: kubectl -n apisix describe certificate api-gateway-tls then kubectl get certificaterequest -A show the refusal of the issuer.

  2. Check the internal certificates. deploy/cert-manager/platform-services-certificates.yaml issues, from eai-internal-ca, nats-tls, qdrant-tls, redis-tls, temporal-tls, keycloak-tls, opa-tls, openfga-tls, clickhouse-tls, redis-opik-tls, opensearch-tls, opensearch-admin-tls, jaeger-tls, otel-collector-tls and openbao-tls. PostgreSQL uses the CA of CloudNativePG (secret eai-postgres-ca).

    kubectl get certificate -A

    Expected result: every certificate Ready=True.

  3. Distribute the CA. External Secrets copies ca.crt (never tls.key) of eai-root-ca to the namespaces that need it (deploy/external-secrets/secrets/ca-stores.yaml). The Control Plane database URL points to the mounted CA: sslmode=verify-full&sslrootcert=/run/secrets/postgres-ca/ca.crt.

  4. Portal ingress. portal.ingress.tlsSecretName (default portal-tls) is issued by portal.ingress.clusterIssuer (default eai-internal-ca); set your public issuer for a portal reached from the workstations. The ingress forces HTTPS, TLS 1.2 and 1.3, and HSTS.

The hops between chart workloads (APISIX to Control Plane, Control Plane to LiteLLM and Opik) carry no application certificate: Cilium encrypts them node to node with WireGuard and authenticates the Control Plane and LiteLLM hops with SPIFFE mutual authentication.

Servers installed from packages​

The services of the navin-platform package listen in plain HTTP on 8080 and 3300: put a TLS reverse proxy of the bank in front of them.

  1. Issue a certificate for the two public names (for example <control-plane-host> and <navin-host>) from the PKI of the bank.

  2. Configure the proxy to forward <control-plane-url> to http://<host>:8080 and https://<navin-host> to http://<host>:3300, with the WebSocket upgrade allowed on /v1/realtime/.

  3. Set the public URLs in /etc/navin-platform/platform.env:

    NAVIN_PORTAL_API_URL='<control-plane-url>'
    EAI_CORS_ORIGINS='https://<navin-host>'
    EAI_ALLOWED_HOSTS='<control-plane-host>'
    EAI_TRUSTED_PROXIES='<proxy-address>/32'
    EAI_HSTS_ENABLED='true'

    <proxy-address>: address of the reverse proxy, the only peer allowed to set X-Forwarded-For.

  4. Use TLS towards the dependencies: sslmode=verify-full in EAI_DATABASE_URL, rediss:// with EAI_REDIS_TLS_CA_FILE, https:// for OpenBao, Keycloak and the file scanning engines (EAI_FILE_SCAN_CA_FILE).

  5. Restart: sudo systemctl restart navin-platform.target.

Verify​

curl -fsSv <control-plane-url>/healthz 2>&1 | grep -E 'SSL connection|strict-transport-security'

Expected output: a TLS 1.2 or 1.3 connection and the header strict-transport-security.

Troubleshooting​

SymptomCauseFix
EAI_DATABASE_URL must use sslmode=require, verify-ca or verify-fullDatabase URL without TLS in productionAdd sslmode=verify-full and the CA
Browser rejects the portal certificateIssued by the internal CAUse the public issuer, or deploy the internal CA on the workstations
400 Bad Request from the API behind the proxyHost not in EAI_ALLOWED_HOSTSAdd the public host name
Client IP of every request is the proxyProxy not in EAI_TRUSTED_PROXIESAdd its address