Skip to content

TLS Setup

The SentriKat API ships with HTTP enabled by default for ease of first install, but production deployments must serve all API traffic over HTTPS. Agents transmit license keys, system inventories, and CVE intelligence over the wire — anything less than TLS leaks credentials and asset data to anyone with network visibility.

This page walks through the three TLS topologies SentriKat supports out of the box and the cert-acquisition flow for each.

Why TLS is required

  • API keys travel as bearer tokens. Every agent → SaaS / on-prem call carries the license key in the Authorization header. Without TLS the key is recoverable from a single packet capture and grants full control of the deployment until it's rotated.
  • Inventory data is sensitive. Agents post software inventories (versions, paths, container images, dependency manifests). For a regulated organisation this is a map of every exploitable surface in the estate — exactly the data an attacker needs to plan lateral movement.
  • GDPR Art. 32 requires "appropriate technical measures" for self-hosted deployments that process personal data tied to operator identities (admin emails, audit log actor IDs). TLS is the minimum bar for a regulator to accept that "appropriate" was actually attempted.
  • Agents refuse plaintext by default. From the 2026 agent release onwards, the installer aborts with an HTTPS-required error when pointed at an http:// URL unless -AllowHttp is explicitly passed (see the agent installation page for the flag).

Quick path — built-in nginx with TLS template

The on-prem image ships two nginx config templates: the default nginx.conf.template (HTTP-only, listens on :80) and nginx-ssl.conf.template (HTTPS on :443, HTTP redirect on :80). Switching to the TLS template is a 4-step flip:

cd /opt/sentrikat                           # or wherever you put the deploy

# 1. Disable the HTTP-only template
mv nginx/nginx.conf.template nginx/nginx.conf.template.disabled

# 2. Activate the TLS template
mv nginx/nginx-ssl.conf.template nginx/nginx.conf.template

# 3. Drop your cert + key into ./ssl/
mkdir -p ssl
cp /path/to/your/fullchain.pem ssl/cert.pem
cp /path/to/your/privkey.pem   ssl/key.pem
chmod 600 ssl/key.pem

# 4. Recycle the stack
docker compose down
docker compose up -d

Verify with:

curl -v https://your-host/api/health
# expect HTTP/2 200, TLS handshake in the verbose output

The cert.pem filename must contain the full chain (leaf + any intermediate certs concatenated). A bare leaf cert will pass curl -k but fail strict validation from agents and from public CT log monitors.

Cert acquisition

Three paths, pick the one that matches your network position.

Option A — Let's Encrypt via certbot (internet-facing deployments)

Free, automated, 90-day rotation. Runs on the host, not inside the container.

# One-time install
sudo apt install certbot python3-certbot-nginx       # Debian / Ubuntu
sudo dnf install certbot python3-certbot-nginx       # RHEL / Rocky / Alma

# Stop the stack so certbot can claim port 80 for the HTTP-01 challenge
docker compose stop nginx

sudo certbot certonly --standalone \
  -d sentrikat.example.com \
  --email [email protected] --agree-tos --no-eff-email

# Wire the resulting files into ./ssl/
sudo cp /etc/letsencrypt/live/sentrikat.example.com/fullchain.pem ssl/cert.pem
sudo cp /etc/letsencrypt/live/sentrikat.example.com/privkey.pem   ssl/key.pem
sudo chown $USER:$USER ssl/*.pem
chmod 600 ssl/key.pem

docker compose up -d

Use this when SentriKat is reachable from the public internet on port 80 (HTTP-01) or via DNS API (DNS-01 — see certbot plugins for Cloudflare, Route53, etc.).

Option B — Internal CA (corporate / air-gapped)

Most enterprises run an AD CS / Vault / step-ca / private CA. The flow is:

  1. Generate a key + CSR on the SentriKat host:

    openssl req -new -newkey rsa:2048 -nodes \
      -keyout ssl/key.pem -out ssl/sentrikat.csr \
      -subj "/CN=sentrikat.corp.example.com/O=Example Corp"
    chmod 600 ssl/key.pem
    
  2. Submit the CSR to your internal CA (the exact mechanism is CA-specific: AD CS web enrollment, step ca certificate, Vault PKI, etc.).

  3. Receive cert.pem (with the full chain) from the CA and drop it into ./ssl/cert.pem.

  4. Distribute the internal CA root to agent hosts so they trust the cert. Two paths:

    • On Windows agents: Import-Certificate into LocalMachine\Root.
    • On Linux agents: drop the CA bundle into /etc/ssl/certs/ and run update-ca-certificates.
    • For the SentriKat container itself if it needs to talk to other internally-issued endpoints (e.g. an internal SMTP relay), mount the CA bundle into /app/custom-certs — see the Container User & Privilege Model page for the bind-mount setup.

Option C — Self-signed (dev / lab only)

Acceptable for short-lived test installs. Agents will refuse the cert in default mode; you must pass -AllowHttp (PowerShell installer) or --allow-insecure-tls (Linux/macOS installer) to skip verification. Do not use self-signed in any environment that holds real inventory data.

openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
  -keyout ssl/key.pem -out ssl/cert.pem \
  -subj "/CN=sentrikat.local"
chmod 600 ssl/key.pem

docker compose down && docker compose up -d

Cert renewal automation

Let's Encrypt certs expire every 90 days. Inside the container is the wrong place to renew — use a host-level cron / systemd timer that:

  1. Runs certbot renew (which is a no-op if the cert is more than 30 days from expiry).
  2. Copies fullchain.pem and privkey.pem into ./ssl/ if they changed.
  3. Sends nginx a SIGHUP to pick up the new cert without dropping connections.

A complete /etc/cron.daily/sentrikat-cert-renew script:

#!/bin/bash
set -e
cd /opt/sentrikat

certbot renew --quiet --no-self-upgrade

LIVE=/etc/letsencrypt/live/sentrikat.example.com
if [ "$LIVE/fullchain.pem" -nt ssl/cert.pem ]; then
  cp "$LIVE/fullchain.pem" ssl/cert.pem
  cp "$LIVE/privkey.pem"   ssl/key.pem
  chown $(stat -c '%u:%g' .) ssl/*.pem
  chmod 600 ssl/key.pem
  docker compose kill -s SIGHUP nginx
fi

For internal-CA certs the analogous job invokes whatever your CA's renewal command is (step ca renew, Vault PKI auto-renew, scheduled CSR submission, etc.) — same cp + SIGHUP afterwards.

Troubleshooting

SSL handshake failed from curl -v

Almost always one of:

  • cert.pem has wrong permissions or wrong owner. Inside the container nginx runs as a non-root user; if your host bind-mount has mode 600 root:root the worker can't read the key. Fix: chmod 644 ssl/cert.pem (cert is public, can be world-readable) and chmod 600 ssl/key.pem with ownership matching the nginx user inside the image.
  • cert.pem is the leaf only, not the full chain. Some clients (browsers) tolerate this because they fetch missing intermediates; agents do not. Fix: concatenate leaf.pem + intermediate.pem into cert.pem (leaf first).
  • Wrong file format — DER instead of PEM, or PKCS#12 instead of PEM. Run openssl x509 -in ssl/cert.pem -noout -text and openssl rsa -in ssl/key.pem -check. Both must succeed and print parseable content.

cert verification failed from agents

The agent reaches the API but rejects the cert. Means the agent's local trust store doesn't contain the issuing CA. Pick one:

  • Public CA (Let's Encrypt, DigiCert, Sectigo, etc.) — already in every OS trust store. If verification fails with a public CA, the cert chain on the server is incomplete (see "wrong chain" above).
  • Internal CA — add the CA root to the agent host's trust store (per-OS instructions in the agent docs).
  • Self-signed — reinstall the agent with -AllowHttp / --allow-insecure-tls (never in production).

If the SentriKat container itself talks to your internal CA-signed endpoints (LDAP, SMTP relay, Jira, etc.), mount the CA bundle into /app/custom-certs and the container's entrypoint will append it to the system trust store at boot — see Container User & Privilege Model for the bind-mount layout.

nginx logs show SSL_CTX_use_PrivateKey_file error

Key and cert don't match. Confirm with:

openssl x509 -in ssl/cert.pem -noout -modulus | openssl md5
openssl rsa  -in ssl/key.pem  -noout -modulus | openssl md5
# the two MD5 values must be identical

If they differ you've mixed certs from different issuance batches — re-issue or copy the right pair.