checkId: check.security.tls.certificate plugin: stellaops.doctor.security severity: fail tags: [security, tls, certificate]
TLS Certificate
What It Checks
Validates TLS certificate validity and expiration. The check only runs when a certificate path is configured (Tls:CertificatePath or Kestrel:Certificates:Default:Path). It loads the certificate file and applies the following validations:
| Condition | Result |
|---|---|
| Certificate file not found | fail |
| Certificate cannot be loaded (corrupt, wrong password) | fail |
Certificate not yet valid (NotBefore in the future) | fail |
Certificate has expired (NotAfter in the past) | fail |
| Certificate expires in less than 30 days | warn |
| Certificate valid for 30+ days | pass |
The check supports both PEM certificates and PKCS#12 (.pfx/.p12) files with optional passwords (Tls:CertificatePassword or Kestrel:Certificates:Default:Password).
Evidence collected includes: subject, issuer, NotBefore, NotAfter, days until expiry, and thumbprint.
Why It Matters
An expired or invalid TLS certificate causes all HTTPS connections to fail. Browsers display security warnings, API clients reject responses, and inter-service communication breaks. In a release control plane, TLS failures prevent:
- Console access for operators.
- API calls from CI/CD pipelines.
- Inter-service communication via HTTPS.
- OIDC authentication flows with the Authority.
Certificate expiration is the most common cause of production outages that is entirely preventable with monitoring.
Common Causes
- Certificate file path is incorrect or the file was deleted
- Certificate has exceeded its validity period (expired)
- Certificate validity period has not started yet (clock skew or pre-dated certificate)
- Certificate file is corrupted
- Certificate password is incorrect (for PKCS#12 files)
- Certificate format not supported
How to Fix
Docker Compose
Mount the certificate and configure the path:
services:
platform:
environment:
Tls__CertificatePath: "/app/certs/stellaops.pfx"
Tls__CertificatePassword: "${TLS_CERT_PASSWORD}"
volumes:
- ./certs/stellaops.pfx:/app/certs/stellaops.pfx:ro
Generate a new self-signed certificate for development:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=stella-ops.local"
openssl pkcs12 -export -out stellaops.pfx -inkey key.pem -in cert.pem
Bare Metal / systemd
Renew the certificate (e.g., with Let’s Encrypt):
sudo certbot renew
sudo systemctl restart stellaops-platform
Or update appsettings.json to point at a new certificate:
{
"Tls": {
"CertificatePath": "/etc/ssl/stellaops/cert.pfx",
"CertificatePassword": "<password>"
}
}
Kubernetes / Helm
Use cert-manager for automatic certificate management:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: stellaops-tls
spec:
secretName: stellaops-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- stella-ops.yourdomain.com
Reference in Helm values:
tls:
secretName: "stellaops-tls-secret"
Verification
stella doctor run --check check.security.tls.certificate
Related Checks
check.security.headers— HSTS requires a valid TLS certificatecheck.security.encryption— validates encryption at rest (TLS handles encryption in transit)check.core.crypto.available— RSA/ECDSA must be available for certificate operations
