Files
git.stella-ops.org/docs/doctor/articles/crypto/certchain.md
master c58a236d70 Doctor plugin checks: implement health check classes and documentation
Implement remediation-aware health checks across all Doctor plugin modules
(Agent, Attestor, Auth, BinaryAnalysis, Compliance, Crypto, Environment,
EvidenceLocker, Notify, Observability, Operations, Policy, Postgres, Release,
Scanner, Storage, Vex) and their backing library counterparts (AI, Attestation,
Authority, Core, Cryptography, Database, Docker, Integration, Notify,
Observability, Security, ServiceGraph, Sources, Verification).

Each check now emits structured remediation metadata (severity, category,
runbook links, and fix suggestions) consumed by the Doctor dashboard
remediation panel.

Also adds:
- docs/doctor/articles/ knowledge base for check explanations
- Advisory AI search seed and allowlist updates for doctor content
- Sprint plan for doctor checks documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:28:00 +02:00

4.8 KiB

checkId, plugin, severity, tags
checkId plugin severity tags
check.crypto.certchain stellaops.doctor.crypto warn
crypto
certificate
tls
security

Certificate Chain Validation

What It Checks

Verifies certificate chain completeness, trust anchor validity, and expiration for the configured TLS certificate. The check reads the certificate path from Crypto:TlsCertPath, Kestrel:Certificates:Default:Path, or Server:TlsCertificate and validates:

  • File existence: whether the configured certificate file exists on disk.
  • Chain completeness: whether all intermediate certificates are present (no missing links).
  • Trust anchor validity: whether the root CA is trusted by the system trust store.
  • Expiration: days until the certificate expires, with tiered severity.
Condition Result
No TLS certificate configured Skip
Certificate file not found Fail
Certificate chain incomplete (missing intermediates) Fail
Trust anchor not valid (unknown root CA) Fail
Certificate already expired Fail
Certificate expires within 7 days Fail
Certificate expires within 30 days Warn
Chain complete, trust anchor valid, not expiring soon Pass

Evidence collected: CertPath, ChainLength, MissingIntermediates, TrustAnchorValid, TrustAnchorIssuer, ExpirationDate, DaysRemaining.

This check always runs (no precondition), but skips if no TLS certificate path is configured.

Why It Matters

An incomplete certificate chain causes TLS handshake failures for clients that do not have intermediate certificates cached. An untrusted root CA triggers browser and API client warnings or outright connection refusal. An expired certificate causes immediate service outage for all HTTPS connections. Certificate issues affect every component that communicates over TLS, including the UI, API, inter-service communication, and external integrations.

Common Causes

  • Certificate file was moved or deleted from the configured path
  • Incorrect certificate path in configuration
  • Missing intermediate certificates in the certificate bundle
  • Incomplete certificate bundle (only leaf certificate, no intermediates)
  • Root CA not added to the system trust store
  • Self-signed certificate not explicitly trusted
  • Certificate not renewed before expiration
  • Automated renewal process failed silently

How to Fix

Docker Compose

# Check if certificate file exists at configured path
docker compose exec gateway ls -la /certs/

# Verify certificate details
docker compose exec gateway openssl x509 -in /certs/server.crt -noout -dates -subject -issuer

# Verify certificate chain
docker compose exec gateway openssl verify -untrusted /certs/chain.pem /certs/server.crt

# Bundle certificates correctly (leaf + intermediates)
cat server.crt intermediate.crt > fullchain.pem

# Update configuration in .env or compose override
# Crypto__TlsCertPath=/certs/fullchain.pem

# Set up automated renewal notification
# Notify__CertExpiry__ThresholdDays=14

Bare Metal / systemd

# Verify certificate file exists
ls -la /etc/stellaops/certs/server.crt

# Check certificate expiration
openssl x509 -in /etc/stellaops/certs/server.crt -noout -enddate

# Download missing intermediates
stella crypto cert fetch-chain --cert /etc/stellaops/certs/server.crt --output /etc/stellaops/certs/fullchain.pem

# Add CA to system trust store (Debian/Ubuntu)
sudo cp root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# Or configure explicit trust anchor
stella crypto trust-anchors add --type ca --cert root-ca.crt

# Renew certificate
stella crypto cert renew --cert /etc/stellaops/certs/server.crt

# Update appsettings.json
# "Crypto": { "TlsCertPath": "/etc/stellaops/certs/fullchain.pem" }

sudo systemctl restart stellaops-gateway

Kubernetes / Helm

# Check certificate secret
kubectl get secret stellaops-tls-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates

# Verify certificate chain
kubectl get secret stellaops-tls-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl verify

# Update TLS certificate secret
kubectl create secret tls stellaops-tls-cert \
  --cert=fullchain.pem \
  --key=server.key \
  --dry-run=client -o yaml | kubectl apply -f -
# values.yaml - use cert-manager for automated renewal
certManager:
  enabled: true
  issuer: letsencrypt-prod
  renewBefore: 360h  # 15 days before expiry

Verification

stella doctor run --check check.crypto.certchain
  • check.crypto.fips — FIPS compliance may impose certificate algorithm constraints
  • check.crypto.eidas — eIDAS compliance requires specific signature algorithms on certificates
  • check.crypto.hsm — HSM may store the private key associated with the certificate
  • check.compliance.attestation-signing — attestation signing uses related key material