kastoria-proxy-mtls
An NGINX reverse proxy that serves as an internal Kastoria ingress with
support for mutual TLS (mTLS). The ingress serves hosts inside the
Azure VNet, where requests arrive over HTTP with the client certificate
provided in the X-Forwarded-Client-Cert header. Each route is gated by the
client certificate's SHA-256 thumbprint found in the client certificate's HASH=
value. The ingress listens on port 8080 and routes to the same upstreams
as the public proxy.
The public variant is a separate image, kastoria-proxy.
Runtime details
| Property | Value |
|---|---|
| Runtime base image | nginxinc/nginx-unprivileged:1.31-alpine (pinned by digest) |
| Exposed port | 8080 |
| Container user | nginx (non-root) |
| Entrypoint | /entrypoint.sh (validates the role allowlist, renders the role maps and config, then execs NGINX) |
| Command | CMD ["nginx", "-g", "daemon off;"] |
| Health check | GET http://localhost:8080/healthz (every 30s, 5s timeout, 30s start period, 3 retries) |
| Stop signal | SIGTERM |
Both Azure proxy variants build from a common config template. The mTLS build keeps the mutual-TLS authorization blocks verbatim, so the shipped config proves its mTLS posture.
Enabling mTLS Authentication
To get mTLS working, the following is required:
- TLS termination occurs at the Azure Container Apps Environment's Envoy-based ingress.
- The calling clients' CA certificate must be registered with the environment (which will allow Envoy to validate the client requests).
- The
kastoria-proxy-mtlscontainer app's ingress must be configured withclientCertificateMode: require, which ensures the client certificate exists and is forwarded to the proxy. - The
KASTORIA_MTLS_CLIENT_ROLESenvironment variable (see below) defining the mTLS routing rules must exist and be formatted correctly.
OCI labels
| Label | Value |
|---|---|
org.opencontainers.image.title |
Kastoria Reverse Proxy |
org.opencontainers.image.description |
NGINX reverse proxy for the Kastoria Health platform |
org.opencontainers.image.licenses |
Proprietary |
org.opencontainers.image.url |
https://github.com/merkalis-io/kastoria-health |
org.opencontainers.image.documentation |
https://github.com/merkalis-io/kastoria-health |
io.kastoria.health-endpoint |
/ |
The org.opencontainers.image.version, org.opencontainers.image.revision,
org.opencontainers.image.created, org.opencontainers.image.source,
io.kastoria.base-image, and io.kastoria.service labels are injected at
build time by the CI/CD pipeline.
Pull and verify
Promoted images are published to the Distribution Registry under the customer namespace:
docker pull acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-proxy-mtls:<release-tag>
notation verify acrmerkalisdist0c66.azurecr.io/<customer>/kastoria-proxy-mtls:<release-tag>
See the Distribution Registry guide for authentication and trust setup.
Environment variables
| Variable | Secret | Required | Default | Description |
|---|---|---|---|---|
KASTORIA_MTLS_CLIENT_ROLES[1] |
Yes | Yes | — | mTLS routing rules. Startup fails if missing, empty, or malformed. |
KASTORIA_HEALTH_ADDRESS[2] |
No | No | kastoria-health:3000 |
Upstream DICOMweb server host and port. |
KASTORIA_SMARTLAUNCH_ADDRESS[2] |
No | No | kastoria-smartlaunch:4000 |
Upstream SMART Launch API host and port. |
KASTORIA_CONSOLEAPI_ADDRESS[2] |
No | No | kastoria-consoleapi:3005 |
Upstream Admin Console API host and port. |
KASTORIA_CONSOLEUI_ADDRESS[2] |
No | No | kastoria-consoleui:8081 |
Upstream Admin Console UI host and port. |
KASTORIA_REPORTVIEW_ADDRESS[2] |
No | No | kastoria-perfview:80 |
Upstream report viewer host and port. |
KASTORIA_REPORTVIEW_ENABLED |
No | No | true |
Set to false to strip the /reportview/ route. |
OHIF_VIEWER_ADDRESS[2] |
No | No | kastoria-ohif:8080 |
Upstream OHIF viewer host and port. |
PORT[3] |
No | No | 8080 |
Used by the container HEALTHCHECK; NGINX listens on 8080. |
Every variable above is substituted into the NGINX config at container start by
/entrypoint.sh, which also validates the mTLS routing rules and renders the role
maps; disabled route blocks are removed before NGINX starts.
Note: The default values listed for the various *_ADDRESS are in place for using the
non-mTLS proxy in a local docker compose environment. When deploying to Azure you must change these (see [2] below), or the container will fail to start.
mTLS Routing Rules
The mTLS routing rule is provided in the KASTORIA_MTLS_CLIENT_ROLES environment variable. It is a routing allowlist that maps each client certificate to its roles in the following format:
Thumbprint→role allowlist, <sha256hex>:role1,role2;<sha256hex>:role3;....
Thumbprint is the SHA-256 hash of the client's certificate as a hex string.
role is defined in the following way:
| Role | Grants |
|---|---|
dicomweb |
/studies, /errors |
smart |
/smart/ |
console |
/api/, /console/ |
reportview |
/reportview/ |
ohif |
/ (catch-all) |
all |
every route |
Note: The proxy's /healthz endpoint is exempt from the mTLS checks.
Deploying to an Azure Container Apps Environment
[1] The role allowlist should be supplied via a secretref: to a Key Vault secret. To allow automatic updates, this should be a versionless secret. The value of KASTORIA_MTLS_CLIENT_ROLES is validated at startup. a) Thumbprints must be exactly 64 hex characters, b) No whitespace or newlines are allowed, and c) There must be no empty or duplicate entries.
[2] The various *_ADDRESS variables should be set to the environment's internal FQDN for the container app. The Azure configuration template automatically includes the correct (443) port.
[3] The selected value for PORT must match the environment's ingress Target port for the container app.
Available versions
v0.10.0v0.9.1— deprecated for security reasons (CVE-2026-93990); usev0.10.0