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 send API keys and software inventories over the wire: without TLS, anyone with network visibility can read them.

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 call carries its organization API key in the Authorization header. Without TLS the key can be read from a single packet capture and used until it is rotated.
  • Inventory data is sensitive. Agents post software inventories (versions, paths, container images, dependency manifests). Keep that traffic encrypted.
  • Session cookies and sign-ins also travel over the same connection. Serve the web interface over HTTPS only.
  • The Windows installer has a -AllowHttp option for plain HTTP in labs (see Windows agent). Do not use it in production.

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 proxy so certbot can claim port 80 for the HTTP-01 challenge
docker compose stop sentrikat-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. Three cases:

    • On Windows agents: Import-Certificate into LocalMachine\Root.
    • On Debian/Ubuntu agents: copy the CA certificate to /usr/local/share/ca-certificates/ as a .crt file 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 not trust the cert unless the certificate is installed in their trust store (see Option B, step 4). 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). The certificate above was issued with --standalone, so renewal needs port 80 free: stop the proxy before the challenge and start it after.
  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 \
  --pre-hook "docker compose -f /opt/sentrikat/docker-compose.yml stop sentrikat-nginx" \
  --post-hook "docker compose -f /opt/sentrikat/docker-compose.yml start sentrikat-nginx"

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 sentrikat-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 or key.pem has wrong permissions or wrong owner. The proxy must be able to read both files. Fix: chmod 644 ssl/cert.pem (the certificate is public). For the key, keep it private (chmod 600) when the proxy reads it as the file owner; if the proxy runs as a different user, give that user's group read access (chgrp <proxy-group> ssl/key.pem and chmod 640 ssl/key.pem). Never make the key world-readable.
  • 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: install the certificate in the agent host's trust store (lab use only).

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.