Skip to content

TLS Certificates

Configure TLS certificates for secure communication between Mantis components.

ComponentCertificate TypePurpose
MandibleServer certificateHTTPS for API clients
ThoraxServer certificategRPC server for agents
TarsusClient certificatemTLS authentication
RequirementSpecification
Key AlgorithmRSA 2048+ or ECDSA P-256+
SignatureSHA-256 or stronger
Validity1 year recommended
FormatPEM encoded

For development environments, use the provided script:

Terminal window
just dev-certs

This generates:

  • Self-signed CA certificate and private key
  • Thorax server certificate (CN=thorax, serverAuth + clientAuth)
  • Tarsus listen-mode client certificate (CN=tarsus-dev, clientAuth + serverAuth)
  • Mandible dispatch certificate (CN=mandible, clientAuth only — required for Mandible→Thorax deployment dispatch)
  • Mandible server certificate (CN=mandible, serverAuth — for HTTPS on port 3000)
  • Lens dev-server certificate (CN=localhost, serverAuth — for Vite HTTPS on port 5173)
certs/
├── ca-cert.pem # CA certificate
├── ca-key.pem # CA private key
├── server-cert.pem # Thorax server certificate (serverAuth + clientAuth)
├── server-key.pem # Thorax server private key
├── client-cert.pem # Tarsus listen-mode client certificate (clientAuth + serverAuth)
├── client-key.pem # Tarsus client private key
├── dispatch-cert.pem # Mandible dispatch certificate (clientAuth, CN=mandible)
├── dispatch-key.pem # Mandible dispatch private key
├── mandible-cert.pem # Mandible server certificate (serverAuth, CN=mandible)
├── mandible-key.pem # Mandible server private key
├── lens-cert.pem # Lens dev-server certificate (serverAuth, CN=localhost)
└── lens-key.pem # Lens dev-server private key
OptionUse CaseComplexity
Public CAInternet-facing servicesLow
Private CAInternal infrastructureMedium
Self-managed CAAir-gapped environmentsHigh

For internet-facing Mandible deployments:

Terminal window
# Using Let's Encrypt with certbot
certbot certonly --standalone -d mantis.example.com
# Certificate files
/etc/letsencrypt/live/mantis.example.com/fullchain.pem
/etc/letsencrypt/live/mantis.example.com/privkey.pem

For internal infrastructure:

Terminal window
# Create CA private key
openssl genrsa -out ca.key 4096
# Create CA certificate
openssl req -x509 -new -nodes -key ca.key \
-sha256 -days 3650 \
-subj "/CN=Mantis Internal CA/O=Your Organization" \
-out ca.crt
[server]
host = "0.0.0.0"
port = 3000
[server.tls]
cert_path = "/etc/mantis/certs/server.crt"
key_path = "/etc/mantis/certs/server.key"
VariableDescriptionExample
MANDIBLE__SERVER__PORTServer port3000
MANDIBLE__SERVER__TLS__CERT_PATHCertificate path/etc/mantis/certs/server.crt
MANDIBLE__SERVER__TLS__KEY_PATHPrivate key path/etc/mantis/certs/server.key

For certificates signed by an intermediate CA:

Terminal window
# Concatenate certificate chain
cat server.crt intermediate.crt > fullchain.crt

Configure with the full chain:

[server.tls]
cert_path = "/etc/mantis/certs/fullchain.crt"
key_path = "/etc/mantis/certs/server.key"
[grpc]
address = "0.0.0.0"
port = 50051
[tls]
auth_mode = "thumbprint"
cert_path = "/etc/mantis/certs/thorax.crt"
key_path = "/etc/mantis/certs/thorax.key"
ca_cert_path = "/etc/mantis/certs/ca.crt"

Thorax always requires client certificates (mTLS is mandatory). The auth_mode controls how they are validated:

[tls]
auth_mode = "ca_signed"
cert_path = "/etc/mantis/certs/thorax.crt"
key_path = "/etc/mantis/certs/thorax.key"
ca_cert_path = "/etc/mantis/certs/ca.crt"
Auth ModeDescription
thumbprintSelf-signed certs accepted, thumbprint verified at app layer (default)
ca_signedClient certs must be CA-signed at TLS layer
[tls]
cert_path = "/etc/tarsus/cert.pem"
key_path = "/etc/tarsus/key.pem"
ca_cert_path = "/etc/tarsus/ca.crt"

For thumbprint mode, Tarsus automatically generates a certificate on first run if one is not configured. You can also generate one manually:

Terminal window
# Generate self-signed certificate using OpenSSL
openssl req -new -x509 -nodes \
-keyout /etc/tarsus/key.pem \
-out /etc/tarsus/cert.pem \
-days 365 \
-subj "/CN=$(hostname)/O=Mantis Agent"
# View the thumbprint
tarsus show-thumbprint

For CA-signed mode:

Terminal window
# Generate CSR
openssl req -new -nodes \
-keyout /etc/tarsus/key.pem \
-out /etc/tarsus/client.csr \
-subj "/CN=tarsus-web-prod-01/O=Your Organization"
# Sign with CA (on CA server)
openssl x509 -req -in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 365 -sha256
# Install on agent
cp client.crt /etc/tarsus/cert.pem
Terminal window
# Generate server private key
openssl genrsa -out server.key 2048
# Generate CSR
openssl req -new -key server.key \
-subj "/CN=mantis.example.com/O=Your Organization" \
-out server.csr
# Create certificate with SAN
cat > server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = mantis.example.com
DNS.2 = *.mantis.example.com
IP.1 = 10.0.0.10
EOF
# Sign with CA
openssl x509 -req -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 365 \
-extfile server.ext
Terminal window
# Generate client private key
openssl genrsa -out client.key 2048
# Generate CSR
openssl req -new -key client.key \
-subj "/CN=tarsus-agent-01/O=Your Organization" \
-out client.csr
# Create certificate
cat > client.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = clientAuth
EOF
# Sign with CA
openssl x509 -req -in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 365 \
-extfile client.ext
Terminal window
# Verify server certificate against CA
openssl verify -CAfile ca.crt server.crt
# Expected output:
# server.crt: OK
Terminal window
# View certificate information
openssl x509 -in server.crt -text -noout
# Check expiration date
openssl x509 -in server.crt -enddate -noout
# Get thumbprint
openssl x509 -in server.crt -fingerprint -sha256 -noout
Terminal window
# Test Mandible HTTPS
openssl s_client -connect mantis.example.com:3000 \
-servername mantis.example.com
# Test Thorax gRPC with client cert
openssl s_client -connect thorax.example.com:50051 \
-cert client.crt -key client.key -CAfile ca.crt
Terminal window
# Secure private keys
chmod 600 /etc/mantis/certs/*.key
chown mantis:mantis /etc/mantis/certs/*.key
# Certificates can be readable
chmod 644 /etc/mantis/certs/*.crt
/etc/mantis/certs/
├── ca.crt # CA certificate (readable)
├── server.crt # Server certificate (readable)
├── server.key # Server private key (restricted)
└── chain.crt # Certificate chain (readable)

For production, consider using:

SolutionIntegration
HashiCorp VaultPKI secrets engine
AWS Secrets ManagerCertificate storage
Kubernetes Secretscert-manager integration
Terminal window
# Generate new CSR with existing key
openssl req -new -key server.key \
-subj "/CN=mantis.example.com" \
-out server.csr
# Sign new certificate
openssl x509 -req -in server.csr \
-CA ca.crt -CAkey ca.key \
-out server-new.crt -days 365
# Replace certificate (with brief downtime)
mv server-new.crt server.crt
systemctl restart mantis-mandible

For Let’s Encrypt certificates:

Terminal window
# Certbot auto-renewal
certbot renew --deploy-hook "systemctl reload mantis-mandible"
# Add to crontab
0 0 * * * certbot renew --quiet --deploy-hook "systemctl reload mantis-mandible"

The TLS protocol version and cipher suites are not configurable. Mandible’s [server.tls] section accepts only cert_path and key_path; there are no min_version or cipher_suites keys. The underlying rustls stack negotiates TLS 1.2/1.3 with modern, secure cipher suites by default and rejects deprecated versions (TLS 1.0/1.1).

VersionStatus
TLS 1.0Not offered (deprecated)
TLS 1.1Not offered (deprecated)
TLS 1.2Supported
TLS 1.3Supported (preferred)
ErrorCauseSolution
certificate verify failedCA not trustedAdd CA to trust store
certificate has expiredCertificate expiredRenew certificate
hostname mismatchWrong CN or SANRegenerate with correct names
unable to get local issuerMissing intermediateInclude full chain

Certificate chain incomplete:

Terminal window
# Check chain
openssl s_client -connect mantis.example.com:3000 -showcerts
# Fix: concatenate certificates
cat server.crt intermediate.crt > fullchain.crt

Permission denied on key file:

Terminal window
# Check permissions
ls -la /etc/mantis/certs/
# Fix permissions
chmod 600 /etc/mantis/certs/server.key
chown mantis:mantis /etc/mantis/certs/server.key

Wrong certificate format:

Terminal window
# Convert DER to PEM
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM
# Convert PKCS12 to PEM
openssl pkcs12 -in cert.p12 -out cert.pem -nodes