Production-grade setup (root CA with intermediate CA) Linux
Production-grade setup (root CA with intermediate CA) Linux
🔐 1. Architecture (What changes vs basic setup)
Instead of:
Root CA → Server / Client certs
Production uses:
Root CA (offline, highly protected)
↓
Intermediate CA (online, signs certs)
↓
Server & Client Certificates
Why this matters
Root key is never exposed → reduces catastrophic compromise risk
You can rotate/revoke intermediates without replacing root
Enables scalable issuance across environments
🧱 2. Directory Structure (OpenSSL CA layout)
mkdir -p ~/pki/{root,intermediate}
cd ~/pki
# Root CA structure
mkdir -p root/{certs,crl,newcerts,private}
chmod 700 root/private
touch root/index.txt
echo 1000 > root/serial
# Intermediate CA structure
mkdir -p intermediate/{certs,crl,csr,newcerts,private}
chmod 700 intermediate/private
touch intermediate/index.txt
echo 1000 > intermediate/serial
echo 1000 > intermediate/crlnumber
🏛️ 3. Create Root CA (OFFLINE)
cd ~/pki/root
# Root private key (VERY sensitive)
openssl genrsa -out private/root.key 4096
# Root certificate (self-signed)
openssl req -x509 -new -nodes \
-key private/root.key \
-sha256 -days 3650 \
-out certs/root.crt
🔴 Best practice
Store root key offline (USB, HSM, air-gapped machine)
Never use it directly to sign server/client certs
🏗️ 4. Create Intermediate CA
cd ~/pki/intermediate
# Generate intermediate key
openssl genrsa -out private/intermediate.key 4096
# Create CSR
openssl req -new \
-key private/intermediate.key \
-out csr/intermediate.csr
Now sign it using the root CA:
cd ~/pki/root
openssl x509 -req \
-in ../intermediate/csr/intermediate.csr \
-CA certs/root.crt \
-CAkey private/root.key \
-CAcreateserial \
-out ../intermediate/certs/intermediate.crt \
-days 1825 -sha256
🔗 5. Create Certificate Chain
cat intermediate/certs/intermediate.crt root/certs/root.crt > intermediate/certs/ca-chain.crt
This chain is critical for:
NGINX trust
Client validation
🌐 6. Issue Server Certificate (via Intermediate)
cd ~/pki/intermediate
# Server key
openssl genrsa -out private/server.key 2048
# CSR
openssl req -new \
-key private/server.key \
-out csr/server.csr
Sign with intermediate:
openssl x509 -req \
-in csr/server.csr \
-CA certs/intermediate.crt \
-CAkey private/intermediate.key \
-CAcreateserial \
-out certs/server.crt \
-days 825 -sha256
👤 7. Issue Client Certificate
# Client key
openssl genrsa -out private/client.key 2048
# CSR
openssl req -new \
-key private/client.key \
-out csr/client.csr
# Sign
openssl x509 -req \
-in csr/client.csr \
-CA certs/intermediate.crt \
-CAkey private/intermediate.key \
-CAcreateserial \
-out certs/client.crt \
-days 825 -sha256
⚙️ 8. NGINX Production mTLS Config
server {
listen 443 ssl;
server_name your_domain;
# Server cert + full chain
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# Trust chain for client verification
ssl_client_certificate /etc/nginx/certs/ca-chain.crt;
# Enforce mTLS
ssl_verify_client on;
ssl_verify_depth 2;
# Strong TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Pass client identity upstream
proxy_set_header X-Client-Cert $ssl_client_cert;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
location / {
proxy_pass http://backend;
}
}
🔍 9. Identity & Authorization (Often Missed)
mTLS ≠ authorization.
After TLS succeeds, you should:
Extract client identity:
$ssl_client_s_dn$ssl_client_i_dn
Enforce rules:
Only specific CNs or OUs allowed
Map cert → user/service
Example:
if ($ssl_client_s_dn !~ "CN=trusted-client") {
return 403;
}
🚫 10. Revocation (CRL / OCSP)
Generate CRL
openssl ca -gencrl -out crl/intermediate.crl
Configure NGINX
ssl_crl /etc/nginx/certs/intermediate.crl;
Why important
You can revoke compromised client certs
Without this, certs remain valid until expiry
🔐 11. Key Security Practices
Use HSM or KMS (AWS KMS, HashiCorp Vault)
Rotate:
Intermediate CA every ~1–3 years
Server/client certs every ~60–90 days
Restrict file permissions:
chmod 600 *.key
📦 12. Automation (What real systems use)
Manual OpenSSL doesn’t scale. Production setups typically use:
HashiCorp Vault → dynamic cert issuance
cert-manager → Kubernetes-native automation
Smallstep Step CA → modern lightweight CA
⚠️ Common Production Pitfalls
❌ Using root CA directly
❌ No revocation mechanism
❌ Long-lived client certs
❌ No identity mapping (authZ gap)
❌ Missing SAN (modern TLS ignores CN)
🧠 Final Mental Model
Think of production mTLS as three layers:
Trust → Root + Intermediate CA chain
Authentication → TLS handshake verifies certs
Authorization → App decides “who is allowed”
If you want, I can go deeper into:
Full OpenSSL config files (
openssl.cnf) for CA policiesKubernetes mTLS (Istio / Linkerd)
Or a real-world architecture (API gateway + microservices)
Comments
Post a Comment