Chapter 1: Cryptography, PKI Structure & Fundamentals
Before You Begin: This chapter builds the conceptual foundation you need before touching a single certificate. By the end, you will understand why cryptography exists, how it works at a practical level, and what happens behind the scenes when two machines establish a secure connection.
1.1 Why Use Cryptography?
Imagine sending a postcard. Anyone who handles it—postal workers, neighbors, strangers—can read it. Now imagine the postcard contains your bank password. That is what unencrypted network traffic looks like.
Every packet traveling across a network can be intercepted, read, modified, or forged. Without cryptography:
| Threat | What happens | Real-world example |
|---|---|---|
| Eavesdropping | Attacker reads your data | Capturing passwords on public Wi-Fi |
| Tampering | Attacker modifies data in transit | Injecting malware into a software download |
| Impersonation | Attacker pretends to be someone else | Fake banking website collecting credentials |
| Repudiation | Sender denies having sent a message | Denying a financial transaction |
Cryptography solves all four of these problems. It is not optional in modern systems—it is the foundation of every secure communication.
1.2 The Four Pillars of Information Security
Cryptography provides four fundamental guarantees. Every secure system relies on a combination of these:
Confidentiality — “Only you can read this”
Confidentiality ensures that data is readable only by the intended recipient. Even if an attacker intercepts the data, they see only meaningless noise.
How it is implemented:
- Symmetric encryption (AES-256): Same key encrypts and decrypts. Fast, used for bulk data.
- Asymmetric encryption (RSA, ECC): Public key encrypts, private key decrypts. Used for key exchange.
What it protects against: Eavesdropping.
Integrity — “This has not been changed”
Integrity guarantees that data has not been altered between sender and receiver. If even a single bit changes, the modification is detected.
How it is implemented:
- Hash functions (SHA-256): Produce a fixed-size fingerprint of data.
- HMAC: Hash combined with a secret key for authenticated integrity.
- Digital signatures: Hash signed with a private key.
Original: "Transfer $100 to Bob" → SHA-256 → a1b2c3d4...
Tampered: "Transfer $900 to Bob" → SHA-256 → f7e8d9c0... ← DIFFERENT!
What it protects against: Tampering.
Authenticity — “You are who you say you are”
Authenticity proves the identity of the communicating party. When you connect to your bank’s website, you need assurance that it is actually your bank, not an impersonator.
How it is implemented:
- Digital certificates (X.509): Bind a public key to an identity.
- Certificate Authorities (CAs): Trusted third parties that verify identities.
- Digital signatures: Prove that a message was created by the claimed sender.
What it protects against: Impersonation.
Non-Repudiation — “You cannot deny this”
Non-repudiation ensures that the sender cannot deny having sent a message or performed an action. This is the digital equivalent of a handwritten signature on a contract.
How it is implemented:
- Digital signatures with private keys: Only the key holder can produce the signature.
- Timestamping: Proves when an action occurred.
- Audit logs with cryptographic integrity: Tamper-evident records.
What it protects against: Repudiation (denying responsibility).
Summary: The Four Pillars
| Pillar | Question it answers | Implemented by | Protects against |
|---|---|---|---|
| Confidentiality | Can anyone else read this? | Encryption (AES, RSA) | Eavesdropping |
| Integrity | Has this been modified? | Hashes (SHA-256), HMAC | Tampering |
| Authenticity | Who sent this? | Certificates, signatures | Impersonation |
| Non-repudiation | Can the sender deny it? | Digital signatures | Repudiation |
1.3 Hash Functions: Digital Fingerprints
A hash function takes an input of any size and produces an output of fixed size. Think of it as a fingerprint for data.
Properties and Examples
1. Deterministic — Same input always produces the same output.
SHA-256("Hello") → 185f8db32271... (always)
SHA-256("Hello") → 185f8db32271... (always)
2. One-Way (Pre-image Resistance) — You cannot reverse-engineer the input from the output.
185f8db32271... → ??? (computationally infeasible to find input)
3. Collision Resistant — It is practically impossible to find two different inputs that produce the same output.
SHA-256("input A") → hash1
SHA-256("input B") → hash2
hash1 ≠ hash2 (with overwhelming probability)
4. Avalanche Effect — A tiny change in input produces a completely different output.
SHA-256("Hello World") → a591a6d40bf420404a011733cfb7b190...
SHA-256("Hello World!") → 7f83b1657ff1fc53b92dc18148a1d65d...
↑ completely different!
Are Hashes Safe?
Not all hash algorithms are equal. Some have been broken:
| Algorithm | Output Size | Status | Why |
|---|---|---|---|
| MD5 | 128 bits | BROKEN | Collisions found in seconds. Never use for security. |
| SHA-1 | 160 bits | BROKEN | Google demonstrated a practical collision in 2017 (SHAttered). |
| SHA-256 | 256 bits | SAFE | No known practical attacks. Current standard. |
| SHA-384 | 384 bits | SAFE | Higher security margin. |
| SHA-512 | 512 bits | SAFE | Maximum security for SHA-2 family. |
| SHA-3 | 256+ bits | SAFE | Different design (Keccak). Future-proof alternative. |
| BLAKE2 | 256+ bits | SAFE | Very fast, used in modern applications. |
“Broken” means: An attacker can find two different inputs that produce the same hash (collision). This allows forging documents, certificates, or signatures.
# Verify for yourself — compute hashes on any RHEL system:
echo -n "Hello World" | sha256sum
# a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
echo -n "Hello World" | md5sum
# b10a8db164e0754105b7a99be72e3fe5 ← DO NOT trust this for security!
Real-World Uses of Hashes
| Use case | How | Example |
|---|---|---|
| Password storage | Store hash, not password | /etc/shadow on Linux |
| File integrity | Compare hash before/after | sha256sum package.rpm |
| Digital signatures | Sign the hash, not the data | Certificate signing |
| Deduplication | Identify identical files | Backup systems |
| Blockchain | Chain of hashes | Bitcoin proof-of-work |
1.4 Symmetric vs Asymmetric Cryptography
Symmetric: One Key for Everything
Both sender and receiver share the same secret key. Like a padlock where both parties have a copy of the same key.
| Property | Value |
|---|---|
| Speed | Very fast (hardware-accelerated AES) |
| Key size | 128 or 256 bits |
| Problem | How do you share the key securely in the first place? |
| Examples | AES-128, AES-256, ChaCha20 |
Asymmetric: Two Keys, Two Roles
Each party has a key pair: a public key (share freely) and a private key (never share).
| Property | Value |
|---|---|
| Speed | Slow (1000x slower than symmetric) |
| Key size | 2048–4096 bits (RSA) or 256 bits (ECC) |
| Advantage | No need to share a secret key beforehand |
| Examples | RSA, ECDSA, Ed25519 |
Why We Need Both: Hybrid Encryption
Asymmetric cryptography solves the key distribution problem, but it is too slow for bulk data. The solution: use asymmetric to exchange a symmetric key, then use symmetric for the actual data.
This is exactly what happens in every HTTPS connection.
1.5 Understanding Key Exchange: The Color Mixing Analogy
Before diving into the real TLS/RSA handshake, let’s build intuition with a visual analogy. This explains Diffie-Hellman key exchange, the mechanism used in modern TLS to establish a shared secret.
The Problem
Alice and Bob want to agree on a shared secret color that Eve (the eavesdropper) cannot figure out, even though Eve can see everything they send each other.
Why Eve Cannot Cheat
Mixing paint is easy to do but impossible to reverse. You cannot separate mixed paint back into its components. In mathematics, this is analogous to:
- Easy: Multiply two large primes → get a product (mixing)
- Hard: Factor a large product → find the primes (unmixing)
This is the one-way function that makes cryptography work.
From Colors to Numbers
| Color Analogy | Cryptographic Equivalent |
|---|---|
| Public color (Yellow) | Public parameters (large prime, generator) |
| Alice’s secret (Red) | Alice’s private key |
| Bob’s secret (Blue) | Bob’s private key |
| Mixed color sent (Orange/Green) | Public key (computed from private) |
| Final shared secret (Brown) | Shared session key |
| “Cannot unmix paint” | Discrete logarithm problem is computationally hard |
1.6 The TLS Handshake: How a Secure Connection Really Works
Now let’s see what actually happens when your browser connects to https://bank.com. This combines everything we’ve learned: hashes, asymmetric crypto, symmetric crypto, certificates, and key exchange.
Step-by-Step Walkthrough
Step 1-2 (Hello): Client and server exchange capabilities and random numbers. These randoms add freshness—they ensure every session is unique even between the same parties.
Step 3 (Certificate): The server proves its identity by sending its X.509 certificate containing its public key.
Step 4 (Verification): This is the critical trust step. The client walks the chain of trust:
Each signature is verified using the issuer’s public key. If any link breaks, the handshake fails.
Step 5 (Key Exchange): The client generates 48 bytes of random data (PreMasterSecret), encrypts it with the server’s public RSA key, and sends it. Only the server’s private key can decrypt this—this is the asymmetric magic.
Step 6 (Key Derivation): Both sides independently compute the same session keys using a Pseudo-Random Function (PRF). This is where we transition from slow asymmetric to fast symmetric.
Step 7-9 (Encrypted Communication): From here on, everything is encrypted with AES-256—thousands of times faster than RSA.
Modern TLS 1.3: Simpler and Faster
TLS 1.3 simplified the handshake by removing RSA key exchange (forward secrecy is now mandatory) and reducing round trips:
1.7 PKI Structure: The Trust Architecture
Public Key Infrastructure (PKI) is the system that manages digital certificates and public keys. It answers the question: “How do I know this public key really belongs to bank.com?”
Why a Chain?
Root CAs are extremely valuable—if compromised, every certificate they ever signed becomes untrusted. So Root CAs are:
- Stored in offline, air-gapped hardware security modules (HSMs)
- Used only to sign Intermediate CA certificates
- Valid for 20-30 years
Intermediate CAs do the daily work of issuing certificates. If compromised:
- Only that Intermediate’s certificates are affected
- The Root can revoke the Intermediate and create a new one
- Damage is contained
Revocation: What Happens When Trust Breaks
When a private key is compromised or a certificate should no longer be trusted:
| Method | How it works | Trade-off |
|---|---|---|
| CRL (Certificate Revocation List) | CA publishes a list of revoked serial numbers | Can be stale (updated periodically) |
| OCSP (Online Certificate Status Protocol) | Client asks CA “is this cert still valid?” in real-time | Requires network, privacy concern |
| OCSP Stapling | Server fetches its own OCSP response and attaches it to the handshake | Best of both worlds |
1.8 Putting It All Together: A Complete Example
Let’s trace a complete HTTPS connection from start to finish, seeing every concept in action:
1.9 Key Takeaways
Before moving on to RHEL-specific certificate management, make sure you understand:
| Concept | One-sentence summary |
|---|---|
| Hashes | One-way fingerprints that detect any change (use SHA-256+). |
| Symmetric encryption | Same key encrypts and decrypts—fast but key distribution is hard. |
| Asymmetric encryption | Public/private key pairs—solves key distribution but is slow. |
| Hybrid encryption | Use asymmetric to exchange keys, then symmetric for data (TLS does this). |
| Digital signatures | Hash + private key = proof of identity and integrity. |
| Certificates | Bind a public key to an identity, signed by a trusted CA. |
| PKI | The trust architecture: Root CA → Intermediate CA → End-entity certificate. |
| TLS handshake | Authenticates server, exchanges keys, then encrypts everything. |
| Forward secrecy | Use ephemeral keys (ECDHE) so past sessions stay safe even if keys leak. |
Chapter Navigation
Chapter 2: Introduction to Certificates on RHEL
Welcome! This tutorial will take you from knowing nothing about digital certificates to confidently troubleshooting certificate issues on Red Hat Enterprise Linux systems.
2.1 Why This Tutorial?
You’re a RHEL administrator. One day, something breaks:
- Apache refuses to start:
SSL_CTX_use_certificate:ca md too weak - LDAP connections fail:
TLS: hostname does not match CN - certmonger shows:
CA_UNREACHABLE - curl returns:
SSL certificate problem: unable to get local issuer certificate
Sound familiar? These are certificate issues, and they’re everywhere in modern Linux systems.
This tutorial teaches you to:
- ✅ Understand what certificates are (RHEL perspective)
- ✅ Configure certificates for common RHEL services
- ✅ Troubleshoot certificate problems (primary goal!)
- ✅ Automate certificate lifecycle with RHEL tools
- ✅ Handle RHEL version differences (7, 8, 9, 10)
- ✅ Pass audits (FIPS, STIG, compliance)
2.2 Who Is This For?
Primary Audience:
- RHEL administrators and engineers
- Support engineers troubleshooting certificate issues
- Anyone managing RHEL systems with HTTPS, LDAPS, or TLS
Prerequisites:
- Basic Linux command line knowledge
- Access to RHEL systems (7, 8, 9, or 10)
- No prior certificate knowledge needed!
2.3 What Are Certificates? (In 60 Seconds)
Imagine you visit https://example.com. How does your browser know it’s really talking to example.com and not an imposter?
Answer: Digital certificates.
A certificate is like a digital ID card that:
- Proves identity (“I am example.com”)
- Enables encryption (secure communication)
- Is signed by a trusted authority (like a CA)
On RHEL Systems
Certificates are used everywhere:
- Web servers (Apache, NGINX) → HTTPS
- Directory services (OpenLDAP, FreeIPA) → LDAPS
- Mail servers (Postfix, Dovecot) → SMTPS/IMAPS
- Databases (PostgreSQL, MySQL) → TLS connections
- APIs and services (REST, microservices) → mTLS
- VPN tunnels → Secure connections
- Container registries → Secure images
Bottom line: If it’s networked and secure on RHEL, it probably uses certificates.
2.4 Your First Certificate Inspection
Let’s get hands-on immediately. On any RHEL system, run:
# Check a web server's TLS certificate (HTTPS, port 443)
echo | openssl s_client -connect access.redhat.com:443 2>/dev/null | openssl x509 -noout -text | head -20
# Alternative: inspect mail STARTTLS on port 25 (-starttls smtp matches SMTP, not SSH)
echo | openssl s_client -connect localhost:25 -starttls smtp 2>/dev/null | openssl x509 -noout -text | head -20
You’ll see output like:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0a:5d:d2:48:fc:4e:2f:e2:99:81:09:74:2d:4c:d5:69
Signature Algorithm: ecdsa-with-SHA384
Issuer: C=US, O=DigiCert Inc, CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1
Validity
Not Before: Oct 30 00:00:00 2025 GMT
Not After : Oct 27 23:59:59 2026 GMT
Subject: C=US, ST=North Carolina, L=Raleigh, O=Red Hat, Inc., CN=access.redhat.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:fc:08:bf:d2:d8:63:0c:84:a4:c8:dd:04:9c:8c:
99:4f:cb:93:31:7f:9e:64:27:ea:3d:a7:18:fd:3e:
4c:c2:58:8b:cb:f2:5c:6e:95:bf:f3:97:ba:b8:2b:
49:c6:51:30:f4:71:88:e3:fa:d4:f1:73:74:1d:e3:
2b:49:bc:9e:6e
What you’re seeing:
- Issuer: Who signed this certificate
- Subject: Who this certificate belongs to
- Validity: When it’s valid (Not Before / Not After dates)
- Signature Algorithm: How it’s secured (for example, ECDSA with SHA-384)
🎉 Congratulations! You just inspected your first certificate.
2.5 How Certificates Work (RHEL Context)
The Three Key Components
-
Certificate (
.crt,.pem)- Public information: “I am server.example.com”
- Contains the public key
- Stored in
/etc/pki/tls/certs/on RHEL
-
Private Key (
.key,.pem)- Secret! Never share this
- Used to prove you own the certificate
- Stored in
/etc/pki/tls/private/on RHEL (mode 600!)
-
Certificate Authority (CA)
- Issues and signs certificates
- Could be public (Let’s Encrypt, DigiCert)
- Or internal (FreeIPA, corporate CA)
- Trusted CAs stored in
/etc/pki/ca-trust/on RHEL
The Trust Chain
Root CA (trusted by RHEL system)
└─ Intermediate CA
└─ Server Certificate (your web server)
When someone connects to your RHEL server:
- Server sends its certificate
- Client verifies signature chain back to a trusted root CA
- If chain is valid → connection proceeds
- If chain breaks → error (and you get the support call!)
2.6 RHEL’s Certificate Architecture
Key Directories
/etc/pki/
├── ca-trust/
│ ├── source/anchors/ ← Put custom CA certs here
│ └── extracted/ ← System-wide trust store
│ ├── pem/ ← PEM format CAs
│ ├── openssl/ ← OpenSSL trust
│ └── java/ ← Java trust (cacerts)
├── tls/
│ ├── certs/ ← Server certificates
│ ├── private/ ← Private keys (mode 700!)
│ └── cert.pem ← Default certificate symlink
└── nssdb/ ← NSS database (Firefox, etc.)
Key Tools
# OpenSSL - Swiss army knife of certificates
openssl version # Check your version
# NSS Tools - For NSS databases
certutil -L -d /etc/pki/nssdb
# Trust Management - Add/remove CAs
update-ca-trust # RHEL's trust store updater
# Certificate Manager - Automatic renewal (RHEL 7+)
getcert list # Show tracked certificates
# Crypto-Policies - System-wide security (RHEL 8+)
update-crypto-policies --show # Check current policy
2.7 A Day in the Life: Certificate Scenarios
Scenario 1: Adding a Custom CA
# You have a corporate CA that signed your internal servers
sudo cp corporate-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
# Now RHEL trusts certificates signed by your corporate CA!
Scenario 2: Setting Up Apache HTTPS
# Install Apache with SSL/TLS
sudo dnf install httpd mod_ssl
# Generate a private key
sudo openssl genpkey -algorithm RSA -out /etc/pki/tls/private/server.key \
-pkeyopt rsa_keygen_bits:2048
# Generate a certificate signing request (CSR)
sudo openssl req -new -key /etc/pki/tls/private/server.key \
-out /tmp/server.csr \
-subj "/CN=web.example.com"
# Send CSR to CA, get certificate back, install it
sudo cp server.crt /etc/pki/tls/certs/
# Configure Apache, restart
sudo systemctl restart httpd
Scenario 3: Troubleshooting an Expired Certificate
# Service fails with: "certificate has expired"
# Check certificate expiration
sudo openssl x509 -in /etc/pki/tls/certs/server.crt -noout -dates
# Output shows:
# notAfter=Jan 15 23:59:59 2024 GMT ← Oops, expired!
# Renew certificate, replace file, restart service
2.8 RHEL Version Differences (Preview)
Certificate management has evolved significantly across RHEL versions:
| RHEL Version | Key Feature | Troubleshooting Focus |
|---|---|---|
| RHEL 7 | Traditional approach | Manual configuration, legacy TLS issues |
| RHEL 8 | Crypto-policies | Policy conflicts, certmonger integration |
| RHEL 9 | OpenSSL 3.x | Provider issues, stricter validation |
| RHEL 10 | Hardened defaults | Modern-only, enhanced tooling |
Don’t worry! Chapter 8 covers these version differences in detail.
2.9 Common Certificate Problems (Preview)
You’ll learn to troubleshoot:
Configuration Issues:
- Certificate/key mismatch
- Wrong file permissions
- Incorrect paths in config files
Trust Issues:
- Self-signed certificates rejected
- Unknown CA errors
- Chain validation failures
Expiration Issues:
- Expired certificates
- Clock skew problems
- Renewal failures
Version Issues:
- TLS version mismatches
- Cipher suite problems
- Crypto-policy conflicts (RHEL 8+)
- OpenSSL 3.x compatibility (RHEL 9+)
Service-Specific:
- Apache:
SSLCertificateFileerrors - NGINX:
ssl_certificateproblems - Postfix: TLS handshake failures
- LDAP:
TLS: hostname does not match
2.10 Learning Path Overview
This tutorial is organized for RHEL administrators:
Part 1: Foundations (Chapters 1-7)
Start here. Learn certificate basics in RHEL context.
Part 2: Version-Specific (Chapters 8-13)
Deep dive into RHEL 7, 8, 9, 10 differences.
Part 3: Services (Chapters 14-21)
Configure certificates for Apache, NGINX, Postfix, LDAP, etc.
Part 4: Automation (Chapters 22-26)
Master certmonger, crypto-policies, Let’s Encrypt, Ansible.
Part 5: Troubleshooting (Chapters 27-33) ⭐
This is where you become an expert! Systematic troubleshooting, common errors, emergency procedures.
Part 6: Migration (Chapters 34-37)
RHEL version upgrades and certificate migration.
Part 7: Security (Chapters 38-41)
FIPS mode, compliance, hardening, auditing.
Appendices
Optional advanced topics (Kubernetes, Vault, Zero Trust, etc.)
2.11 How to Use This Tutorial
For New Users
📖 Read chapters in order. Each builds on previous knowledge.
For Experienced Users
🎯 Jump to troubleshooting (Part 5) or specific services (Part 3).
For Support Engineers
🚨 Start with Chapter 27 (Troubleshooting Methodology), then dive into specifics.
Hands-On Labs
Every chapter includes practical examples. You’ll need:
- A RHEL system (VM or container is fine)
- Root or sudo access
- Internet connectivity (for package installs)
2.12 Key Concepts to Master
By the end of this tutorial, you’ll understand:
- ✅ What certificates are and why RHEL uses them
- ✅ How trust works on RHEL systems
- ✅ Where certificates live (
/etc/pki/) - ✅ Which tools to use (openssl, certutil, certmonger)
- ✅ Version differences (RHEL 7 vs 8 vs 9 vs 10)
- ✅ How to troubleshoot any certificate issue
- ✅ How to automate certificate lifecycle
- ✅ How to secure systems (FIPS, compliance)
2.13 Real-World Impact
Certificate issues cause:
- ❌ Service outages (expired certs)
- ❌ Security vulnerabilities (weak ciphers)
- ❌ Failed migrations (RHEL upgrades)
- ❌ Compliance failures (audit rejections)
- ❌ Lost productivity (troubleshooting time)
After this tutorial:
- ✅ Prevent issues before they happen
- ✅ Troubleshoot problems in minutes, not hours
- ✅ Automate certificate management
- ✅ Pass security audits
- ✅ Confidently migrate RHEL versions
2.14 Your First Exercise
Let’s verify your RHEL system is ready:
# Check RHEL version
cat /etc/redhat-release
# Check OpenSSL
openssl version
# Check if certmonger is installed
rpm -q certmonger
# Check if you can use sudo
sudo whoami
# Check internet connectivity (for package installs)
ping -c 3 access.redhat.com
# List current trusted CAs (sample)
trust list | head -20
✅ If all commands work, you’re ready to proceed!
2.15 Let’s Begin!
You now understand:
- What certificates are
- Why they matter on RHEL
- Where they live on the filesystem
- Which tools you’ll use
- What you’ll learn in this tutorial
Ready to dive deeper?
Quick Reference
┌────────────────────────────────────────────────────────────┐
│ CERTIFICATE QUICK START (RHEL) │
├────────────────────────────────────────────────────────────┤
│ View cert: openssl x509 -in cert.crt -noout -text │
│ Check expiry: openssl x509 -in cert.crt -noout -dates │
│ Add CA: cp ca.crt /etc/pki/ca-trust/source/anchors/ │
│ sudo update-ca-trust │
│ List tracked: getcert list │
│ Check policy: update-crypto-policies --show (RHEL 8+) │
└────────────────────────────────────────────────────────────┘
Cert location: /etc/pki/tls/certs/
Key location: /etc/pki/tls/private/ (mode 600!)
CA trust: /etc/pki/ca-trust/
🧪 Hands-On Lab
Lab 01: Environment Setup
Validate your RHEL environment and install essential certificate management tools
- 📁 Location:
labs/en_US/01-environment-setup/ - ⏱️ Time: 15-20 minutes
- 🎯 Level: Beginner
Chapter Navigation
| ← Previous: Chapter 1 - Cryptography, PKI Structure & Fundamentals | Next: Chapter 3 - RHEL Certificate Tools Overview → |
|---|
Chapter 3: RHEL Certificate Tools Overview
Learning Objective: Get familiar with the essential tools for managing certificates on RHEL so you know which tool to use for each task.
3.1 Your Certificate Toolkit
When working with certificates on RHEL, you’ll use these core tools:
| Tool | Primary Use | RHEL Versions | When to Use |
|---|---|---|---|
| openssl | Certificate operations, testing | All | Generate keys/CSRs, inspect certs, test connections |
| certutil | NSS database management | All | Firefox/Mozilla-style cert DBs |
| update-ca-trust | Trust store management | All | Add/remove trusted CAs |
| certmonger | Automatic renewal | All | Track and auto-renew certificates |
| crypto-policies | System-wide security | RHEL 8+ | Control TLS versions and ciphers |
| getcert | certmonger CLI | All | Request and manage tracked certs |
| trust | P11-kit trust management | All (enhanced RHEL 8+) | Advanced trust operations |
3.2 OpenSSL - The Swiss Army Knife
Available: All RHEL versions
Package: openssl
Version Differences
# Check your version
openssl version
# RHEL 7: OpenSSL 1.0.2k-26
# RHEL 8: OpenSSL 1.1.1k-14
# RHEL 9: OpenSSL 3.5.5-2
# RHEL 10: OpenSSL 3.5.5-2
Common Uses
#============================================#
# INSPECT CERTIFICATES
#============================================#
# View certificate details
openssl x509 -in cert.crt -noout -text
# Check expiration
openssl x509 -in cert.crt -noout -dates
openssl x509 -in cert.crt -noout -checkend 86400 # Check if expires in 24h
# View certificate subject
openssl x509 -in cert.crt -noout -subject -issuer
#============================================#
# GENERATE KEYS
#============================================#
# RHEL 7 style (still works on all versions)
openssl genrsa -out server.key 2048
# RHEL 8+ modern style (recommended)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048
# RHEL 9+ EC key (elliptic curve)
openssl genpkey -algorithm EC -out ec.key -pkeyopt ec_paramgen_curve:P-256
#============================================#
# CREATE CSR (Certificate Signing Request)
#============================================#
# Basic CSR
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=server.example.com"
# CSR with SANs (required for modern browsers!)
openssl req -new -key server.key -out server.csr \
-subj "/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com,DNS:www.example.com"
#============================================#
# TEST CONNECTIONS
#============================================#
# Test HTTPS
openssl s_client -connect server.example.com:443 -servername server.example.com
# Test specific TLS version
openssl s_client -connect server.example.com:443 -tls1_2
openssl s_client -connect server.example.com:443 -tls1_3
# Test LDAPS
openssl s_client -connect ldap.example.com:636
# Test SMTP with STARTTLS
openssl s_client -connect mail.example.com:25 -starttls smtp
Version-Specific Differences
RHEL 7 (OpenSSL 1.0.2k):
- ✅ Stable and well-tested
- ❌ No TLS 1.3 support
- ❌ Older command syntax
RHEL 8 (OpenSSL 1.1.1k):
- ✅ TLS 1.3 support
- ✅ Modern command syntax
- ✅ Better defaults
RHEL 9/10 (OpenSSL 3.5.5):
- ✅ Provider architecture
- ✅ Enhanced FIPS support
- ⚠️ API changes (affects custom apps)
- ⚠️ Legacy algorithms require
-provider legacy
3.3 certutil - NSS Database Tool
Available: All RHEL versions
Package: nss-tools
Used for Mozilla/Firefox-style certificate databases.
Common Uses
#============================================#
# MANAGE NSS DATABASE
#============================================#
# Create new database
certutil -N -d /etc/pki/nssdb
# List certificates
certutil -L -d /etc/pki/nssdb
# Add CA certificate
certutil -A -n "My CA" -t "CT,C,C" -d /etc/pki/nssdb -i ca.crt
# Delete certificate
certutil -D -n "Certificate Name" -d /etc/pki/nssdb
# Export certificate
certutil -L -n "Certificate Name" -d /etc/pki/nssdb -a > exported.crt
When to Use certutil
- Managing Firefox/Thunderbird certificates
- Working with applications that use NSS (many Red Hat services)
- When you see
.dbfiles in/etc/pki/nssdb/
3.4 update-ca-trust - Trust Store Management
Available: All RHEL versions
Package: ca-certificates (installed by default)
Manages which Certificate Authorities (CAs) your system trusts.
How It Works
Your Custom CAs
↓
/etc/pki/ca-trust/source/anchors/
↓
update-ca-trust extract
↓
/etc/pki/ca-trust/extracted/
├── pem/tls-ca-bundle.pem (OpenSSL/Python/Ruby)
├── openssl/ca-bundle.trust.crt (OpenSSL specific)
└── java/cacerts (Java applications)
Common Uses
#============================================#
# ADD CUSTOM CA
#============================================#
# Step 1: Copy CA certificate
sudo cp corporate-ca.crt /etc/pki/ca-trust/source/anchors/
# Step 2: Update trust store
sudo update-ca-trust extract
# That's it! Now all applications trust this CA
#============================================#
# REMOVE/BLACKLIST CA (RHEL 8+)
#============================================#
# Blacklist a compromised CA
sudo cp compromised-ca.crt /etc/pki/ca-trust/source/blacklist/
sudo update-ca-trust extract
#============================================#
# VERIFY TRUST
#============================================#
# Check if certificate is trusted
openssl verify /path/to/cert.crt
# List all trusted CAs
trust list | grep "certificate-authority"
# Search for specific CA
trust list | grep -i "Let's Encrypt"
Key Directories
/etc/pki/ca-trust/
├── source/
│ ├── anchors/ ← Add your trusted CAs here
│ └── blacklist/ ← Blacklist CAs (RHEL 8+)
└── extracted/
├── pem/ ← Used by most apps
├── openssl/ ← OpenSSL-specific
└── java/ ← Java applications
3.5 certmonger - Automatic Certificate Renewal
Available: All RHEL versions
Package: certmonger
The “set it and forget it” tool for certificates.
What It Does
certmonger:
- Tracks certificate expiration dates
- Automatically renews before expiry
- Works with multiple CA workflows (IPA, local CA, external helpers)
- Runs post-renewal commands (e.g., restart services)
Basic Workflow
#============================================#
# INSTALLATION
#============================================#
sudo dnf install certmonger # RHEL 8/9/10; use yum on RHEL 7
sudo systemctl enable --now certmonger
#============================================#
# REQUEST CERTIFICATE
#============================================#
# From FreeIPA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-D web.example.com \
-K host/web.example.com@REALM
# Self-signed (for testing)
sudo getcert request \
-f /etc/pki/tls/certs/test.crt \
-k /etc/pki/tls/private/test.key
#============================================#
# MONITOR CERTIFICATES
#============================================#
# List all tracked certificates
sudo getcert list
# Check specific certificate
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Watch for renewal
sudo journalctl -u certmonger -f
Key Features by Version
RHEL 7:
- Basic tracking and renewal
- IPA integration
- Manual configuration
RHEL 8:
- Enhanced IPA integration
- Better error reporting
- Post-save commands
RHEL 9:
- Improved monitoring
- Better status reporting
- Same strong fit for IPA and tracked renewals
# RHEL 9 - Native FreeIPA integration
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
3.6 crypto-policies - System-Wide Security (RHEL 8+)
Available: RHEL 8, 9, 10 only
Package: crypto-policies (installed by default)
GAME CHANGER: Control TLS versions, ciphers, and key sizes system-wide!
The Big Idea
Instead of configuring every application individually:
❌ OLD WAY (RHEL 7):
- Configure Apache SSL ciphers
- Configure NGINX SSL ciphers
- Configure Postfix TLS settings
- Configure OpenLDAP TLS settings
- Configure every application...
✅ NEW WAY (RHEL 8+):
- Set ONE system policy
- All applications follow it automatically!
Available Policies
# Check current policy
update-crypto-policies --show
# Policies:
# DEFAULT - Balanced security (TLS 1.2+, RSA 2048+)
# LEGACY - Compatibility mode (allows TLS 1.0/1.1)
# FUTURE - Stricter security (TLS 1.2+, RSA 3072+)
# FIPS - Federal compliance mode
Policy Comparison
| Feature | LEGACY | DEFAULT | FUTURE | FIPS |
|---|---|---|---|---|
| TLS 1.0/1.1 | ✅ Allowed | ❌ Blocked | ❌ Blocked | ❌ Blocked |
| TLS 1.2 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| TLS 1.3 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Min RSA | 1024 bits | 2048 bits | 3072 bits | 2048 bits |
| SHA-1 sigs | ⚠️ Allowed | ❌ Blocked | ❌ Blocked | ❌ Blocked |
| 3DES cipher | ⚠️ Allowed | ❌ Blocked | ❌ Blocked | ❌ Blocked |
Common Uses
#============================================#
# CHANGE POLICY
#============================================#
# Set FUTURE policy (stricter)
sudo update-crypto-policies --set FUTURE
# Reboot or restart services
# Temporarily use LEGACY (for old systems)
sudo update-crypto-policies --set LEGACY
# Note: LEGACY should be temporary!
#============================================#
# CUSTOM POLICIES (RHEL 9+)
#============================================#
# Subpolicies - modify existing policy
sudo update-crypto-policies --set DEFAULT:NO-SHA1
sudo update-crypto-policies --set FUTURE:AD-SUPPORT
#============================================#
# TROUBLESHOOT POLICY ISSUES
#============================================#
# If service fails after policy change:
# 1. Check current policy
update-crypto-policies --show
# 2. Check application config
cat /etc/crypto-policies/back-ends/opensslcnf.config
# 3. Test with LEGACY temporarily
sudo update-crypto-policies --set LEGACY
sudo systemctl restart <service>
What crypto-policies Controls
Automatically configures:
- OpenSSL
- GnuTLS
- NSS
- OpenJDK/Java
- BIND
- Kerberos
- OpenSSH
- And more!
Bottom line: Change one setting, update system-wide security. Brilliant!
3.7 Tool Selection Guide
“Which tool should I use?”
┌─────────────────────────────────────────────────────────────┐
│ CERTIFICATE TOOL DECISION TREE │
└─────────────────────────────────────────────────────────────┘
I need to...
│
├─ Inspect a certificate
│ └─ Use: openssl x509 -in cert.crt -noout -text
│
├─ Generate a key/CSR
│ └─ Use: openssl genpkey / openssl req
│
├─ Test a TLS connection
│ └─ Use: openssl s_client -connect host:port
│
├─ Add a trusted CA system-wide
│ └─ Use: copy to /etc/pki/ca-trust/source/anchors/
│ then: update-ca-trust
│
├─ Auto-renew certificates
│ └─ Use: certmonger (getcert/ipa-getcert)
│
├─ Change system TLS policy (RHEL 8+)
│ └─ Use: update-crypto-policies --set <POLICY>
│
├─ Work with Firefox/NSS databases
│ └─ Use: certutil
│
└─ Troubleshoot certificate issues
└─ Use: Chapter 27 methodology!
3.8 Tool Availability Matrix
| Tool | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 | Notes |
|---|---|---|---|---|---|
| openssl | 1.0.2k | 1.1.1k | 3.5.5 | 3.5.5 | Core tool |
| certutil | ✅ | ✅ | ✅ | ✅ | NSS tool |
| update-ca-trust | ✅ | ✅ Enhanced | ✅ Enhanced | ✅ Enhanced | Trust mgmt |
| certmonger | ✅ | ✅ Enhanced | ✅ ACME | ✅ ACME | Auto-renewal |
| crypto-policies | ❌ | ✅ | ✅ Subpolicies | ✅ Enhanced | System policy |
| getcert | ✅ | ✅ | ✅ | ✅ | certmonger CLI |
| trust | ✅ Basic | ✅ | ✅ | ✅ | p11-kit tool |
3.9 Installation Check
Verify you have the essential tools:
#============================================#
# CHECK INSTALLED TOOLS
#============================================#
# OpenSSL (should be installed by default)
openssl version
# NSS tools
rpm -q nss-tools || echo "Install with: sudo dnf install nss-tools"
# certmonger
rpm -q certmonger || echo "Install with: sudo dnf install certmonger (RHEL 8+); sudo yum install certmonger (RHEL 7)"
# Check crypto-policies (RHEL 8+ only)
which update-crypto-policies &>/dev/null && \
echo "Crypto-policies available: $(update-crypto-policies --show)" || \
echo "Crypto-policies not available (RHEL 7 or earlier)"
3.10 Quick Reference Commands
# === OpenSSL ===
openssl version # Check version
openssl x509 -in cert.crt -noout -text # Inspect certificate
openssl s_client -connect host:443 # Test HTTPS
# === Trust Store ===
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust # Add trusted CA
# === certmonger ===
sudo getcert list # List tracked certs
sudo getcert list -f /path/to/cert.crt # Check specific cert
sudo journalctl -u certmonger -f # Watch logs
# === Crypto-Policies (RHEL 8+) ===
update-crypto-policies --show # Current policy
sudo update-crypto-policies --set <POL> # Change policy
# === NSS ===
certutil -L -d /etc/pki/nssdb # List NSS certs
3.11 What’s Next?
Now that you know the tools, you’ll learn:
- Chapter 4: Basic cryptography concepts
- Chapter 5: Understanding X.509 certificates
- Chapter 6: RHEL trust store deep dive
- Chapter 22: certmonger mastery (detailed)
- Chapter 23: Crypto-policies deep dive (detailed)
Quick Reference Card
┌──────────────────────────────────────────────────────────┐
│ RHEL CERTIFICATE TOOLS CHEAT SHEET │
├──────────────────────────────────────────────────────────┤
│ Inspect: openssl x509 -in cert.crt -noout -text │
│ Test: openssl s_client -connect host:443 │
│ Add CA: cp ca.crt /etc/pki/ca-trust/source/anchors/ │
│ sudo update-ca-trust │
│ Auto-renew: sudo getcert list │
│ Policy: update-crypto-policies --show (RHEL 8+) │
│ NSS: certutil -L -d /etc/pki/nssdb │
└──────────────────────────────────────────────────────────┘
Chapter Navigation
| ← Previous: Chapter 2 - Introduction to Certificates on RHEL | Next: Chapter 4 - Basic Cryptography for RHEL Admins → |
|---|
Chapter 4: Basic Cryptography for RHEL Admins
Practical Focus: Learn the cryptography concepts you need to manage certificates on RHEL - no PhD required!
4.1 Symmetric vs Asymmetric
Symmetric crypto (e.g., AES) relies on a single shared secret. In contrast, asymmetric crypto provides two complementary keys:
- Public key — share freely, used for encryption or signature verification.
- Private key — keep secret, used for decryption or signing.
4.2 RSA in a Nutshell
- Select two large primes p and q.
- Compute modulus
n = p × q. - Derive public exponent
eand private exponentdsuch thate × d ≡ 1 (mod φ(n)). - The pair
(n, e)is public;(n, d)is private.
Strength derives from the difficulty of factoring n.
4.3 Elliptic-Curve Cryptography (ECC)
ECC offers comparable security with much smaller key sizes by operating over points on elliptic curves. Popular curves include secp256r1 (P-256) and Curve25519.
| Algorithm | 128-bit security key size |
|---|---|
| RSA | 3072 bits |
| ECC | 256 bits |
4.4 Key Generation Lab (OpenSSL)
# Generate 3072-bit RSA private key
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out rsa.key.pem
# Extract public key
openssl pkey -in rsa.key.pem -pubout -out rsa.pub.pem
# Generate P-256 EC key-pair
openssl ecparam -genkey -name prime256v1 -out ec.key.pem
openssl pkey -in ec.key.pem -pubout -out ec.pub.pem
We will reuse these keys in later chapters to create certificates.
4.5 Hybrid Encryption
In TLS a symmetric session key is exchanged using asymmetric crypto (RSA or ECDHE). This yields the best of both worlds: efficiency and secure key exchange.
4.6 RHEL-Specific Considerations
Key Generation on RHEL by Version
RHEL 7 (OpenSSL 1.0.2k):
# Old style (still works on all versions)
openssl genrsa -out server.key 2048
# Extract public key
openssl rsa -in server.key -pubout -out server.pub
RHEL 8+ (OpenSSL 1.1.1k / 3.5.5):
# Modern style (recommended)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048
# Extract public key
openssl pkey -in server.key -pubout -out server.pub
Minimum Key Sizes by RHEL Version
| RHEL Version | Minimum RSA | Minimum ECC | Enforced By |
|---|---|---|---|
| RHEL 7 | None (weak allowed) | None | Manual config |
| RHEL 8 | 2048 bits | P-256 | crypto-policy DEFAULT |
| RHEL 9 | 2048 bits | P-256 | crypto-policy DEFAULT |
| RHEL 10 | 2048 bits | P-256 | crypto-policy DEFAULT |
Recommendation: Always use RSA 2048+ or ECC P-256+ for compatibility!
Testing on RHEL
# Generate test key pair (RHEL 8+)
openssl genpkey -algorithm RSA -out test.key -pkeyopt rsa_keygen_bits:2048
# Verify key
openssl pkey -in test.key -text -noout
# Create test data
echo "Hello RHEL" > message.txt
# Sign with private key
openssl dgst -sha256 -sign test.key -out message.sig message.txt
# Verify with public key
openssl dgst -sha256 -verify test.pub -signature message.sig message.txt
# Verified OK
Quick Reference
┌─────────────────────────────────────────────────────────┐
│ CRYPTOGRAPHY FOR RHEL ADMINS │
├─────────────────────────────────────────────────────────┤
│ Asymmetric: Public key (share) + Private key (secret) │
│ Algorithms: RSA, ECC (Elliptic Curve) │
│ │
│ RSA Sizes: 2048 bits (minimum on RHEL 8+) │
│ 4096 bits (recommended) │
│ │
│ ECC Curves: P-256 (secp256r1) - minimum │
│ P-384 (secp384r1) - recommended │
│ │
│ RHEL 7: openssl genrsa -out key 2048 │
│ RHEL 8/9/10: openssl genpkey -algorithm RSA -out key │
│ │
│ Use case: TLS/SSL certificates │
│ Security: Private key MUST be protected (chmod 600) │
└─────────────────────────────────────────────────────────┘
🧪 Hands-On Lab
Lab 02: Key Generation
Practice generating cryptographic key pairs in this hands-on exercise:
-
Generate RSA keys (2048-bit and 4096-bit)
-
Generate ECC keys (P-256 and P-384)
-
Extract public keys from private keys
-
Compare key sizes and performance
-
📁 Location:
labs/en_US/02-key-generation/ -
⏱️ Time: 20-25 minutes
-
🎯 Level: Beginner
Chapter Navigation
| ← Previous: Chapter 3 - RHEL Certificate Tools Overview | Next: Chapter 5 - X.509 Certificates on RHEL → |
|---|
Chapter 5: X.509 Certificates on RHEL
Standard Format: X.509 is the certificate standard used everywhere on RHEL. Learn its structure and how to work with it on Red Hat systems.
5.1 Origins of the Standard
X.509 emerged from the X.500 directory project (ITU-T, 1988) to define a standard identity certificate—a document that binds a public key to a subject name, signed by a trusted authority.
5.2 Certificate Anatomy
| Field | Purpose |
|---|---|
| Version | Usually v3 (adds extensions) |
| Serial Number | Unique per CA |
| Signature Algorithm | e.g., sha256WithRSAEncryption |
| Issuer | Distinguished Name (DN) of CA |
| Validity | Not Before & Not After dates |
| Subject | DN of entity (CN, O, C…) |
| Subject Public Key Info | Algorithm + Key |
| Extensions | Key Usage, SAN, CRL DP, etc. |
| Signature | CA’s digital signature |
5.3 Common Extensions
- Subject Alternative Name (SAN) — Hosts/IPs bound to cert.
- Key Usage / Extended Key Usage — Permitted operations (TLS server, code signing…).
- Basic Constraints — Indicates if cert can sign others (
CA:TRUE).
5.4 Viewing a Certificate
openssl x509 -in server.crt -noout -text
Observe each section matches the table above.
5.5 PEM vs DER Encodings
- PEM — Base64 +
-----BEGIN CERTIFICATE-----headers (most common on RHEL). - DER — Binary ASN.1, useful for embedded devices.
5.6 X.509 on RHEL Systems
Certificate Locations on RHEL
# Standard RHEL certificate locations
/etc/pki/tls/certs/ # Server certificates (public)
/etc/pki/tls/private/ # Private keys (mode 600!)
/etc/pki/ca-trust/ # Trusted CA certificates
/etc/pki/nssdb/ # NSS database (Firefox, etc.)
# Service-specific locations
/etc/httpd/conf/ssl.crt/ # Apache (alternative)
/etc/nginx/certs/ # NGINX (custom)
/var/lib/pgsql/data/ # PostgreSQL
/etc/openldap/certs/ # OpenLDAP
Viewing Certificates on RHEL
# View full certificate details
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -text
# Quick checks (RHEL sysadmin focus)
openssl x509 -in server.crt -noout -subject # Who is it for?
openssl x509 -in server.crt -noout -issuer # Who signed it?
openssl x509 -in server.crt -noout -dates # When is it valid?
openssl x509 -in server.crt -noout -ext subjectAltName # SANs (critical!)
# Check if expired
openssl x509 -in server.crt -noout -checkend 0
# Exit 0 = valid, Exit 1 = expired
RHEL Version Differences for X.509
| RHEL Version | OpenSSL | Validation Strictness | Key Changes |
|---|---|---|---|
| RHEL 7 | 1.0.2k | Standard | SANs recommended |
| RHEL 8 | 1.1.1k | Stricter | SANs strongly recommended |
| RHEL 9 | 3.5.5 | Very strict | SANs required, SHA-1 blocked |
| RHEL 10 | 3.5.5 | Very strict | Same as RHEL 9 |
Key Point: Modern browsers and RHEL 9+ require SANs (Subject Alternative Names)!
Creating X.509 Certificates on RHEL
# Complete workflow on RHEL
# Step 1: Generate private key
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048
# Step 2: Create CSR (Certificate Signing Request)
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=State/O=Company/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com,DNS:www.example.com"
# Step 3: Self-signed (testing only!)
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
# Step 4: View your X.509 certificate
openssl x509 -in server.crt -noout -text
# Step 5: Install on RHEL
sudo cp server.crt /etc/pki/tls/certs/
sudo cp server.key /etc/pki/tls/private/
sudo chmod 600 /etc/pki/tls/private/server.key
Quick Reference
┌─────────────────────────────────────────────────────────────────┐
│ X.509 CERTIFICATES ON RHEL │
├─────────────────────────────────────────────────────────────────┤
│ Standard: X.509 v3 (with extensions) │
│ Encoding: PEM (Base64, human-readable) │
│ │
│ View: openssl x509 -in cert.crt -noout -text │
│ Subject: openssl x509 -in cert.crt -noout -subject │
│ Expiry: openssl x509 -in cert.crt -noout -dates │
│ SANs: openssl x509 -in cert.crt -noout -ext subjectAltName │
│ │
│ Location: /etc/pki/tls/certs/ (certificates) │
│ /etc/pki/tls/private/ (keys, mode 600!) │
│ │
│ Critical: SANs are REQUIRED on RHEL 9+ │
│ SHA-256+ signature required on RHEL 8+ │
└─────────────────────────────────────────────────────────────────┘
🧪 Hands-On Lab
Lab 04: X.509 Certificates
Create self-signed certificates, generate CSRs, inspect certificates, and convert formats
- 📁 Location:
labs/en_US/04-x509-certificates/ - ⏱️ Time: 25-30 minutes
- 🎯 Level: Beginner
Chapter Navigation
| ← Previous: Chapter 4 - Basic Cryptography for RHEL Admins | Next: Chapter 6 - RHEL Trust Store Deep Dive → |
|---|
Chapter 6: RHEL Trust Store Deep Dive
Trust Architecture: Understanding how RHEL validates certificates and manages trusted CAs is essential for troubleshooting certificate problems. This chapter goes beyond the basics: it covers the internal mechanics of
update-ca-trust, how p11-kit processes trust sources, what happens with duplicate certificates, and how to debug trust issues withtrust list.
6.1 RHEL Trust Store Architecture
How RHEL Validates Certificates
When any application on RHEL validates a certificate:
- Check certificate signature using issuer’s public key
- Find issuer certificate in trust store
- Repeat until reaching trusted root CA
- Verify root CA is trusted by RHEL system
Trust store location: /etc/pki/ca-trust/
The Role of p11-kit
RHEL’s trust store is not a flat file that applications read directly. It is a managed system built on top of p11-kit, which provides a PKCS#11 trust module. The key components are:
| Component | Role |
|---|---|
p11-kit | Middleware that loads trust modules and exposes them via PKCS#11 |
p11-kit-trust | The trust module (/usr/lib64/pkcs11/p11-kit-trust.so) that reads source certificates |
update-ca-trust | Shell script that invokes p11-kit extract to regenerate extracted bundles |
trust | CLI front-end to inspect and modify trust objects managed by p11-kit |
Applications like curl, wget, OpenSSL, GnuTLS, and NSS all consume the extracted bundles. They never read the source directories directly.
6.2 Trust Store Directory Structure
RHEL 7/8:
/etc/pki/ca-trust/
├── source/
│ ├── anchors/ ← Admin-added trusted CA certificates
│ ├── blacklist/ ← Admin-added distrusted certificates
│ └── ca-bundle.legacy.crt ← Legacy bundle (compatibility only)
├── extracted/
│ ├── pem/
│ │ ├── tls-ca-bundle.pem ← For TLS clients (curl, wget, Python...)
│ │ ├── email-ca-bundle.pem ← For S/MIME email validation
│ │ └── objsign-ca-bundle.pem ← For code signing verification
│ ├── openssl/
│ │ └── ca-bundle.trust.crt ← OpenSSL "trusted certificate" format
│ ├── java/
│ │ └── cacerts ← Java JKS keystore
│ └── edk2/
│ └── cacerts.bin ← UEFI firmware format
└── README
/usr/share/pki/ca-trust-source/
├── anchors/ ← Package-provided trust anchors (from RPMs)
├── blacklist/ ← Package-provided distrusted certificates
└── ca-bundle.trust.p11-kit ← Mozilla CA bundle shipped by ca-certificates RPM
RHEL 9/10+: The blacklist/ directory was renamed to blocklist/:
/etc/pki/ca-trust/
├── source/
│ ├── anchors/ ← Admin-added trusted CA certificates
│ ├── blocklist/ ← Admin-added distrusted certificates
│ └── ca-bundle.legacy.crt ← Legacy bundle (compatibility only)
├── extracted/
│ └── (same structure as above)
└── README
/usr/share/pki/ca-trust-source/
├── anchors/ ← Package-provided trust anchors (from RPMs)
├── blocklist/ ← Package-provided distrusted certificates
└── ca-bundle.trust.p11-kit ← Mozilla CA bundle shipped by ca-certificates RPM
Naming convention: Throughout this chapter,
blacklist/refers to the RHEL 7/8 directory name andblocklist/refers to the RHEL 9/10+ directory name. Both serve the same purpose. When you see a path likesource/blacklist/, substitutesource/blocklist/on RHEL 9+.
Source Directories Priority
The update-ca-trust pipeline reads certificates from multiple source directories, processed in a defined order:
| Priority | Directory | Managed by |
|---|---|---|
| 1 (lowest) | /usr/share/pki/ca-trust-source/ | RPM packages (ca-certificates) |
| 2 (highest) | /etc/pki/ca-trust/source/ | System administrator |
Within each location:
anchors/— Certificates placed here are treated as trusted CAsblacklist/(RHEL 7/8) orblocklist/(RHEL 9+) — Certificates placed here are treated as explicitly distrusted
The /etc/pki/ paths always override /usr/share/pki/ paths. This follows the standard RHEL convention: /usr/share/ holds package defaults, /etc/ holds admin customizations.
6.3 What Happens When You Run update-ca-trust
The Execution Pipeline
update-ca-trust is a shell script (inspect it yourself: cat /usr/bin/update-ca-trust). When you execute sudo update-ca-trust, the following sequence occurs:
Step 1: Collect all trust sources
p11-kit reads every certificate file from these directories:
/usr/share/pki/ca-trust-source/anchors/
/usr/share/pki/ca-trust-source/blacklist/ ← RHEL 7/8
/usr/share/pki/ca-trust-source/blocklist/ ← RHEL 9+
/usr/share/pki/ca-trust-source/ca-bundle.trust.p11-kit
/etc/pki/ca-trust/source/anchors/
/etc/pki/ca-trust/source/blacklist/ ← RHEL 7/8
/etc/pki/ca-trust/source/blocklist/ ← RHEL 9+
It accepts PEM (.pem, .crt), DER (.der), and p11-kit trust objects (.p11-kit) formats.
Step 2: Parse trust attributes
For each certificate, p11-kit determines its trust disposition. The PEM format of the certificate file matters:
-----BEGIN TRUSTED CERTIFICATE-----(OpenSSL “trusted” format) — Contains the certificate plus auxiliary trust data: explicit lists of trusted and rejected key usage OIDs. p11-kit reads these embedded trust/reject attributes directly.-----BEGIN CERTIFICATE-----(plain PEM) or DER — Contains only the certificate with no trust metadata. p11-kit assigns trust based solely on the directory where the file is placed (anchors/= trusted,blacklist//blocklist/= distrusted)..p11-kitformat files — Contain PKCS#11 trust objects with fine-grained attributes (trusted,x-distrusted, purpose OIDs). Used by theca-bundle.trust.p11-kitfile shipped with theca-certificatespackage.
Critical: The
BEGIN TRUSTED CERTIFICATEandBEGIN CERTIFICATEformats are not interchangeable. ABEGIN TRUSTED CERTIFICATEfile carries auxiliary trust data — explicit lists of trusted and/or rejected use OIDs. When no uses are listed (empty trust attributes), p11-kit interprets this as “trusted for nothing” — effectively distrusted. If the same certificate also exists as a plainBEGIN CERTIFICATE(which implies trust for all purposes), p11-kit detects contradictory trust assertions and marks the certificate as distrusted. This conflict occurs in two cases: (1) theBEGIN TRUSTED CERTIFICATEhas explicit rejected uses, or (2) it has empty trust attributes (no trusted uses, no rejected uses). See Section 6.6 for details.
Step 3: Merge and resolve conflicts
When the same certificate (matched by its DER-encoded content) appears in multiple source locations, p11-kit applies merge rules:
-
Distrust wins over trust. If a certificate appears in both
anchors/andblacklist//blocklist/, it is distrusted. -
Admin overrides packages. Trust attributes set in
/etc/pki/ca-trust/source/override those from/usr/share/pki/ca-trust-source/. -
Explicit attributes override defaults. A
.p11-kitfile with specific purpose restrictions overrides the blanket trust given to a plain PEM inanchors/. -
Conflicting trust formats cause distrust. If the same certificate (identical fingerprint) appears as both
BEGIN TRUSTED CERTIFICATE(or.p11-kit) andBEGIN CERTIFICATE, distrust occurs in two cases:- The
BEGIN TRUSTED CERTIFICATEhas explicit rejected uses — the rejection contradicts the plain PEM’s implicit full trust. - The
BEGIN TRUSTED CERTIFICATEhas empty trust attributes (no trusted uses, no rejected uses) — p11-kit interprets “no uses listed” as “trusted for nothing,” which contradicts the plain PEM’s implicit “trusted for all purposes.”
In both cases, p11-kit marks the certificate as distrusted.
- The
This is the single most common cause of unexpected distrust. An administrator copies a plain PEM file into
source/anchors/not realizing that the same certificate already exists inca-bundle.trust.p11-kitwith rejected use attributes or empty trust. The result: the CA is distrusted afterupdate-ca-trust, and services that depend on it break silently.
Step 4: Extract to format-specific bundles
p11-kit runs extraction commands for each output format:
# PEM bundle for TLS (most commonly consumed)
p11-kit extract --format=pem-bundle \
--filter=ca-anchors \
--overwrite \
--purpose=server-auth \
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
# PEM bundle for email (S/MIME)
p11-kit extract --format=pem-bundle \
--filter=ca-anchors \
--overwrite \
--purpose=email-protection \
/etc/pki/ca-trust/extracted/pem/email-ca-bundle.pem
# PEM bundle for code signing
p11-kit extract --format=pem-bundle \
--filter=ca-anchors \
--overwrite \
--purpose=code-signing \
/etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem
# OpenSSL "trusted certificate" format (includes trust/reject attributes)
p11-kit extract --format=openssl-bundle \
--filter=certificates \
--overwrite \
/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
# Java keystore
p11-kit extract --format=java-cacerts \
--filter=ca-anchors \
--overwrite \
--purpose=server-auth \
/etc/pki/ca-trust/extracted/java/cacerts
# UEFI EDK2 format
p11-kit extract --format=edk2-cacerts \
--filter=ca-anchors \
--overwrite \
--purpose=server-auth \
/etc/pki/ca-trust/extracted/edk2/cacerts.bin
Step 5: Update compatibility symlinks
The system maintains symlinks so that legacy paths point to the extracted bundles:
/etc/pki/tls/certs/ca-bundle.crt → /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
/etc/pki/tls/certs/ca-bundle.trust.crt → /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
/etc/pki/java/cacerts → /etc/pki/ca-trust/extracted/java/cacerts
update-ca-trust vs update-ca-trust extract
These are functionally identical. The update-ca-trust script accepts extract as a subcommand, but running update-ca-trust with no arguments defaults to the extract action. There is no difference in behavior.
# These two commands produce identical results:
sudo update-ca-trust
sudo update-ca-trust extract
The extract subcommand exists for explicitness in scripts and documentation. Historically, update-ca-trust also supported enable and disable subcommands (to switch between the new p11-kit-managed trust store and the legacy flat-file approach), but those are no longer relevant on modern RHEL systems where p11-kit management is always active.
# Check current status (informational only on modern RHEL)
update-ca-trust check
Verifying What Changed
After running update-ca-trust, you can verify the result:
# Count trusted CAs in the PEM bundle
grep -c '^-----BEGIN CERTIFICATE-----' /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
# List all trusted CAs via p11-kit
trust list --filter=ca-anchors | grep "label:" | wc -l
# Check if a specific CA is present
trust list | grep -A 4 "My Company CA"
6.4 Adding Custom CA Certificates
Step-by-Step
# Step 1: Copy CA certificate (PEM or DER format)
sudo cp company-ca.crt /etc/pki/ca-trust/source/anchors/
# Step 2: Update trust store
sudo update-ca-trust
# Step 3: Verify addition
trust list | grep -i "company"
# Step 4: Test
curl https://internal-server.example.com
Works identically on RHEL 7, 8, 9, 10.
Adding with Purpose Restrictions
If a CA should only be trusted for TLS server authentication (not email signing or code signing), use the trust command instead of the simple file copy approach:
# Trust only for TLS server authentication
sudo trust anchor --store /path/to/company-ca.crt
# Or with explicit purpose restriction via p11-kit format
# Create a .p11-kit file with restricted trust:
cat > /tmp/company-ca.p11-kit <<'EOF'
[p11-kit-object-v1]
class: x-certificate-extension
label: "My Company CA"
x-public-key-info: <extracted-from-cert>
[p11-kit-object-v1]
class: certificate
label: "My Company CA"
certificate-type: x-509
java-midp-security-domain: 0
trusted: true
x-distrusted: false
[p11-kit-object-v1]
class: x-certificate-extension
label: "My Company CA"
object-id: 2.5.29.37
value: "%06%08%2b%06%01%05%05%07%03%01"
EOF
The trust anchor --store command handles this automatically and is the recommended approach on RHEL 8+.
6.5 RHEL Version Trust Store Features
Trust Management Evolution
| RHEL Version | trust Command | Distrusting | Distrust Directory | Notes |
|---|---|---|---|---|
| RHEL 7 | Basic | Limited | blacklist/ | Manual management |
| RHEL 8 | Enhanced | Full support | blacklist/ | p11-kit integration |
| RHEL 9 | Enhanced | Full support | blocklist/ | Renamed from blacklist/ |
| RHEL 10 | Enhanced | Full support | blocklist/ | Same as RHEL 9 |
RHEL 8+ Enhancement:
# Advanced trust management (RHEL 8+)
trust anchor /path/to/ca.crt --purpose server-auth
trust anchor --remove "pkcs11:id=%CERT_ID%"
trust list --filter=ca-anchors
# Distrust a compromised CA
# RHEL 7/8:
sudo cp compromised.crt /etc/pki/ca-trust/source/blacklist/
# RHEL 9+:
sudo cp compromised.crt /etc/pki/ca-trust/source/blocklist/
sudo update-ca-trust
6.6 Duplicate Certificates: What Happens and How to Handle Them
PEM Format Variants and Trust Implications
Before discussing duplicates, it is essential to understand the three PEM header formats RHEL uses, because the format itself carries trust semantics:
| PEM Header | Trust Data | Where It Appears |
|---|---|---|
-----BEGIN CERTIFICATE----- | None — raw X.509 certificate only | Files you download, CSR responses, manual exports |
-----BEGIN TRUSTED CERTIFICATE----- | Embedded — includes auxiliary trust/reject OID lists | OpenSSL extracted bundle (ca-bundle.trust.crt), some vendor-provided files |
.p11-kit format (not PEM) | Structured — PKCS#11 trust objects with fine-grained attributes | ca-bundle.trust.p11-kit shipped by ca-certificates RPM |
You can inspect the trust attributes embedded in a BEGIN TRUSTED CERTIFICATE file:
# Show the auxiliary trust data
openssl x509 -in cert.crt -noout -text -trustout 2>/dev/null | grep -A 5 "Trusted Uses\|Rejected Uses"
A BEGIN TRUSTED CERTIFICATE file with “Rejected Uses: TLS Web Server Authentication” and a BEGIN CERTIFICATE file for the same cert (which carries no rejection information) are contradictory from p11-kit’s perspective. The same applies to a BEGIN TRUSTED CERTIFICATE with no trust/reject uses at all — p11-kit interprets empty trust as “trusted for nothing,” which contradicts the implicit full trust of a plain BEGIN CERTIFICATE.
Understanding “Duplicate” Certificates
Two certificates can be duplicates at different levels:
| Match Level | What It Means | How p11-kit Treats It |
|---|---|---|
| Identical DER encoding, same format | Byte-for-byte same certificate, same trust wrapping | Deduplicated — appears once in output |
| Identical DER encoding, different trust format | Same certificate but one has BEGIN TRUSTED CERTIFICATE / .p11-kit attributes and the other has BEGIN CERTIFICATE | DISTRUSTED if the trusted format has rejected uses OR empty trust attributes (no uses listed = “trusted for nothing”) |
| Same Subject + Serial + Issuer, different DER | Same logical certificate but re-encoded | Treated as separate objects — both are loaded |
| Same Subject DN, different Serial | Different certificates for the same entity (e.g., reissued CA) | Both valid, both loaded independently |
The critical distinction: p11-kit matches duplicates by DER-encoded certificate content, not by metadata fields. However, “matching” the DER content is only half the story — what happens next depends entirely on whether the trust wrapping formats agree:
- If the raw certificate DER bytes are identical and all sources agree on trust disposition → deduplicated, trusted
- If the raw certificate DER bytes are identical but one source has rejected use OIDs embedded and the other does not → contradictory trust assertions → DISTRUSTED
- If the raw certificate DER bytes are identical but one source has
BEGIN TRUSTED CERTIFICATEwith empty trust attributes (no trusted uses, no rejected uses) → p11-kit interprets this as “trusted for nothing” → contradicts implicit full trust → DISTRUSTED - If the raw certificate DER bytes are identical and one source has
BEGIN TRUSTED CERTIFICATEwith explicit trusted uses only (no rejected uses) → certificate is accepted with the specified trust purposes - If the raw certificate DER bytes differ (even by a single byte) → p11-kit treats them as separate certificates and both are included
Scenario: Conflicting Trust Formats (Most Dangerous)
This is the single most common and most damaging “duplicate” problem. It happens silently when an administrator copies a certificate into source/anchors/ not realizing the same certificate already exists in the system bundle with rejected use OIDs or empty trust attributes in its trust data.
Example:
/usr/share/pki/ca-trust-source/ca-bundle.trust.p11-kit
→ Contains "My Corp CA" as a p11-kit trust object with specific trust attributes
INCLUDING rejected uses (e.g., "Rejected Uses: TLS Web Server Authentication")
OR with empty trust attributes (no trusted uses, no rejected uses listed)
/etc/pki/ca-trust/source/anchors/my-corp-ca.crt
→ Same "My Corp CA" as plain PEM (-----BEGIN CERTIFICATE-----)
(no trust attributes — relies on directory placement for implicit trust)
Result: The same certificate DER content now has contradictory trust descriptions. The p11-kit trust object either explicitly rejects certain uses, or lists no uses at all (which p11-kit interprets as “trusted for nothing”). The plain PEM carries no trust metadata, implying trust for all purposes. p11-kit cannot reconcile these contradictions, and marks the certificate as distrusted. After running update-ca-trust:
- The certificate disappears from
tls-ca-bundle.pem trust listshows it withtrust: distrusted- All services relying on this CA start failing with
certificate verify failed
This same problem occurs with the OpenSSL BEGIN TRUSTED CERTIFICATE format when it contains rejected uses OR empty trust:
/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
→ Contains the certificate as -----BEGIN TRUSTED CERTIFICATE-----
(with "Rejected Uses" OIDs embedded, OR with no trust/reject uses at all)
/etc/pki/ca-trust/source/anchors/certificate.crt
→ Same certificate as -----BEGIN CERTIFICATE-----
(no embedded trust data — no rejection information)
Result: Same outcome — the rejection or empty trust in one source contradicts the implicit full trust in the other, certificate marked as distrusted.
Key insight: In a
BEGIN TRUSTED CERTIFICATE, “no trust/reject uses listed” does NOT mean “trusted for all purposes.” It means “trusted for nothing.” This is the critical distinction from a plainBEGIN CERTIFICATEinanchors/, where directory placement grants implicit full trust. To debug, search for duplicate certificates across/etc/pki/ca-trust/source/and/usr/share/pki/ca-trust-source/and check if anyBEGIN TRUSTED CERTIFICATEexists with no Reject or empty trust that would conflict with a plain PEM copy.
How to fix it:
# Option 1: Remove the plain PEM duplicate from anchors/
sudo rm /etc/pki/ca-trust/source/anchors/my-corp-ca.crt
sudo update-ca-trust
# Option 2: If you NEED the cert in anchors/, convert it to the
# trusted format matching the existing trust attributes:
openssl x509 -in my-corp-ca.crt -addtrust serverAuth \
-addtrust emailProtection -out my-corp-ca-trusted.crt
sudo cp my-corp-ca-trusted.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Verify the fix
trust list --filter=ca-anchors | grep -A 3 "My Corp CA"
Scenario: Explicit Distrusting
/usr/share/pki/ca-trust-source/ca-bundle.trust.p11-kit
→ Contains "Legacy Corp CA" with full trust
/etc/pki/ca-trust/source/blacklist/legacy-corp-ca.crt ← RHEL 7/8
/etc/pki/ca-trust/source/blocklist/legacy-corp-ca.crt ← RHEL 9+
→ Same "Legacy Corp CA" as plain PEM, placed in distrust directory
Result: Distrust wins by design. The certificate is excluded from tls-ca-bundle.pem and marked as distrusted in the OpenSSL bundle. This is the expected behavior when an admin intentionally distrusts a CA.
Scenario: Nearly Identical Certificates (Different DER)
A subtler problem occurs when two certificates look the same but are not byte-for-byte identical:
- A CA certificate was downloaded from two different sources with slightly different PEM line wrapping
- A CA certificate was re-encoded (PEM → DER → PEM) and gained different header metadata
- A CA was reissued with the same Subject DN but a new key pair and serial number
In these cases, p11-kit treats them as distinct certificates, and both end up in the extracted bundles. This can cause:
- Confusing
trust listoutput with apparent duplicates - Increased bundle size (cosmetic issue)
- Chain building confusion if one copy is distrusted and the other is trusted but they have different DER content
How to Detect Duplicate Certificates
Method 1: Fingerprint + format comparison
Extract fingerprints from all source certificates and check for duplicates, including what format each file uses:
# Scan all source directories for duplicate SHA-256 fingerprints
# AND report the PEM format of each file
for dir in /usr/share/pki/ca-trust-source/anchors \
/etc/pki/ca-trust/source/anchors; do
for cert in "$dir"/*.crt "$dir"/*.pem 2>/dev/null; do
[ -f "$cert" ] || continue
fp=$(openssl x509 -in "$cert" -noout -fingerprint -sha256 2>/dev/null)
fmt=$(head -1 "$cert" 2>/dev/null)
[ -n "$fp" ] && echo "$fp [$fmt] $cert"
done
done | sort
# Look for same fingerprint appearing with different formats:
# If you see the same fingerprint with both "BEGIN CERTIFICATE" and
# "BEGIN TRUSTED CERTIFICATE", that is the source of a trust conflict.
If the same fingerprint appears with different PEM headers, you have found a trust format conflict that will cause distrust.
Method 2: Using trust list to spot duplicates
# Extract all labels and look for duplicates
trust list --filter=ca-anchors | grep "^ label:" | sort | uniq -c | sort -rn | head -20
If any label appears more than once, investigate further:
# Show full details for a suspected duplicate
trust list | grep -B 2 -A 10 "label: DigiCert Global Root G2"
Method 3: Compare the extracted bundle against source files
# Count certificates in the PEM bundle
grep -c 'BEGIN CERTIFICATE' /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
# Count unique certificates by fingerprint
awk '/BEGIN CERT/,/END CERT/' /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem | \
csplit -z -f /tmp/cert- - '/BEGIN CERTIFICATE/' '{*}' 2>/dev/null
for f in /tmp/cert-*; do
openssl x509 -in "$f" -noout -fingerprint -sha256 2>/dev/null
done | sort -u | wc -l
rm -f /tmp/cert-*
If the certificate count exceeds the unique fingerprint count, duplicates exist in the extracted bundle.
How to Identify Differences Between Suspected Duplicates
When you have two certificate files that appear to be the same CA, compare them at four levels:
Level 1: Check the PEM format (most common cause of conflicts)
# Check what PEM header each file uses
head -1 cert1.crt
head -1 cert2.crt
# "-----BEGIN CERTIFICATE-----" → plain PEM, no trust data
# "-----BEGIN TRUSTED CERTIFICATE-----" → OpenSSL trusted format, HAS trust data
If one says BEGIN CERTIFICATE and the other says BEGIN TRUSTED CERTIFICATE, you have found the problem. They will cause a trust conflict even if the underlying certificate is identical.
Level 2: Compare DER-encoded certificate content
# Compare DER-encoded content (strips PEM headers, whitespace differences)
openssl x509 -in cert1.crt -outform DER -out /tmp/cert1.der
openssl x509 -in cert2.crt -outform DER -out /tmp/cert2.der
diff /tmp/cert1.der /tmp/cert2.der && echo "IDENTICAL DER" || echo "DIFFERENT DER"
Level 3: Compare certificate fields
# Compare subject, issuer, serial, validity, and key
for field in subject issuer serial dates fingerprint pubkey; do
echo "=== $field ==="
echo "cert1: $(openssl x509 -in cert1.crt -noout -$field 2>/dev/null)"
echo "cert2: $(openssl x509 -in cert2.crt -noout -$field 2>/dev/null)"
done
Level 4: Compare embedded trust attributes (if TRUSTED CERTIFICATE format)
# Show trust attributes for each file
echo "=== cert1 trust attributes ==="
openssl x509 -in cert1.crt -noout -text -trustout 2>/dev/null | grep -A 5 "Trusted Uses\|Rejected Uses"
echo "=== cert2 trust attributes ==="
openssl x509 -in cert2.crt -noout -text -trustout 2>/dev/null | grep -A 5 "Trusted Uses\|Rejected Uses"
Common findings:
| Observation | Likely Explanation | Impact |
|---|---|---|
| Same fingerprint, same PEM format | Harmless duplicate — p11-kit deduplicates | None |
| Same fingerprint, different PEM format (trusted format has rejected uses OR empty trust) | Trust contradiction — rejected uses or “trusted for nothing” vs implicit full trust | Certificate marked DISTRUSTED |
| Same subject + serial, different fingerprint | Re-encoded or tampered certificate | Both loaded as separate objects |
| Same subject, different serial | Reissued CA certificate (new key pair or renewed) | Both loaded independently |
Same subject + serial + fingerprint, different trust list output | Same certificate with different trust attributes applied | Possible distrust |
Cleaning Up Duplicates
If the certificate is already in the system bundle (most common case):
A certificate in anchors/ that already exists in ca-bundle.trust.p11-kit is not always harmless — if the system bundle copy has rejected use OIDs or empty trust attributes, the plain PEM duplicate causes distrust. Always check the trust attributes before assuming a duplicate is benign.
# 1. Check the format of the file in anchors/
head -1 /etc/pki/ca-trust/source/anchors/suspect.crt
# 2. Find the fingerprint
openssl x509 -in /etc/pki/ca-trust/source/anchors/suspect.crt -noout -fingerprint -sha256
# 3. Check if that certificate exists in the system bundle
trust list | grep -B5 -A5 "<subject from the cert>"
# 4. If the certificate already exists in the system bundle with proper
# trust attributes, remove the duplicate from anchors/:
sudo rm /etc/pki/ca-trust/source/anchors/suspect.crt
sudo update-ca-trust
# 5. Verify the certificate is now trusted (not distrusted)
trust list --filter=ca-anchors | grep -A 3 "<subject from the cert>"
If you need to keep the certificate in anchors/ (custom CA not in system bundle):
# Ensure the file format matches what p11-kit expects.
# For custom CAs not in the system bundle, plain PEM is fine:
openssl x509 -in suspect.crt -out /etc/pki/ca-trust/source/anchors/suspect.crt
sudo update-ca-trust
6.7 Using trust list to Identify Distrusted Certificates
The trust list command is the primary tool for inspecting the state of the trust store after update-ca-trust has been run.
Basic Usage
# List all trust objects (trusted + distrusted)
trust list
# List only trusted CA anchors
trust list --filter=ca-anchors
# List only distrusted certificates
trust list --filter=blacklist # RHEL 7/8
trust list --filter=blocklist # RHEL 9+
# List all certificates (without filtering by trust disposition)
trust list --filter=certificates
Anatomy of trust list Output
Each entry in trust list output looks like this:
pkcs11:id=%DE%28%F4%A4%FF%E5%B9%2F%A3%C5%03%D1%A3%49%A7%F9%96%2A%82%12;type=cert
type: certificate
label: DigiCert Global Root G2
trust: anchor
category: authority
pkcs11:id=%01%02%03...;type=cert
type: certificate
label: Legacy Compromised CA
trust: distrusted
category: authority
| Field | Meaning |
|---|---|
pkcs11:id=... | PKCS#11 URI uniquely identifying this object |
type | Always certificate for CA certs |
label | Human-readable name (CN from the certificate subject) |
trust: anchor | Certificate is trusted as a CA |
trust: distrusted | Certificate is explicitly distrusted |
category: authority | Certificate is a CA (has Basic Constraints CA:TRUE) |
category: other-entry | Certificate is an end-entity or unclassified |
Finding Distrusted Certificates
# List all distrusted certificates with their labels
trust list --filter=blacklist # RHEL 7/8
trust list --filter=blocklist # RHEL 9+
# Count distrusted certificates
trust list --filter=blocklist | grep "^pkcs11:" | wc -l # RHEL 9+
# Search for a specific distrusted certificate
trust list --filter=blocklist | grep -B 1 -A 4 "Symantec" # RHEL 9+
Tracing a Distrusted Certificate to Its Source
When trust list --filter=blacklist (RHEL 7/8) or trust list --filter=blocklist (RHEL 9+) shows a certificate you did not expect to be distrusted, you need to find where the distrust originates:
# Step 1: Get the label of the distrusted certificate
trust list --filter=blacklist # RHEL 7/8
trust list --filter=blocklist # RHEL 9+
# Example output:
# pkcs11:id=%AB%CD...;type=cert
# type: certificate
# label: Suspicious CA
# trust: distrusted
# category: authority
# Step 2: Check admin distrust directory
# RHEL 7/8: blacklist/ | RHEL 9+: blocklist/
for distrust_dir in /etc/pki/ca-trust/source/blacklist \
/etc/pki/ca-trust/source/blocklist; do
[ -d "$distrust_dir" ] || continue
echo "=== $distrust_dir ==="
ls -la "$distrust_dir"/
for f in "$distrust_dir"/*; do
[ -f "$f" ] || continue
subj=$(openssl x509 -in "$f" -noout -subject 2>/dev/null)
echo "$f: $subj"
done
done
# Step 3: Check package-provided distrust directory
for distrust_dir in /usr/share/pki/ca-trust-source/blacklist \
/usr/share/pki/ca-trust-source/blocklist; do
[ -d "$distrust_dir" ] || continue
echo "=== $distrust_dir ==="
ls -la "$distrust_dir"/
for f in "$distrust_dir"/*; do
[ -f "$f" ] || continue
subj=$(openssl x509 -in "$f" -noout -subject 2>/dev/null)
echo "$f: $subj"
done
done
# Step 4: Check the main p11-kit bundle for inline distrust attributes
grep -A 5 "Suspicious CA" /usr/share/pki/ca-trust-source/ca-bundle.trust.p11-kit
# Look for "x-distrusted: true" or "nss-mozilla-ca-policy: false"
Interpretation of results:
| Found in | Meaning | Action |
|---|---|---|
/etc/pki/ca-trust/source/blacklist/ (RHEL 7/8) or blocklist/ (RHEL 9+) | Admin explicitly distrusted it | Intentional — verify with team if unexpected |
/usr/share/pki/ca-trust-source/blacklist/ (RHEL 7/8) or blocklist/ (RHEL 9+) | RPM package distrusted it | Mozilla/Red Hat revoked trust — check security advisories |
ca-bundle.trust.p11-kit with distrust attributes | Mozilla removed trust upstream | Normal — CA was distrusted by Mozilla NSS root program |
| Not in any distrust directory | Likely a trust format conflict — same cert exists as both BEGIN CERTIFICATE and BEGIN TRUSTED CERTIFICATE / .p11-kit | Check source/anchors/ for a plain PEM duplicate of a cert already in the system bundle (Section 6.6) |
Practical Example: Debugging a Distrusted CA
A service fails with certificate verify failed and you suspect the CA was distrusted:
# 1. Identify the CA from the failing certificate
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -issuer
# issuer=CN = Internal Corp CA, O = CorpCo
# 2. Check if the issuer is trusted
trust list --filter=ca-anchors | grep -A 3 "Internal Corp CA"
# No output means it's NOT in the trusted anchors
# 3. Check if it's actively distrusted
trust list --filter=blacklist | grep -A 3 "Internal Corp CA" # RHEL 7/8
trust list --filter=blocklist | grep -A 3 "Internal Corp CA" # RHEL 9+
# If found here, the CA is explicitly distrusted
# 4. Check if it's present at all
trust list | grep -A 3 "Internal Corp CA"
# If not found anywhere, it was never added to the trust store
# 5. If distrusted, find the source file (checks both RHEL 7/8 and 9+ paths)
find /etc/pki/ca-trust/source/blacklist/ \
/etc/pki/ca-trust/source/blocklist/ \
/usr/share/pki/ca-trust-source/blacklist/ \
/usr/share/pki/ca-trust-source/blocklist/ \
-type f 2>/dev/null | while read f; do
if openssl x509 -in "$f" -noout -subject 2>/dev/null | grep -qi "Internal Corp CA"; then
echo "FOUND: $f"
fi
done
# 6. If NOT found in any distrust directory, check for trust format conflicts:
# look for a plain PEM in anchors/ that duplicates a cert in the system bundle
for f in /etc/pki/ca-trust/source/anchors/*; do
[ -f "$f" ] || continue
if openssl x509 -in "$f" -noout -subject 2>/dev/null | grep -qi "Internal Corp CA"; then
echo "DUPLICATE IN ANCHORS: $f"
echo " Format: $(head -1 "$f")"
echo " Check if the same cert exists in ca-bundle.trust.p11-kit with different trust format"
fi
done
# 7. Also check the main p11-kit bundle
grep -B 2 -A 10 "Internal Corp CA" \
/usr/share/pki/ca-trust-source/ca-bundle.trust.p11-kit
Restoring Trust for a Distrusted Certificate
If you determine a certificate was incorrectly distrusted:
# If the distrust entry is in /etc/pki/ (admin-managed)
# RHEL 7/8:
sudo rm /etc/pki/ca-trust/source/blacklist/the-cert.crt
# RHEL 9+:
sudo rm /etc/pki/ca-trust/source/blocklist/the-cert.crt
sudo update-ca-trust
# If the distrust comes from the ca-certificates package, you need to
# override it by adding the certificate as a trusted anchor:
sudo cp the-cert.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Note: this works because admin anchors override package-level distrust
# for certificates matched by DER content.
# Verify the certificate is now trusted
trust list --filter=ca-anchors | grep -A 3 "The Cert Label"
Warning: Overriding a Mozilla/Red Hat distrust decision should only be done if you have a specific business reason and understand the security implications. CAs are distrusted for cause (compromise, mis-issuance, policy violations).
6.8 Advanced Debugging with trust and p11-kit
Inspecting Individual Trust Objects
# Dump the full PKCS#11 attributes for a specific certificate
trust dump --filter="pkcs11:id=%DE%28%F4%A4..."
# List all attributes of all trust objects (verbose, large output)
trust dump
Verifying the Extraction Pipeline
If you suspect update-ca-trust is not producing the expected output:
# Run the extraction manually with verbose output
p11-kit extract --format=pem-bundle \
--filter=ca-anchors \
--purpose=server-auth \
/tmp/test-tls-bundle.pem
# Compare with the system bundle
diff <(sort /tmp/test-tls-bundle.pem) \
<(sort /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem)
# Run with p11-kit debug logging
P11_KIT_DEBUG=all update-ca-trust 2>&1 | head -100
Checking Certificate Chain Completeness
# Verify a server certificate against the system trust store
openssl verify -CAfile /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem server.crt
# If the chain has intermediates, include them:
openssl verify -CAfile /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
-untrusted intermediate.crt server.crt
# Show the full chain that OpenSSL would build:
openssl verify -show_chain -CAfile /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem server.crt
Cross-checking the OpenSSL Bundle
The OpenSSL “trusted certificate” format (ca-bundle.trust.crt) includes embedded trust/reject attributes that are distinct from the plain PEM bundle. You can inspect them:
# Show trust attributes for certificates in the OpenSSL bundle
openssl x509 -in /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt \
-noout -text -trustout 2>/dev/null | grep -A 2 "Trusted Uses\|Rejected Uses"
Checking the Java Keystore
# List all entries in the Java trust store
keytool -list -cacerts -storepass changeit 2>/dev/null | grep "trustedCertEntry" | wc -l
# Search for a specific CA
keytool -list -cacerts -storepass changeit 2>/dev/null | grep -i "company"
6.9 Troubleshooting Trust Issues
Systematic Approach
Symptom: Certificate verification failed
Step 1: Identify what certificate is failing and who issued it
# Get the issuer chain from a remote server
openssl s_client -connect server.example.com:443 -showcerts </dev/null 2>/dev/null | \
openssl x509 -noout -issuer -subject
# Or from a local certificate file
openssl x509 -in server.crt -noout -issuer -subject -serial
Step 2: Check if the issuer CA is in the trust store
trust list --filter=ca-anchors | grep -i "ISSUER_CN_HERE"
Step 3: Check if the issuer CA is distrusted
trust list --filter=blacklist | grep -i "ISSUER_CN_HERE" # RHEL 7/8
trust list --filter=blocklist | grep -i "ISSUER_CN_HERE" # RHEL 9+
Step 4: If missing, add it
sudo cp issuer-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
Step 5: If distrusted, find the source and decide on action
# Trace the distrust source (see Section 6.7)
# Checks both RHEL 7/8 (blacklist) and RHEL 9+ (blocklist) paths
find /etc/pki/ca-trust/source/blacklist/ \
/etc/pki/ca-trust/source/blocklist/ \
/usr/share/pki/ca-trust-source/blacklist/ \
/usr/share/pki/ca-trust-source/blocklist/ \
-name '*.crt' -o -name '*.pem' 2>/dev/null | while read f; do
openssl x509 -in "$f" -noout -subject 2>/dev/null
done
Common Trust Store Problems
| Problem | Cause | Solution |
|---|---|---|
| CA not found after adding to anchors/ | Forgot to run update-ca-trust | Run sudo update-ca-trust |
| CA distrusted after adding to anchors/ | Plain PEM conflicts with existing TRUSTED CERTIFICATE or .p11-kit format that has rejected uses or empty trust | Remove the duplicate from anchors/ — the system bundle already has it (Section 6.6) |
| CA still distrusted after adding to anchors/ | DER content differs from distrusted copy | Compare fingerprints; ensure identical certificate |
| Java app doesn’t trust CA | Java keystore not regenerated | Run sudo update-ca-trust (rebuilds cacerts) |
| Trust restored after reboot | Admin added cert to /usr/share/ (overwritten by RPM updates) | Always use /etc/pki/ca-trust/source/anchors/ |
trust list shows duplicate entries | Same subject CA from multiple sources with different DER content | Identify and remove the redundant source file |
update-ca-trust silently fails | Corrupted certificate file in sources | Check for syntax errors: openssl x509 -in suspect.crt -noout |
Quick Reference
┌───────────────────────────────────────────────────────────────────────────────┐
│ RHEL TRUST STORE QUICK REFERENCE │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Add CA: sudo cp ca.crt /etc/pki/ca-trust/source/anchors/ │
│ sudo update-ca-trust │
│ │
│ Distrust: sudo cp bad.crt /etc/pki/ca-trust/source/blacklist/ │
│ (RHEL 7/8) or .../source/blocklist/ (RHEL 9+) │
│ sudo update-ca-trust │
│ │
│ Verify: trust list --filter=ca-anchors | grep "CA Name" │
│ openssl verify -CAfile /etc/pki/ca-trust/extracted/ │
│ pem/tls-ca-bundle.pem cert.crt │
│ │
│ Distrusted: trust list --filter=blacklist (RHEL 7/8) │
│ trust list --filter=blocklist (RHEL 9+) │
│ │
│ Duplicates: trust list --filter=ca-anchors | grep "label:" | │
│ sort | uniq -c | sort -rn │
│ │
│ Compare: openssl x509 -in a.crt -outform DER | sha256sum │
│ openssl x509 -in b.crt -outform DER | sha256sum │
│ │
│ Debug: P11_KIT_DEBUG=all update-ca-trust 2>&1 │
│ │
│ Source dirs: /etc/pki/ca-trust/source/{anchors,blacklist|blocklist}/ │
│ /usr/share/pki/ca-trust-source/{anchors,blacklist|blocklist}/ │
│ │
│ Extracted: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem │
│ /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt │
│ /etc/pki/ca-trust/extracted/java/cacerts │
│ │
│ Pipeline: update-ca-trust = update-ca-trust extract (identical) │
│ │
│ Priority: blacklist/blocklist > anchors │
│ /etc/pki/ > /usr/share/pki/ │
│ .p11-kit attributes > plain PEM defaults │
│ │
│ DANGER: Never add a plain "BEGIN CERTIFICATE" PEM to anchors/ │
│ if the same cert already exists in the system bundle │
│ as "BEGIN TRUSTED CERTIFICATE" or .p11-kit format. │
│ Empty trust (no uses) = "trusted for nothing" = DISTRUSTED. │
│ Rejected uses + plain PEM → also DISTRUSTED. │
└───────────────────────────────────────────────────────────────────────────────┘
🧪 Hands-On Lab
Lab 05: Trust Store Management
Add custom CAs to the system trust store, manage trust attributes, detect duplicates, and debug distrusted certificates.
- 📁 Location:
labs/en_US/05-trust-store/ - ⏱️ Time: 45 minutes
- 🎯 Level: Intermediate
Chapter Navigation
| ← Previous: Chapter 5 - X.509 Certificates on RHEL | Next: Chapter 7 - Digital Signatures & Verification on RHEL → |
|---|
Chapter 7: Digital Signatures & Verification on RHEL
How Trust Works: Learn how digital signatures enable certificate validation on RHEL systems.
7.1 Cryptographic Hash Functions
Properties:
- Deterministic
- Pre-image resistance
- Collision resistance
- Avalanche effect
Popular algorithms: SHA-256, SHA-3, BLAKE2.
7.2 Building Signatures
- Calculate hash of message.
- Encrypt hash with private key → signature.
- Receiver decrypts signature with public key and compares with their own hash.
7.3 Certificate Fingerprints
A fingerprint is simply the hash of the DER-encoded certificate, used to uniquely identify it, e.g.,
openssl x509 -in server.crt -noout -fingerprint -sha256
7.4 Lab: Sign & Verify a File
# sign
openssl dgst -sha256 -sign rsa.key.pem -out report.sig report.pdf
# verify
openssl dgst -sha256 -verify rsa.pub.pem -signature report.sig report.pdf
7.5 Summary
Signatures tie data to identities; hashes ensure integrity. Together they underpin certificate validation and all PKI operations.
7.6 Signature Algorithms on RHEL
Approved by RHEL Version
| Algorithm | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|---|
| SHA-256 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| SHA-384 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| SHA-512 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| SHA-1 | ✅ Yes | ⚠️ Deprecated | ❌ Blocked | ❌ Blocked |
| MD5 | ✅ Yes | ⚠️ Legacy only | ❌ Blocked | ❌ Blocked |
Critical: RHEL 9+ blocks SHA-1 and MD5 for security!
Verifying Certificates on RHEL
# Verify certificate chain
openssl verify /etc/pki/tls/certs/server.crt
# Verify against specific CA
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt server.crt
# Check signature algorithm
openssl x509 -in server.crt -noout -text | grep "Signature Algorithm"
# Must be SHA-256+ on RHEL 8+
Quick Reference
┌──────────────────────────────────────────────────────────┐
│ DIGITAL SIGNATURES ON RHEL │
├──────────────────────────────────────────────────────────┤
│ Purpose: Prove authenticity and integrity │
│ How: Hash + Private key = Signature │
│ Verify: Signature + Public key = Original hash │
│ │
│ Approved: SHA-256, SHA-384, SHA-512 │
│ Deprecated: SHA-1 (blocked on RHEL 9+) │
│ Blocked: MD5 (blocked on RHEL 9+) │
│ │
│ Verify cert: openssl verify cert.crt │
│ Check alg: openssl x509 -noout -text | grep Signature │
│ Fingerprint: openssl x509 -noout -fingerprint -sha256 │
└──────────────────────────────────────────────────────────┘
🧪 Hands-On Lab
Lab 03: Digital Signatures
Sign files, verify signatures, and detect tampering
- 📁 Location:
labs/en_US/03-digital-signatures/ - ⏱️ Time: 20 minutes
- 🎯 Level: Beginner
Chapter Navigation
| ← Previous: Chapter 6 - RHEL Trust Store Deep Dive | Next: Chapter 8 - RHEL Versions & Certificate Evolution → |
|---|
Chapter 8: RHEL Versions & Certificate Evolution
Learning Objective: Understand how certificate management differs across RHEL 7, 8, 9, and 10 so you can quickly identify version-specific behaviors when troubleshooting.
8.1 Why RHEL Version Matters
When troubleshooting certificate issues on RHEL, the first question should always be: “What RHEL version am I on?”
Certificate management has evolved significantly across RHEL versions. What works on RHEL 7 may fail on RHEL 9. Understanding these differences is crucial for:
- ✅ Choosing the right troubleshooting approach
- ✅ Identifying version-specific errors
- ✅ Planning migrations
- ✅ Writing compatible automation scripts
8.2 Quick Version Check
# Method 1: Check /etc/redhat-release
cat /etc/redhat-release
# Output examples:
# Red Hat Enterprise Linux Server release 7.9 (Maipo)
# Red Hat Enterprise Linux release 8.10 (Ootpa)
# Red Hat Enterprise Linux release 9.8 (Plow)
# Red Hat Enterprise Linux release 10.2 (Coughlan)
# Method 2: Use rpm
rpm -q --queryformat '%{VERSION}\n' redhat-release
# Method 3: Check OpenSSL version (indirect but useful)
openssl version
# RHEL 7: OpenSSL 1.0.2k family
# RHEL 8: OpenSSL 1.1.1 family
# RHEL 9: OpenSSL 3.x
# RHEL 10: OpenSSL 3.x
8.3 RHEL Version Overview
| RHEL Version | GA Date | Lifecycle Note | OpenSSL Version | Key Certificate Feature |
|---|---|---|---|---|
| RHEL 7 | June 2014 | June 2024 base life cycle; extended options vary | 1.0.2k family | Traditional manual management |
| RHEL 8 | May 2019 | May 2029 | 1.1.1 family | Crypto-policies introduced |
| RHEL 9 | May 2022 | May 2032 | 3.x | OpenSSL 3.x, stricter defaults |
| RHEL 10 | May 2025 | May 2035 | 3.x | Continued hardening, PQC prep |
Source: Red Hat Product Life Cycle
Use the product life cycle page for exact EUS/ELS/add-on details; the table above is a major-version planning view, not a contract summary.
8.4 Major Differences Summary
RHEL 7 (Legacy)
Characteristics:
- ✅ Stable, well-understood
- ✅ Maximum compatibility
- ⚠️ TLS 1.0/1.1 enabled by default
- ⚠️ Weak ciphers allowed
- ⚠️ Manual certificate management
Package Family: openssl-1.0.2k*
When You’ll See It:
- Legacy systems not yet migrated
- Applications requiring old TLS versions
- Conservative environments
Key Command:
# Generate key (RHEL 7 style)
openssl genrsa -out server.key 2048
RHEL 8 (Widely Deployed)
Characteristics:
- ✅ System-wide crypto-policies (game changer!)
- ✅ TLS 1.2+ by default
- ✅ certmonger for auto-renewal
- ✅ Modern cipher suites
- ⚠️ Breaking changes from RHEL 7
Package Family: openssl-1.1.1*
Key Innovation - Crypto-Policies:
# View current policy
update-crypto-policies --show
# DEFAULT, LEGACY, FUTURE, or FIPS
# System-wide security control!
sudo update-crypto-policies --set FUTURE
When You’ll See It:
- Most enterprise deployments
- Modern applications
- FreeIPA environments
Key Command:
# Generate key (RHEL 8 modern style)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server.key
RHEL 9 (Modern Standard)
Characteristics:
- ✅ OpenSSL 3.5.5 with provider architecture
- ✅ TLS 1.2+ mandatory
- ✅ Enhanced crypto-policies
- ✅ Stricter certificate validation
- ⚠️ OpenSSL 3.x API changes
- ⚠️ Legacy algorithms disabled
Package Family: openssl-3*
Major Change - Provider Architecture:
# List crypto providers
openssl list -providers
# default, fips, legacy, base
# Legacy algorithms require explicit provider
openssl md5 -provider legacy file.txt
When You’ll See It:
- New deployments
- Security-conscious environments
- Latest application versions
Key Command:
# Generate EC key (RHEL 9)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.key
RHEL 10 (Current Release)
Characteristics:
- ✅ Same OpenSSL 3.5.5 as RHEL 9.8
- ✅ Continued security hardening
- ✅ Post-quantum cryptography preparation
- ✅ Enhanced container certificate support
- ⚠️ Even stricter defaults
- ⚠️ Additional legacy removals
Package Family: openssl-3*
Note: RHEL 10.0 GA was May 20, 2025. Features and capabilities may evolve across minor versions (10.1, 10.2, etc.). Always consult official documentation for your specific RHEL 10.x release.
Key Focus:
- Quantum-resistant cryptography foundation
- Modern security practices
- Container and cloud-native workloads
When You’ll See It:
- Brand new deployments
- Cutting-edge security requirements
- Future-proofing initiatives
8.5 Critical Version Differences
TLS Version Support
| TLS Version | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|---|
| TLS 1.0 | ✅ Yes | ⚠️ LEGACY only | ❌ No | ❌ No |
| TLS 1.1 | ✅ Yes | ⚠️ LEGACY only | ❌ No | ❌ No |
| TLS 1.2 | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| TLS 1.3 | ❌ No | ✅ Yes | ✅ Yes (preferred) | ✅ Yes (preferred) |
Certificate Tools Availability
| Tool | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|---|
openssl | 1.0.2k | 1.1.1k | 3.5.5 | 3.5.5 |
certutil (NSS) | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
update-ca-trust | ✅ Yes | ✅ Enhanced | ✅ Enhanced | ✅ Enhanced |
certmonger | ✅ Yes | ✅ Enhanced | ✅ Enhanced | ✅ Enhanced |
crypto-policies | ❌ No | ✅ Yes | ✅ Enhanced | ✅ Enhanced |
authconfig | ✅ Yes | ❌ No (use authselect) | ❌ No | ❌ No |
Key Cipher/Algorithm Changes
| Algorithm/Feature | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|---|
| 3DES | ✅ Yes | ⚠️ LEGACY | ❌ No | ❌ No |
| RC4 | ✅ Yes | ❌ No | ❌ No | ❌ No |
| MD5 signatures | ✅ Yes | ⚠️ LEGACY | ❌ No | ❌ No |
| SHA-1 signatures | ✅ Yes | ⚠️ Deprecated | ❌ No | ❌ No |
| RSA < 2048 bits | ✅ Yes | ❌ No | ❌ No | ❌ No |
| DSA keys | ✅ Yes | ⚠️ LEGACY | ❌ No | ❌ No |
8.6 Common Version-Specific Issues
RHEL 7 Issues
# Problem: Old cipher suites accepted
# Impact: Security vulnerabilities
# Solution: Manual Apache/NGINX cipher configuration
RHEL 8 Issues
# Problem: Application fails after migration from RHEL 7
# Reason: TLS 1.0/1.1 disabled by default
# Quick Fix: Temporarily use LEGACY policy (not recommended long-term)
sudo update-crypto-policies --set LEGACY
# Better Fix: Update application to support TLS 1.2+
RHEL 9 Issues
# Problem: OpenSSL commands fail with provider errors
# Reason: OpenSSL 3.x provider architecture
# Fix: Specify provider explicitly
openssl md5 -provider legacy file.txt
# Problem: SHA-1 certificates rejected
# Reason: Stricter validation
# Fix: Reissue certificates with SHA-256+
RHEL 10 Issues
# Problem: Even stricter defaults than RHEL 9
# Impact: Legacy certificates may fail validation
# Solution: Ensure all certificates use modern algorithms
# Check RHEL 10.x specific documentation for your minor version
8.7 Migration Impact
RHEL 7 → RHEL 8
Certificate Impact: MODERATE
- TLS 1.0/1.1 disabled
- Weak ciphers removed
- certmonger integration required for automation
Action Required:
- Audit TLS versions in use
- Update cipher configurations
- Test applications with TLS 1.2+
- Consider crypto-policies
RHEL 8 → RHEL 9
Certificate Impact: HIGH
- OpenSSL 3.x API changes
- Legacy algorithm removal
- Stricter certificate validation
- Provider architecture changes
Action Required:
- Test all certificate operations
- Update custom scripts using OpenSSL
- Validate certificate chain integrity
- Check for SHA-1 usage
RHEL 9 → RHEL 10
Certificate Impact: LOW-MODERATE
- Same OpenSSL base (3.5.5)
- Incremental hardening
- Policy refinements
Action Required:
- Review RHEL 10.x documentation
- Test crypto-policy compatibility
- Validate modern algorithm usage
8.8 Choosing the Right Approach
For Troubleshooting
# Always start with version check
cat /etc/redhat-release
# Then check OpenSSL version
openssl version
# For RHEL 8+: Check crypto-policy
update-crypto-policies --show 2>/dev/null || echo "Pre-RHEL 8"
Quick Reference Decision Tree
Is it RHEL 7?
├─ YES → Check for legacy TLS/cipher issues
│ Manual configuration likely needed
│ Consider migration planning
│
└─ NO → Is it RHEL 8?
├─ YES → Check crypto-policies first!
│ Use certmonger for automation
│ Consider RHEL 9 upgrade
│
└─ NO → Is it RHEL 9 or 10?
└─ YES → Check OpenSSL 3.x provider issues
Verify modern algorithms in use
Leverage enhanced tooling
8.9 Key Takeaways
- Always check RHEL version first when troubleshooting
- RHEL 8 introduced crypto-policies - game changer for certificate management
- RHEL 9 uses OpenSSL 3.x - significant API and behavior changes
- RHEL 10 continues RHEL 9 foundation - incremental improvements
- Legacy algorithms progressively removed across versions
- Migration testing is critical - certificate behavior changes significantly
8.10 What’s Next?
Now that you understand RHEL version differences, we’ll dive deeper into:
- Chapter 9: RHEL 7 Certificate Management (detailed)
- Chapter 10: RHEL 8 & Crypto-Policies (detailed)
- Chapter 11: RHEL 9 Modern Security (detailed)
- Chapter 12: RHEL 10 Current Features (detailed)
Quick Reference Card
┌────────────────────────────────────────────────────────────┐
│ RHEL VERSION QUICK REFERENCE │
├─────────────┬───────────┬──────────────┬───────────────────┤
│ RHEL 7 │ 1.0.2k │ Manual │ Legacy-friendly │
│ RHEL 8 │ 1.1.1k │ Crypto-pols │ Widely deployed │
│ RHEL 9 │ 3.5.5 │ OpenSSL 3.x │ Modern secure │
│ RHEL 10 │ 3.5.5 │ Hardened │ Future-ready │
└─────────────┴───────────┴──────────────┴───────────────────┘
Check version: cat /etc/redhat-release
Check OpenSSL: openssl version
Check policy: update-crypto-policies --show (RHEL 8+)
Chapter Navigation
| ← Previous: Chapter 7 - Digital Signatures & Verification on RHEL | Next: Chapter 9 - RHEL 7 Certificate Management → |
|---|
Chapter 9: RHEL 7 Certificate Management
Legacy but Important: RHEL 7 reached end of maintenance in June 2024, but many enterprises still run it. Learn how certificate management works on RHEL 7.
9.1 RHEL 7 Overview
Release: June 10, 2014 Maintenance Support Ended: June 30, 2024 Extended Life Cycle Support: Available through 2028
Key Characteristics:
- OpenSSL Version: 1.0.2k-26 (package:
openssl-1.0.2k-26.el7_9.x86_64) - Default TLS: TLS 1.0, 1.1, 1.2 all enabled
- Trust Store:
/etc/pki/ca-trust/extracted/ - Management Approach: Primarily manual
- Crypto-Policies: Not available (RHEL 8+ feature)
Note: If you’re still on RHEL 7, plan migration to RHEL 8 or 9. Security updates are limited.
9.2 OpenSSL 1.0.2k Specifics
Version Check
# Check OpenSSL version on RHEL 7
openssl version
# OpenSSL 1.0.2k-fips 12 Jan 2017
# Check package
rpm -q openssl
# openssl-1.0.2k-26.el7_9.x86_64
Key Features and Limitations
Features:
- ✅ TLS 1.0, 1.1, 1.2 support
- ✅ Stable and well-tested
- ✅ Wide compatibility
- ✅ RSA, ECC, DSA key types
Limitations:
- ❌ No TLS 1.3 support
- ❌ Older command syntax (genrsa vs genpkey)
- ❌ Weaker default ciphers
- ❌ Limited modern cipher suites
Command Syntax (RHEL 7 Style)
#============================================#
# GENERATE RSA KEY (RHEL 7)
#============================================#
# Old style (common on RHEL 7)
openssl genrsa -out server.key 2048
# With passphrase protection
openssl genrsa -aes256 -out server.key 2048
# Remove passphrase from key
openssl rsa -in server.key -out server-nopass.key
#============================================#
# GENERATE CSR (RHEL 7)
#============================================#
# Basic CSR
openssl req -new -key server.key -out server.csr
# With subject specified
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=State/L=City/O=Company/CN=server.example.com"
# ⚠️ Note: SANs are harder to add with RHEL 7 OpenSSL
# Need config file for SANs
#============================================#
# VIEW CERTIFICATE
#============================================#
# Full details
openssl x509 -in server.crt -noout -text
# Just expiration
openssl x509 -in server.crt -noout -dates
# Just subject
openssl x509 -in server.crt -noout -subject
9.3 Trust Store Management on RHEL 7
Adding Custom CAs
#============================================#
# ADD CUSTOM CA (RHEL 7)
#============================================#
# Step 1: Copy CA certificate to anchors directory
sudo cp corporate-ca.crt /etc/pki/ca-trust/source/anchors/
# Step 2: Update trust store
sudo update-ca-trust extract
# Step 3: Verify
trust list | grep -i "corporate"
# Verify applications use it
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt test-cert.crt
Trust Store Locations (RHEL 7)
/etc/pki/ca-trust/
├── source/
│ └── anchors/ ← Add custom CAs here
│
└── extracted/
├── pem/
│ └── tls-ca-bundle.pem ← OpenSSL, Python, Ruby
├── openssl/
│ └── ca-bundle.trust.crt ← OpenSSL specific
└── java/
└── cacerts ← Java applications
9.4 Service Configuration (RHEL 7 Approach)
Apache HTTPS on RHEL 7
#============================================#
# APACHE SSL/TLS SETUP (RHEL 7)
#============================================#
# Install Apache with SSL
sudo yum install httpd mod_ssl -y
# Generate certificate and key
sudo openssl genrsa -out /etc/pki/tls/private/server.key 2048
sudo openssl req -new -key /etc/pki/tls/private/server.key \
-out /tmp/server.csr \
-subj "/CN=$(hostname -f)"
# Get certificate from CA (or self-signed for testing)
sudo openssl x509 -req -days 365 -in /tmp/server.csr \
-signkey /etc/pki/tls/private/server.key \
-out /etc/pki/tls/certs/server.crt
# Configure Apache (/etc/httpd/conf.d/ssl.conf)
sudo vi /etc/httpd/conf.d/ssl.conf
# Set:
# SSLCertificateFile /etc/pki/tls/certs/server.crt
# SSLCertificateKeyFile /etc/pki/tls/private/server.key
#
# # Recommended: Disable weak TLS versions
# SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
#
# # Recommended: Strong ciphers only
# SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!RC4
# Start Apache
sudo systemctl enable httpd
sudo systemctl start httpd
# Test
curl -vk https://localhost/
NGINX on RHEL 7
#============================================#
# NGINX SSL/TLS SETUP (RHEL 7)
#============================================#
# Install NGINX (from EPEL)
sudo yum install epel-release -y
sudo yum install nginx -y
# Generate certificate
sudo openssl genrsa -out /etc/pki/tls/private/nginx.key 2048
sudo openssl req -new -x509 -days 365 \
-key /etc/pki/tls/private/nginx.key \
-out /etc/pki/tls/certs/nginx.crt \
-subj "/CN=$(hostname -f)"
# Configure NGINX (/etc/nginx/nginx.conf)
# Add to server block:
# listen 443 ssl;
# ssl_certificate /etc/pki/tls/certs/nginx.crt;
# ssl_certificate_key /etc/pki/tls/private/nginx.key;
#
# # Recommended
# ssl_protocols TLSv1.2;
# ssl_ciphers HIGH:!aNULL:!MD5;
# Start NGINX
sudo systemctl enable nginx
sudo systemctl start nginx
9.5 Manual Certificate Renewal (RHEL 7)
No crypto-policies, no automatic tools - everything is manual!
Renewal Process
#============================================#
# MANUAL RENEWAL PROCESS (RHEL 7)
#============================================#
# Step 1: Check expiration (set calendar reminder)
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -dates
# Step 2: Generate new CSR (reuse existing key)
openssl req -new -key /etc/pki/tls/private/server.key \
-out /tmp/server-renewal.csr \
-subj "/CN=server.example.com"
# Step 3: Submit CSR to CA
# Step 4: Receive new certificate from CA
# Step 5: Backup old certificate
sudo cp /etc/pki/tls/certs/server.crt \
/etc/pki/tls/certs/server.crt.$(date +%Y%m%d).old
# Step 6: Install new certificate
sudo cp new-server.crt /etc/pki/tls/certs/server.crt
sudo chmod 644 /etc/pki/tls/certs/server.crt
# Step 7: Reload service
sudo systemctl reload httpd
# Step 8: Test
curl -v https://localhost/
openssl s_client -connect localhost:443
Tracking Certificate Renewals
#============================================#
# CREATE RENEWAL TRACKING (RHEL 7)
#============================================#
# Cron job to check expiration
cat > /etc/cron.weekly/check-cert-expiration << 'EOF'
#!/bin/bash
# Check certificates expiring in 60 days
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
if ! openssl x509 -in "$cert" -noout -checkend $((86400*60)); then
echo "⚠️ $cert expires within 60 days!"
echo "$cert" | mail -s "Certificate Expiring Soon" admin@example.com
fi
done
EOF
chmod +x /etc/cron.weekly/check-cert-expiration
9.6 Common RHEL 7 Certificate Issues
Issue 1: TLS 1.0/1.1 Deprecated
Problem: Modern clients reject TLS 1.0/1.1
Symptoms:
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Fix:
# Update Apache to disable old TLS versions
# /etc/httpd/conf.d/ssl.conf
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Restart Apache
sudo systemctl restart httpd
Issue 2: Weak Ciphers
Problem: PCI/Security scans flag weak ciphers
Fix:
# Apache: Use strong ciphers only
# /etc/httpd/conf.d/ssl.conf
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!RC4:!EXPORT
SSLHonorCipherOrder on
# Test
openssl s_client -connect localhost:443 -cipher '3DES'
# Should fail if 3DES is disabled
Issue 3: Missing SANs
Problem: Modern browsers require Subject Alternative Names
RHEL 7 Challenge: SANs are harder to add with OpenSSL 1.0.2
Solution: Use config file
# Create OpenSSL config
cat > /tmp/san.cnf << EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
CN = server.example.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = server.example.com
DNS.2 = www.example.com
IP.1 = 10.0.0.100
EOF
# Generate CSR with SANs
openssl req -new -key server.key -out server.csr -config /tmp/san.cnf
# Verify SANs in CSR
openssl req -in server.csr -noout -text | grep -A3 "Subject Alternative Name"
9.7 certmonger on RHEL 7
Available: Yes (basic version)
#============================================#
# CERTMONGER ON RHEL 7
#============================================#
# Install
sudo yum install certmonger -y
sudo systemctl enable certmonger
sudo systemctl start certmonger
# Request certificate from FreeIPA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-K host/$(hostname -f)@REALM
# List tracked certificates
sudo getcert list
# Check specific certificate status
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Monitor certmonger logs
sudo tail -f /var/log/messages | grep certmonger
RHEL 7 Limitations:
- No ACME support (Let’s Encrypt requires manual certbot)
- Less detailed status output
- Fewer post-save command options
9.8 Migration Considerations
When to Migrate from RHEL 7
You should migrate if:
- ✅ Support ended (June 2024) and you need updates
- ✅ Need TLS 1.3 support
- ✅ Want crypto-policies for easier management
- ✅ Require modern security features
- ✅ Compliance requires supported OS
Pre-Migration Certificate Tasks
#============================================#
# RHEL 7 CERTIFICATE PRE-MIGRATION AUDIT
#============================================#
# 1. List all certificates
find /etc/pki/tls/ -name "*.crt" -o -name "*.key"
# 2. Check expirations
for cert in /etc/pki/tls/certs/*.crt; do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -subject -dates
echo ""
done
# 3. Check signature algorithms (SHA-1 won't work on RHEL 8+)
for cert in /etc/pki/tls/certs/*.crt; do
SIG=$(openssl x509 -in "$cert" -noout -text | grep "Signature Algorithm" | head -2)
echo "$cert: $SIG"
done | grep -i sha1
# If any found, reissue before migration!
# 4. Document custom CAs
ls -l /etc/pki/ca-trust/source/anchors/
# 5. Export certificates and keys
tar czf rhel7-certificates-backup-$(date +%Y%m%d).tar.gz \
/etc/pki/tls/certs/*.crt \
/etc/pki/tls/private/*.key \
/etc/pki/ca-trust/source/anchors/*
9.9 Common RHEL 7 Workflows
Workflow 1: Manual Apache HTTPS Setup
# Complete workflow from scratch
# 1. Install Apache with SSL
sudo yum install httpd mod_ssl -y
# 2. Generate private key
sudo openssl genrsa -out /etc/pki/tls/private/$(hostname -s).key 2048
# 3. Set key permissions
sudo chmod 600 /etc/pki/tls/private/$(hostname -s).key
# 4. Create CSR
sudo openssl req -new \
-key /etc/pki/tls/private/$(hostname -s).key \
-out /tmp/$(hostname -s).csr \
-subj "/C=US/O=Company/CN=$(hostname -f)"
# 5. Submit CSR to CA, wait for certificate
# 6. Install certificate
sudo cp $(hostname -s).crt /etc/pki/tls/certs/
# 7. Configure Apache
sudo vi /etc/httpd/conf.d/ssl.conf
# Edit:
# SSLCertificateFile /etc/pki/tls/certs/$(hostname -s).crt
# SSLCertificateKeyFile /etc/pki/tls/private/$(hostname -s).key
# SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
# 8. Test configuration
sudo apachectl configtest
# 9. Open firewall
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
# 10. Start Apache
sudo systemctl enable httpd
sudo systemctl start httpd
# 11. Test
curl -vk https://$(hostname -f)/
Workflow 2: FreeIPA Integration
#============================================#
# FREEIPA CERTIFICATE WORKFLOW (RHEL 7)
#============================================#
# Prerequisites: System must be IPA-enrolled
ipa-client-install
# Install certmonger
sudo yum install certmonger -y
sudo systemctl enable certmonger
sudo systemctl start certmonger
# Request certificate for Apache
sudo ipa-getcert request \
-f /etc/pki/tls/certs/$(hostname -s).crt \
-k /etc/pki/tls/private/$(hostname -s).key \
-K host/$(hostname -f)@REALM.EXAMPLE.COM \
-D $(hostname -f)
# Check status
sudo getcert list
# Wait for MONITORING status (certificate issued)
# Configure Apache to use cert
# /etc/httpd/conf.d/ssl.conf
# Reload Apache when cert renews
sudo ipa-getcert request \
-f /etc/pki/tls/certs/$(hostname -s).crt \
-k /etc/pki/tls/private/$(hostname -s).key \
-K host/$(hostname -f)@REALM \
-C "systemctl reload httpd"
9.10 Troubleshooting RHEL 7 Certificates
Diagnostic Commands
#============================================#
# RHEL 7 CERTIFICATE DIAGNOSTICS
#============================================#
# Check OpenSSL version
openssl version
# Test HTTPS locally
openssl s_client -connect localhost:443
# Check Apache SSL configuration
sudo apachectl -t -D DUMP_VHOSTS | grep 443
# View Apache SSL errors
sudo tail -f /var/log/httpd/ssl_error_log
# Check SELinux denials
sudo grep AVC /var/log/audit/audit.log | grep cert
# Check file permissions
ls -lZ /etc/pki/tls/certs/*.crt
ls -lZ /etc/pki/tls/private/*.key
# Verify certificate/key pair
openssl x509 -noout -modulus -in /etc/pki/tls/certs/server.crt | openssl md5
openssl rsa -noout -modulus -in /etc/pki/tls/private/server.key | openssl md5
# MD5 hashes should match
Common RHEL 7 Errors
| Error | Cause | Solution |
|---|---|---|
| “certificate verify failed” | Missing CA in trust store | Add CA to /etc/pki/ca-trust/source/anchors/ |
| “permission denied” on key | Wrong permissions | chmod 600 on .key file |
| “certificate has expired” | Cert expired | Renew certificate manually |
| “no shared cipher” | Client/server cipher mismatch | Update SSLCipherSuite |
| “wrong version number” | TLS version mismatch | Update SSLProtocol |
9.11 Security Hardening on RHEL 7
Recommended Configuration
#============================================#
# APACHE SSL/TLS HARDENING (RHEL 7)
#============================================#
# /etc/httpd/conf.d/ssl.conf
# Disable old protocols
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# Strong ciphers only
SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!3DES:!DES
# Honor server cipher preference
SSLHonorCipherOrder on
# Enable HSTS (HTTP Strict Transport Security)
Header always set Strict-Transport-Security "max-age=31536000"
# OCSP Stapling (not available in RHEL 7 OpenSSL 1.0.2 by default)
# Available in some backports
# Perfect Forward Secrecy
SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256
9.12 Migration Path to RHEL 8+
Certificate-Specific Migration Steps
#============================================#
# PREPARE CERTIFICATES FOR MIGRATION
#============================================#
# 1. Verify all certificates use SHA-256+ (no SHA-1 or MD5)
for cert in /etc/pki/tls/certs/*.crt; do
openssl x509 -in "$cert" -noout -text | grep "Signature Algorithm"
done | grep -i sha1 && echo "⚠️ SHA-1 certificates found! Reissue before migration!"
# 2. Verify key sizes (2048+ bits)
for cert in /etc/pki/tls/certs/*.crt; do
SIZE=$(openssl x509 -in "$cert" -noout -text | grep "Public-Key" | grep -oP '\d+')
if [ "$SIZE" -lt 2048 ]; then
echo "⚠️ $cert: Key too small ($SIZE bits)"
fi
done
# 3. Backup everything
tar czf rhel7-certs-$(hostname)-$(date +%Y%m%d).tar.gz \
/etc/pki/tls/ \
/etc/pki/ca-trust/source/anchors/ \
/etc/httpd/conf.d/ssl.conf \
/etc/nginx/nginx.conf
# 4. Document certificate inventory
./generate-cert-inventory.sh > cert-inventory-pre-migration.csv
# 5. Test TLS 1.2 compatibility
# Ensure all services work with TLS 1.2 only
9.13 When RHEL 7 Makes Sense
Still Using RHEL 7? Consider:
Reasons to Stay (Temporarily):
- Extended Life Cycle Support contract active
- Critical legacy applications requiring TLS 1.0/1.1
- Migration planned for near future
- Testing RHEL 8/9 in parallel
Reasons to Migrate:
- ✅ Extended maintenance ended June 2024
- ✅ No crypto-policies (harder to manage)
- ✅ No TLS 1.3
- ✅ Security updates limited
- ✅ Modern applications dropping TLS 1.0/1.1 support
9.14 Key Takeaways
- RHEL 7 is manual - No crypto-policies, careful configuration needed
- OpenSSL 1.0.2k - Older syntax, no TLS 1.3
- TLS 1.0/1.1 enabled by default - Disable them manually
- SHA-1 still works - But won’t after migration to RHEL 8+
- certmonger available - But basic compared to RHEL 8+
- Plan migration - RHEL 7 support is ending
- Document everything - Makes migration easier
Quick Reference
┌───────────────────────────────────────────────────────┐
│ RHEL 7 CERTIFICATE QUICK REFERENCE │
├───────────────────────────────────────────────────────┤
│ OpenSSL: 1.0.2k-26 │
│ TLS: 1.0, 1.1, 1.2 (no 1.3) │
│ Policy: Manual configuration (no crypto-policies) │
│ │
│ Generate: openssl genrsa -out key.pem 2048 │
│ CSR: openssl req -new -key key.pem -out req.csr │
│ View: openssl x509 -in cert.crt -noout -text │
│ Test: openssl s_client -connect host:443 │
│ │
│ Harden: SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 │
│ SSLCipherSuite HIGH:!aNULL:!MD5:!3DES │
└───────────────────────────────────────────────────────┘
Chapter Navigation
| ← Previous: Chapter 8 - RHEL Versions & Certificate Evolution | Next: Chapter 10 - RHEL 8 & Crypto-Policies → |
|---|
Chapter 10: RHEL 8 & Crypto-Policies
Game Changer: RHEL 8 introduced crypto-policies, revolutionizing how certificates and cryptography are managed system-wide. This is the most important feature to understand for RHEL 8.
10.1 What Changed in RHEL 8?
Release: May 7, 2019 Support Until: May 31, 2029 Current Version: RHEL 8.10 (as of 2024)
Major Certificate-Related Changes:
| Feature | RHEL 7 | RHEL 8 |
|---|---|---|
| OpenSSL | 1.0.2k | 1.1.1k-14 |
| TLS 1.3 | ❌ No | ✅ Yes |
| Crypto-Policies | ❌ No | ✅ NEW! |
| TLS 1.0/1.1 | ✅ Enabled | ❌ Disabled (DEFAULT) |
| certmonger | Basic | Enhanced |
| Default Security | Mixed | Stronger |
Package: openssl-1.1.1k-14.el8_6.x86_64
10.2 Understanding Crypto-Policies
The Revolutionary Idea
RHEL 7 Problem:
❌ Configure Apache: SSLProtocol, SSLCipherSuite
❌ Configure NGINX: ssl_protocols, ssl_ciphers
❌ Configure Postfix: smtpd_tls_protocols
❌ Configure OpenLDAP: olcTLSProtocolMin
❌ Configure every application differently!
RHEL 8 Solution:
✅ Set ONE system-wide policy
✅ All applications automatically comply!
How It Works
┌────────────────────────────────────────┐
│ update-crypto-policies --set DEFAULT │ ← Single command
└──────────────────┬─────────────────────┘
│
┌───────────┴────────────┐
│ Crypto-Policies System │
└───────────┬────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
OpenSSL GnuTLS NSS
Postfix Apache NGINX
OpenSSH Kerberos BIND
(all apps!) (automatic!) (consistent!)
10.3 Available Crypto-Policies
The Four Main Policies
# Check current policy
update-crypto-policies --show
# Policies available in RHEL 8:
| Policy | TLS Versions | Min RSA | SHA-1 | 3DES | Use Case |
|---|---|---|---|---|---|
| DEFAULT | 1.2, 1.3 | 2048 | ❌ No | ❌ No | Standard (recommended) |
| LEGACY | 1.0+, all | 1024 | ⚠️ Yes | ⚠️ Yes | Old systems compatibility |
| FUTURE | 1.2, 1.3 | 3072 | ❌ No | ❌ No | Stricter security |
| FIPS | 1.2, 1.3 | 2048 | ❌ No | ❌ No | Federal compliance |
Policy Details
DEFAULT Policy:
TLS Versions: 1.2, 1.3
Minimum RSA/DH: 2048 bits
Minimum ECC: secp256r1 (P-256)
Ciphers: AES-GCM, ChaCha20-Poly1305, AES-CBC
Signatures: SHA-256, SHA-384, SHA-512
Blocked: MD5, SHA-1 signatures, 3DES, RC4, DSS
LEGACY Policy:
TLS Versions: 1.0, 1.1, 1.2, 1.3
Minimum RSA/DH: 1024 bits
Ciphers: Includes 3DES, weak ciphers
Signatures: Allows SHA-1
Use: Only for old systems compatibility (temporary!)
FUTURE Policy:
TLS Versions: 1.2, 1.3 (stricter ciphers)
Minimum RSA/DH: 3072 bits
Minimum ECC: secp384r1 (P-384)
Signatures: SHA-384, SHA-512 preferred
Blocked: Everything in DEFAULT, plus more
FIPS Policy:
TLS Versions: 1.2, 1.3
Algorithms: Only FIPS 140-2 approved
Requires: FIPS mode enabled
Strictest: Federal compliance requirements
10.4 Changing Crypto-Policies
Basic Policy Changes
#============================================#
# VIEW CURRENT POLICY
#============================================#
update-crypto-policies --show
# DEFAULT
#============================================#
# SET POLICY
#============================================#
# Set to FUTURE (stricter)
sudo update-crypto-policies --set FUTURE
# Set to LEGACY (less secure, for compatibility)
sudo update-crypto-policies --set LEGACY
# Set to FIPS (requires FIPS mode enabled)
sudo fips-mode-setup --enable
sudo reboot
sudo update-crypto-policies --set FIPS
# Return to DEFAULT
sudo update-crypto-policies --set DEFAULT
#============================================#
# APPLY POLICY (restart services)
#============================================#
# Crypto-policies update config files, but services must restart
sudo systemctl restart httpd nginx postfix
# Or reboot (ensures everything picks up new policy)
sudo reboot
What Happens When You Change Policy
# Example: Switching to FUTURE policy
# Before:
update-crypto-policies --show
# DEFAULT
# After:
sudo update-crypto-policies --set FUTURE
# Changes happen in:
ls -l /etc/crypto-policies/back-ends/
# opensslcnf.config ← OpenSSL config updated
# gnutls.config ← GnuTLS config updated
# nss.config ← NSS config updated
# bind.config ← BIND config updated
# ... and more
# View OpenSSL policy applied:
cat /etc/crypto-policies/back-ends/opensslcnf.config
10.5 Policy Impact on Certificates
DEFAULT Policy Impact
#============================================#
# WHAT DEFAULT POLICY ALLOWS/BLOCKS
#============================================#
# ✅ ALLOWED:
- TLS 1.2, 1.3
- RSA 2048+ bits
- AES-128-GCM, AES-256-GCM
- ChaCha20-Poly1305
- SHA-256, SHA-384, SHA-512 signatures
# ❌ BLOCKED:
- TLS 1.0, 1.1
- RSA < 2048 bits
- 3DES, RC4, DES
- MD5, SHA-1 signatures
- DSA keys
- Export ciphers
Testing Against Current Policy
#============================================#
# TEST IF YOUR CERTIFICATE WORKS
#============================================#
# Test TLS 1.2
openssl s_client -connect server.example.com:443 -tls1_2
# Test TLS 1.3
openssl s_client -connect server.example.com:443 -tls1_3
# Test specific cipher
openssl s_client -connect server.example.com:443 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
# See what ciphers are available under current policy
openssl ciphers -v | head -20
10.6 OpenSSL 1.1.1 Features (RHEL 8)
New Features
#============================================#
# TLS 1.3 SUPPORT (New in RHEL 8!)
#============================================#
# Test TLS 1.3
openssl s_client -connect server.example.com:443 -tls1_3
# TLS 1.3 benefits:
# - Faster handshake
# - Forward secrecy mandatory
# - Removed outdated features
#============================================#
# MODERN KEY GENERATION
#============================================#
# Old style (still works)
openssl genrsa -out server.key 2048
# New style (preferred on RHEL 8)
openssl genpkey -algorithm RSA -out server.key \
-pkeyopt rsa_keygen_bits:2048
# EC keys (elliptic curve)
openssl genpkey -algorithm EC -out ec.key \
-pkeyopt ec_paramgen_curve:P-256
#============================================#
# IMPROVED CSR GENERATION
#============================================#
# CSR with SANs (much easier than RHEL 7!)
openssl req -new -key server.key -out server.csr \
-subj "/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com,DNS:www.example.com,IP:10.0.0.100"
# Verify SANs
openssl req -in server.csr -noout -text | grep -A2 "Subject Alternative Name"
10.7 certmonger Enhancements in RHEL 8
Improved Features
#============================================#
# CERTMONGER ON RHEL 8
#============================================#
# Better IPA integration
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-D web.example.com \
-K host/web.example.com@REALM \
-C "systemctl reload httpd" # Post-save command (improved!)
# Enhanced status output
sudo getcert list -v
# Better error reporting
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Shows detailed error messages if renewal fails
RHEL 8 certmonger improvements:
- ✅ Better error messages
- ✅ Post-save command support
- ✅ Improved IPA integration
- ✅ More reliable renewal
10.8 Common RHEL 8 Scenarios
Scenario 1: Migrated from RHEL 7, TLS 1.0 App Breaks
Problem:
# Application worked on RHEL 7
# After migration to RHEL 8: connection failures
Diagnosis:
# Check crypto-policy
update-crypto-policies --show
# DEFAULT ← TLS 1.0/1.1 disabled!
# Check application logs
journalctl -xe | grep -i tls
# "wrong version number" or "no shared cipher"
Quick Fix (Temporary):
# Use LEGACY policy to allow TLS 1.0/1.1
sudo update-crypto-policies --set LEGACY
sudo systemctl restart <service>
Proper Fix:
# Update application to support TLS 1.2+
# Or configure application specifically (opt-out of policy)
Scenario 2: Need to Support Old Clients
Problem: Windows Server 2008, Java 7 clients can’t connect
Solution:
# Option 1: LEGACY policy (not recommended long-term)
sudo update-crypto-policies --set LEGACY
# Option 2: Custom policy module
# Create /etc/crypto-policies/policies/modules/COMPAT-OLD-CLIENTS.pmod
sudo update-crypto-policies --set DEFAULT:COMPAT-OLD-CLIENTS
# Option 3: Opt-out specific service
# Configure that service to allow TLS 1.0/1.1
Scenario 3: Testing Before Production
#============================================#
# TEST CRYPTO-POLICY IMPACT
#============================================#
# Current policy
CURRENT=$(update-crypto-policies --show)
# Test with FUTURE policy
sudo update-crypto-policies --set FUTURE
sudo systemctl restart httpd
# Run tests
curl https://localhost/
# Application test suite
# If problems:
sudo update-crypto-policies --set $CURRENT # Revert
sudo systemctl restart httpd
10.9 Per-Application Overrides
When to Override
Sometimes you need ONE application to opt-out of system policy:
Example: Legacy app needs TLS 1.0, but you want DEFAULT for everything else
#============================================#
# APACHE OVERRIDE (Opt-Out)
#============================================#
# /etc/httpd/conf.d/ssl.conf
# Add this to re-enable TLS 1.0 for Apache only:
SSLProtocol all
# Or use Include to load crypto-policy
Include /etc/crypto-policies/back-ends/httpd.config
# Then override specific settings after
# ⚠️ Note: This opts OUT of crypto-policies for Apache
# You now manage Apache TLS manually again
Better: Use policy modules (see Chapter 23 for details)
10.10 Troubleshooting Crypto-Policy Issues
Common Issues
Issue 1: “no shared cipher”
# Diagnosis
update-crypto-policies --show
# DEFAULT
# Test what ciphers are available
openssl ciphers -v
# Check client request
openssl s_client -connect localhost:443 -cipher 'ALL'
# Solution: Temporarily use LEGACY to identify issue
sudo update-crypto-policies --set LEGACY
# If works → cipher compatibility issue
# Proper fix: Update client or create custom policy
Issue 2: Service fails after policy change
# Symptom
sudo systemctl status httpd
# Failed to start
# Check logs
sudo journalctl -xe -u httpd | grep -i tls
# Revert policy
sudo update-crypto-policies --set DEFAULT
sudo systemctl restart httpd
10.11 Best Practices for RHEL 8
Recommendation: Use DEFAULT Policy
# For most environments:
sudo update-crypto-policies --set DEFAULT
# Reasons:
✅ Balanced security/compatibility
✅ Tested by Red Hat
✅ Meets modern standards
✅ Blocks known weak algorithms
✅ Works with most clients
When to Use Other Policies
Use LEGACY when:
- Temporarily supporting very old clients
- Migration period from RHEL 7
- Testing compatibility
- But: Plan to move back to DEFAULT ASAP!
Use FUTURE when:
- High-security requirements
- All clients are modern
- Want strictest settings
- Planning ahead
Use FIPS when:
- Federal compliance required
- Government contracts
- Regulated industries
- Security certifications needed
10.12 Key Takeaways (RHEL 8)
- Crypto-policies are THE feature - Learn them well
- DEFAULT policy is good - Don’t change without reason
- TLS 1.3 now available - Faster and more secure
- OpenSSL 1.1.1 - Modern features, better syntax
- certmonger enhanced - Better automation
- Migration from RHEL 7 - Test thoroughly
- Plan for RHEL 9 - OpenSSL 3.x coming
Quick Reference
┌───────────────────────────────────────────────────────────────┐
│ RHEL 8 CRYPTO-POLICIES QUICK REFERENCE │
├───────────────────────────────────────────────────────────────┤
│ OpenSSL: 1.1.1k-14 │
│ TLS: 1.2, 1.3 (DEFAULT policy) │
│ Feature: System-wide crypto-policies │
│ │
│ View policy: update-crypto-policies --show │
│ Set policy: sudo update-crypto-policies --set <POLICY> │
│ Policies: DEFAULT, LEGACY, FUTURE, FIPS │
│ │
│ Config files: /etc/crypto-policies/back-ends/ │
│ Restart: systemctl restart <services> │
│ │
│ Generate key: openssl genpkey -algorithm RSA -out key.pem │
│ CSR with SANs: openssl req -new -addext "subjectAltName=..." │
└───────────────────────────────────────────────────────────────┘
Chapter Navigation
Chapter 11: RHEL 9 Modern Security
Modern Standard: RHEL 9 represents the current state-of-the-art in Linux certificate management with OpenSSL 3.x, enhanced crypto-policies, and stricter security defaults.
11.1 RHEL 9 Overview
Release: May 17, 2022 Support Until: May 31, 2032 Current Version: RHEL 9.8
Major Changes from RHEL 8:
| Feature | RHEL 8 | RHEL 9 |
|---|---|---|
| OpenSSL | 1.1.1k | 3.5.5 |
| Architecture | Traditional | Provider-based |
| TLS 1.0/1.1 | LEGACY policy | ❌ Completely removed |
| Crypto-Policies | Basic | Subpolicies |
| Validation | Standard | Stricter |
| SHA-1 | Deprecated | Blocked |
| certmonger | Enhanced | Native IPA/tracking workflows |
Package: openssl-3.5.5-2.el9_8.x86_64
11.2 OpenSSL 3.5.5 - Major Changes
Provider Architecture (New!)
What Changed: OpenSSL 3.x introduced a “provider” system for different crypto implementations.
#============================================#
# LIST PROVIDERS (RHEL 9)
#============================================#
openssl list -providers
# Output:
# Providers:
# default
# name: OpenSSL Default Provider
# version: 3.5.5
# status: active
#
# fips
# name: OpenSSL FIPS Provider
# version: 3.5.5
# status: inactive (unless FIPS mode enabled)
#
# legacy
# name: OpenSSL Legacy Provider
# version: 3.5.5
# status: inactive
#
# base
# name: OpenSSL Base Provider
# version: 3.5.5
# status: active
Legacy Algorithms Require Explicit Provider
Breaking Change: MD5, Blowfish, CAST5 need -provider legacy
#============================================#
# USING LEGACY ALGORITHMS (RHEL 9)
#============================================#
# This FAILS on RHEL 9:
openssl md5 file.txt
# Error: unsupported
# This WORKS (explicit provider):
openssl md5 -provider legacy file.txt
# Why: Legacy algorithms disabled by default for security
Modern Key Generation (RHEL 9)
#============================================#
# GENERATE KEYS (RHEL 9)
#============================================#
# RSA 2048 (standard)
openssl genpkey -algorithm RSA -out server.key \
-pkeyopt rsa_keygen_bits:2048
# RSA 4096 (stronger)
openssl genpkey -algorithm RSA -out server.key \
-pkeyopt rsa_keygen_bits:4096
# EC P-256 (elliptic curve, recommended)
openssl genpkey -algorithm EC -out ec.key \
-pkeyopt ec_paramgen_curve:P-256
# EC P-384 (stronger)
openssl genpkey -algorithm EC -out ec.key \
-pkeyopt ec_paramgen_curve:P-384
#============================================#
# GENERATE CSR WITH SANS (RHEL 9)
#============================================#
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=State/O=Company/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com,DNS:www.example.com,IP:10.0.0.100" \
-addext "keyUsage=digitalSignature,keyEncipherment" \
-addext "extendedKeyUsage=serverAuth,clientAuth"
# Verify
openssl req -in server.csr -noout -text | grep -A5 "Subject Alternative Name"
11.3 Enhanced Crypto-Policies (RHEL 9)
Subpolicies (New Feature!)
RHEL 9 introduces policy modifiers:
#============================================#
# CRYPTO-POLICY SUBPOLICIES (RHEL 9)
#============================================#
# Base policy with NO-SHA1 module
sudo update-crypto-policies --set DEFAULT:NO-SHA1
# Multiple modules
sudo update-crypto-policies --set DEFAULT:NO-SHA1:GOST
# Common subpolicies:
# - NO-SHA1: Completely disable SHA-1 (even in signatures)
# - NO-ENFORCE-EMS: Disable Extended Master Secret
# - GOST: Enable GOST algorithms
# - NO-CAMELLIA: Disable Camellia cipher
# View available modules
ls /usr/share/crypto-policies/policies/modules/
Custom Crypto-Policy Modules (RHEL 9)
#============================================#
# CREATE CUSTOM POLICY MODULE
#============================================#
# Create custom module
sudo vi /etc/crypto-policies/policies/modules/CUSTOM.pmod
# Example content:
min_rsa_size = 3072
min_dh_size = 3072
min_dsa_size = 3072
# Apply
sudo update-crypto-policies --set DEFAULT:CUSTOM
# Test
openssl ciphers -v | head
11.4 Stricter Certificate Validation
What’s Stricter in RHEL 9?
#============================================#
# STRICTER VALIDATION EXAMPLES
#============================================#
# 1. SHA-1 signatures completely rejected
openssl verify sha1-signed-cert.crt
# Error: CA md too weak
# 2. Self-signed without proper CA trust rejected
curl https://self-signed.example.com/
# Error: certificate verify failed
# 3. Certificate chain must be complete
# Missing intermediate → connection fails
# 4. Hostname must match (CN or SAN)
openssl s_client -connect server.example.com:443 -servername different.example.com
# Verification error: hostname mismatch
# 5. Keys < 2048 bits rejected
# (even in LEGACY policy, < 1024 rejected)
Impact on Applications
Applications compiled against OpenSSL 3.x:
- May need code changes if using deprecated APIs
- Error handling may be different
- Custom crypto code needs testing
System administrators:
- ✅ Most changes transparent
- ✅ Commands mostly the same
- ⚠️ Stricter validation catches more issues (this is good!)
11.5 RHEL 9 Automation: certmonger, certbot, and IdM ACME
Use the Right Client for the Right CA
#============================================#
# AUTOMATION CHOICES ON RHEL 9
#============================================#
# Native certmonger workflow for FreeIPA / IdM
sudo dnf install certmonger -y
sudo systemctl enable --now certmonger
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
# Public Let's Encrypt workflow
# Use certbot, not a fake certmonger Let's Encrypt CA definition.
sudo certbot certonly --apache -d web.example.com
# IdM ACME workflow (optional)
# This points at your IPA server's ACME directory, not Let's Encrypt.
sudo certbot certonly \
--server https://ipa.example.com/acme/directory \
-d host.example.com
Important: IdM ACME and Let’s Encrypt are different CAs. certmonger remains the native RHEL tool for IPA, local CA, and tracked renewal workflows.
11.6 Trust Store Enhancements
Advanced Trust Management
#============================================#
# RHEL 9 TRUST MANAGEMENT
#============================================#
# Add CA (same as RHEL 7/8)
sudo cp corporate-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# NEW: Purpose-specific trust
trust anchor /path/to/ca.crt --purpose server-auth
# List trust with details
trust list --filter=ca-anchors
# Export specific CA
trust extract --format=pem-bundle --filter=ca-anchors \
--purpose server-auth /tmp/server-cas.pem
# Remove specific trust
trust anchor --remove "pkcs11:id=%CERT_ID%"
11.7 Common RHEL 9 Issues and Solutions
Issue 1: OpenSSL 3.x API Changes
Problem: Custom application fails with OpenSSL errors
Symptoms:
Error: EVP_PKEY_RSA no longer supported
Error: Provider not available
Solution:
# Check if application is using deprecated APIs
# Application needs recompilation against OpenSSL 3.x
# Temporary: Set compat environment variable (if available)
export OPENSSL_CONF=/etc/pki/tls/openssl-compat.cnf
# Long-term: Update application
Issue 2: SHA-1 Certificates Rejected
Problem: Legacy certificates with SHA-1 signatures fail
Symptoms:
openssl verify cert.crt
# error 3: CA md too weak
Solution:
# Reissue certificate with SHA-256+
# No workaround - SHA-1 is blocked for security
# Check certificate signature
openssl x509 -in cert.crt -noout -text | grep "Signature Algorithm"
# Must show: sha256WithRSAEncryption or better
Issue 3: Legacy Algorithm Not Available
Problem: Application needs MD5/RC4/etc.
Symptoms:
openssl md5 file.txt
# Error: unsupported
Solution:
# Use legacy provider explicitly
openssl md5 -provider legacy file.txt
# For applications: Update to use SHA-256+
# Or configure to load legacy provider
11.8 FIPS Mode on RHEL 9
Improved FIPS Support
#============================================#
# FIPS MODE (RHEL 9)
#============================================#
# Enable FIPS mode
sudo fips-mode-setup --enable
sudo reboot
# Check FIPS status
fips-mode-setup --check
# FIPS mode is enabled.
# Check FIPS provider loaded
openssl list -providers | grep -A3 fips
# fips
# name: OpenSSL FIPS Provider
# version: 3.5.5
# status: active
# Generate FIPS-compliant certificate
openssl req -new -x509 -days 365 -newkey rsa:2048 \
-keyout fips.key -out fips.crt \
-subj "/CN=$(hostname)" -provider fips
RHEL 9 FIPS:
- Uses OpenSSL 3.x FIPS provider
- FIPS 140-2 validated modules
- Transition to FIPS 140-3 in progress
11.9 Migration from RHEL 8
Certificate Impact
Moderate Impact:
- OpenSSL API changes (affects custom apps)
- Stricter validation (catches more issues)
- Legacy algorithms removed
- SHA-1 completely blocked
Pre-Migration Checks
#============================================#
# RHEL 8 → 9 CERTIFICATE PRE-MIGRATION
#============================================#
# 1. Check for SHA-1 certificates (will fail on RHEL 9)
for cert in /etc/pki/tls/certs/*.crt; do
SIG=$(openssl x509 -in "$cert" -noout -text | grep "Signature Algorithm" | head -2)
echo "$cert: $SIG"
done | grep -i sha1
# ⚠️ Reissue any SHA-1 certs before migration!
# 2. Check custom applications using OpenSSL
rpm -qa | grep -E "custom|local"
# Test these applications in RHEL 9 environment
# 3. Verify crypto-policy compatibility
update-crypto-policies --show
# 4. Test certificate operations
openssl s_client -connect localhost:443
# 5. Backup everything
tar czf rhel8-certs-backup-$(date +%Y%m%d).tar.gz \
/etc/pki/tls/ \
/etc/pki/ca-trust/source/anchors/
11.10 Best Practices for RHEL 9
Recommended Configuration
#============================================#
# RECOMMENDED SETUP (RHEL 9)
#============================================#
# 1. Use DEFAULT crypto-policy (unless specific need)
sudo update-crypto-policies --set DEFAULT
# 2. Use certmonger for native automation
sudo dnf install certmonger
sudo systemctl enable --now certmonger
# 3. For public sites: Use certbot for Let's Encrypt
sudo certbot certonly --apache -d web.example.com
# 4. For internal: Use FreeIPA with certmonger
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K host/$(hostname -f)@REALM
# 5. Generate EC keys (smaller, faster)
openssl genpkey -algorithm EC -out ec.key \
-pkeyopt ec_paramgen_curve:P-256
# 6. Always use SANs
openssl req -new -addext "subjectAltName=DNS:..."
11.11 New Features You Should Use
Feature 1: Stronger certmonger + IPA Workflows
# Native RHEL automation for internal certificates
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
# Better status output on RHEL 9
sudo getcert list -v
Feature 2: Enhanced Status Reporting
# More detailed status
sudo getcert list -v
# Better error messages
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Shows exact error reason if renewal fails
Feature 3: Crypto-Policy Subpolicies
# Fine-tune DEFAULT policy
sudo update-crypto-policies --set DEFAULT:NO-SHA1
# Multiple modifiers
sudo update-crypto-policies --set FUTURE:AD-SUPPORT
11.12 Breaking Changes from RHEL 8
API Changes
If you have custom applications:
// RHEL 8 (OpenSSL 1.1.1) - DEPRECATED on RHEL 9:
RSA *rsa = RSA_new();
// RHEL 9 (OpenSSL 3.x) - NEW API:
EVP_PKEY *pkey = EVP_PKEY_new();
Impact: Custom compiled applications may need updates
Command Changes
# Most commands work the same, but some edge cases:
# RHEL 8: This works
openssl md5 file.txt
# RHEL 9: Requires provider
openssl md5 -provider legacy file.txt
# Solution: Use SHA-256 instead
openssl sha256 file.txt
11.13 Common RHEL 9 Scenarios
Scenario 1: Fresh RHEL 9 Apache HTTPS Setup (Internal CA)
#============================================#
# COMPLETE APACHE HTTPS SETUP (RHEL 9)
#============================================#
# 1. Install Apache with mod_ssl
sudo dnf install httpd mod_ssl -y
# 2. Use certmonger + FreeIPA / IdM
sudo dnf install certmonger -y
sudo systemctl enable --now certmonger
# 3. Request certificate
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
# 4. Wait for certificate (check status)
sudo getcert list
# 5. Configure Apache to use certificate
# /etc/httpd/conf.d/ssl.conf already points to:
# SSLCertificateFile /etc/pki/tls/certs/localhost.crt
# Update to:
# SSLCertificateFile /etc/pki/tls/certs/web.crt
# SSLCertificateKeyFile /etc/pki/tls/private/web.key
# 6. Crypto-policy handles TLS settings automatically!
# No need to set SSLProtocol or SSLCipherSuite
# 7. Open firewall
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
# 8. Start Apache
sudo systemctl enable httpd
sudo systemctl start httpd
# 9. Test
curl -v https://$(hostname -f)/
# 10. Automatic renewal happens ~30 days before expiry!
Result: Fully automated internal HTTPS with FreeIPA and certmonger!
11.14 Troubleshooting RHEL 9 Certificates
Diagnostic Commands
#============================================#
# RHEL 9 CERTIFICATE DIAGNOSTICS
#============================================#
# Check OpenSSL version
openssl version
# OpenSSL 3.5.5
# Check providers
openssl list -providers
# Check crypto-policy
update-crypto-policies --show
# Test connection with TLS 1.3
openssl s_client -connect server:443 -tls1_3
# Check certificate signature algorithm
openssl x509 -in cert.crt -noout -text | grep "Signature Algorithm"
# Must be SHA-256+ on RHEL 9
# Test with legacy provider (if needed)
openssl md5 -provider legacy file.txt
# Check certmonger tracking
sudo getcert list
# View certmonger logs
sudo journalctl -u certmonger -f
Common RHEL 9 Errors
| Error | Cause | Solution |
|---|---|---|
| “CA md too weak” | SHA-1 signature | Reissue with SHA-256+ |
| “Provider not available” | Legacy algorithm used | Add -provider legacy or update to modern algorithm |
| “unsupported” on openssl command | Algorithm disabled | Use modern alternative or legacy provider |
| “no shared cipher” (migrated app) | Client uses old ciphers | Update client or use LEGACY policy temporarily |
| “certificate verify failed” | Stricter validation | Check cert chain, SANs, expiration |
11.15 When to Use RHEL 9
Ideal For:
✅ New deployments - Start with modern security ✅ Security-focused environments - Stricter defaults ✅ Modern applications - Benefit from TLS 1.3 ✅ Long-term support - 10 years maintenance ✅ Compliance requirements - Modern security standards
Migration Timing:
From RHEL 7:
- ✅ Yes! RHEL 7 maintenance ended June 2024
- Plan carefully - big jump (test thoroughly)
From RHEL 8:
- Moderate - OpenSSL 3.x is main change
- Test custom applications first
- SHA-1 certificates must be reissued
11.16 Key Takeaways
- OpenSSL 3.5.5 provider architecture - Understand providers
- Stricter validation - Catches security issues (good!)
- SHA-1 completely blocked - Reissue old certificates
- Crypto-policy subpolicies - Fine-tune security
- certmonger stays valuable for IPA and tracked renewal workflows
- TLS 1.3 mandatory support - Faster, more secure
- Plan testing - Custom apps may need updates
Quick Reference
┌──────────────────────────────────────────────────────────────┐
│ RHEL 9 CERTIFICATE QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ OpenSSL: 3.5.5 (provider architecture) │
│ TLS: 1.2, 1.3 (1.0/1.1 removed completely) │
│ Feature: Subpolicies, stricter validation │
│ │
│ Providers: openssl list -providers │
│ Policy: update-crypto-policies --show │
│ Subpolicy: update-crypto-policies --set DEFAULT:NO-SHA1 │
│ │
│ Generate key: openssl genpkey -algorithm RSA -out key.pem │
│ EC key: openssl genpkey -algorithm EC -out ec.pem │
│ -pkeyopt ec_paramgen_curve:P-256 │
│ │
│ Public ACME: certbot certonly --apache -d example.com │
│ certmonger: ipa-getcert request ... │
│ Legacy algo: openssl md5 -provider legacy file.txt │
└──────────────────────────────────────────────────────────────┘
⚠️ SHA-1 is BLOCKED - reissue old certificates!
✅ Use certmonger for automation
✅ DEFAULT policy works for most cases
Chapter Navigation
Chapter 12: RHEL 10 Current Features
Cutting Edge: RHEL 10 GA was released May 20, 2025; RHEL 10.2 is the current minor release. Learn about the latest features and prepare for the future of certificate management on Red Hat Enterprise Linux.
12.1 RHEL 10 Overview
GA Release: May 20, 2025 Current Version: RHEL 10.2 Support Until: May 31, 2035 Status: ✅ Production Release
Key Characteristics:
- OpenSSL Version: 3.5.5-2 (package:
openssl-3.5.5-2.el10_2.x86_64) - Same base as: RHEL 9.8 (OpenSSL 3.5.5)
- Focus: Continued hardening, post-quantum prep, cloud-native
- Philosophy: Incremental improvement over RHEL 9
Important: RHEL 10 features may evolve across minor versions (10.1, 10.2, etc.). Always consult official Red Hat documentation for your specific RHEL 10.x release.
12.2 What’s New vs. RHEL 9?
Key Differences
| Feature | RHEL 9 | RHEL 10 |
|---|---|---|
| OpenSSL | 3.5.5 | 3.5.5 (same base) |
| Crypto-Policies | Subpolicies | Enhanced subpolicies |
| TLS Versions | 1.2, 1.3 | 1.3 preferred, 1.2 supported |
| FIPS | 140-2 modules | 140-3 transition |
| Security Defaults | Strict | Stricter |
| Container Support | Good | Enhanced |
| Post-Quantum | Foundation | Active preparation |
Package: openssl-3.5.5-2.el10_2.x86_64
Not a Revolutionary Change
Unlike RHEL 7→8 (crypto-policies) or RHEL 8→9 (OpenSSL 3.x), RHEL 10 is an incremental improvement.
Think of it as:
- RHEL 7 → 8: 🚀 Revolutionary (crypto-policies)
- RHEL 8 → 9: 🔄 Major (OpenSSL 3.x)
- RHEL 9 → 10: ⬆️ Incremental (refinements)
12.3 Certificate Management on RHEL 10
Same Foundation as RHEL 9
#============================================#
# RHEL 10 CERTIFICATE BASICS
#============================================#
# Same OpenSSL version as RHEL 9.8
openssl version
# OpenSSL 3.5.5 27 Jan 2026
# Same crypto-policies system
update-crypto-policies --show
# Same certmonger
getcert list
# Same directory structure
ls -la /etc/pki/tls/
Bottom Line: If you know RHEL 9, you know RHEL 10 certificates!
12.4 Enhanced Security Features
Stricter Defaults
#============================================#
# RHEL 10 SECURITY ENHANCEMENTS
#============================================#
# 1. DEFAULT policy is stricter
# - Stronger cipher preferences
# - Additional weak algorithms removed
# - Enhanced validation
# 2. LEGACY policy more restricted
# - Fewer legacy algorithms allowed
# - Stronger minimums even in LEGACY
# 3. Container certificate management improved
# - Better integration with Podman
# - Simplified cert mounting
# - Enhanced secret management
Post-Quantum Cryptography Preparation
Foundation for Future:
# RHEL 10 prepares for post-quantum algorithms
# (Not yet default, but infrastructure ready)
# Future capability (as standards finalize):
# - ML-KEM (Module-Lattice Key Encapsulation)
# - ML-DSA (Module-Lattice Digital Signatures)
# - Hybrid classical/quantum cryptography
# Current status: Monitoring NIST standards
# Expected: RHEL 10.x minor releases will add PQC support
Note: Post-quantum cryptography is still evolving. RHEL 10 provides foundation, actual implementation will come as standards are finalized.
12.5 RHEL 10-Specific Features
Feature 1: Enhanced Crypto-Policy Modules
#============================================#
# RHEL 10 CRYPTO-POLICY IMPROVEMENTS
#============================================#
# More granular control
sudo update-crypto-policies --set DEFAULT:NO-SHA1
# Better validation
update-crypto-policies --check
# Improved error messages when policies conflict
Feature 2: Improved Container Certificate Support
#============================================#
# CONTAINERS WITH CERTIFICATES (RHEL 10)
#============================================#
# Easier certificate mounting in Podman
podman run -d \
-v /etc/pki/tls/certs/web.crt:/certs/web.crt:ro \
-v /etc/pki/tls/private/web.key:/certs/web.key:ro \
-p 443:443 \
nginx
# Enhanced secret management
podman secret create web-cert /etc/pki/tls/certs/web.crt
podman secret create web-key /etc/pki/tls/private/web.key
# Use secrets in container
podman run -d --secret web-cert --secret web-key nginx
Feature 3: Enhanced FIPS Mode
#============================================#
# FIPS ON RHEL 10
#============================================#
# FIPS mode with OpenSSL 3.x FIPS provider
sudo fips-mode-setup --enable
sudo reboot
# Check FIPS status
fips-mode-setup --check
# RHEL 10: Transition toward FIPS 140-3
# Current: Still FIPS 140-2 validated modules
# Future: FIPS 140-3 compliance as certification completes
12.6 Migration from RHEL 9
Should You Upgrade?
Upgrade Considerations:
Reasons to Upgrade:
- ✅ Want 10+ years of support (until 2035)
- ✅ Need latest security enhancements
- ✅ Future-proofing (post-quantum prep)
- ✅ Enhanced container support
- ✅ Latest features and improvements
Reasons to Wait:
- ⏸️ RHEL 9 supported until 2032
- ⏸️ No urgent certificate-related features
- ⏸️ Let others test RHEL 10 in production first
- ⏸️ Want to wait for RHEL 10.3 or 10.4
Certificate Impact: LOW
- Same OpenSSL base (3.5.5)
- Same tools and commands
- Minimal breaking changes
- Mostly transparent
Migration Process
#============================================#
# RHEL 9 → RHEL 10 CERTIFICATE MIGRATION
#============================================#
# 1. Pre-migration: Verify certificates
for cert in /etc/pki/tls/certs/*.crt; do
openssl x509 -in "$cert" -noout -text | grep "Signature Algorithm"
done
# All should show SHA-256+ (no SHA-1 or MD5)
# 2. Backup
tar czf rhel9-certs-backup-$(date +%Y%m%d).tar.gz \
/etc/pki/tls/ \
/etc/pki/ca-trust/source/anchors/
# 3. Perform RHEL upgrade
sudo leapp upgrade
# 4. Verify crypto-policy
update-crypto-policies --show
# 5. Restart services
sudo systemctl restart httpd nginx postfix
# 6. Test certificates
curl -v https://localhost/
openssl s_client -connect localhost:443
# 7. Check certmonger
sudo getcert list
12.7 Best Practices for RHEL 10
Recommended Setup
#============================================#
# RHEL 10 RECOMMENDED CONFIGURATION
#============================================#
# 1. Use DEFAULT crypto-policy (already optimal)
sudo update-crypto-policies --set DEFAULT
# 2. Prefer TLS 1.3
# (Automatically preferred by DEFAULT policy)
# 3. Use EC keys for new certificates
openssl genpkey -algorithm EC -out ec.key \
-pkeyopt ec_paramgen_curve:P-256
# 4. Automate with the right tool
# Public Let's Encrypt certificate: use certbot
sudo certbot certonly --apache -d web.example.com
# Internal FreeIPA / IdM certificate: use certmonger
# sudo ipa-getcert request \
# -f /etc/pki/tls/certs/web.crt \
# -k /etc/pki/tls/private/web.key \
# -K HTTP/web.example.com@REALM \
# -D web.example.com \
# -C "systemctl reload httpd"
# 5. Monitor certificates
# Use built-in monitoring or external tools
# 6. Plan for future PQC
# Keep up with RHEL 10.x minor releases
12.8 Looking Ahead: Post-Quantum Readiness
What is Post-Quantum Cryptography?
Problem: Future quantum computers could break current encryption (RSA, ECC) Solution: New quantum-resistant algorithms
NIST Standards (Finalized 2024):
- ML-KEM-768 (Key Encapsulation)
- ML-DSA-65 (Digital Signatures)
- SLH-DSA (Stateless signatures)
RHEL 10 Role:
- Provides foundation for PQC
- OpenSSL 3.x architecture supports new algorithms
- Future RHEL 10.x releases will add PQC support
Hybrid Cryptography (Future)
# Future capability in RHEL 10.x:
# Use both classical AND quantum-resistant crypto
# Example (conceptual - not yet in RHEL 10.2):
openssl genpkey -algorithm hybrid-rsa-mlkem768 -out hybrid.key
# Provides:
# - Security against classical attacks (RSA)
# - Security against quantum attacks (ML-KEM)
Note: PQC support is coming in future RHEL 10.x minor versions as standards are finalized and tested.
12.9 What Stays the Same
No Major Breaking Changes
#============================================#
# FAMILIAR COMMANDS STILL WORK
#============================================#
# Generate key (same as RHEL 9)
openssl genpkey -algorithm RSA -out server.key \
-pkeyopt rsa_keygen_bits:2048
# Generate CSR (same as RHEL 9)
openssl req -new -key server.key -out server.csr \
-subj "/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com"
# View certificate (same)
openssl x509 -in cert.crt -noout -text
# Test connection (same)
openssl s_client -connect server:443
# Trust management (same)
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# certmonger (same)
sudo getcert list
# crypto-policies (same)
update-crypto-policies --show
If you know RHEL 9, you’re ready for RHEL 10!
12.10 When to Adopt RHEL 10
Adoption Timeline Recommendations
Early Adopters (2025-2026):
- Testing environments
- Non-critical workloads
- Want latest features
- Security research
Mainstream (2026-2027):
- New deployments
- Refreshed infrastructure
- After RHEL 10.3/10.4 release
- When major apps certified
Conservative (2027-2028):
- Critical production systems
- Stable workloads
- After extensive community testing
- When migration from RHEL 9 necessary
Current Recommendation (Late 2025):
- ✅ New projects: Consider RHEL 10
- ⏸️ Existing RHEL 9: No urgency to upgrade
- ✅ RHEL 8 or older: Evaluate RHEL 9 or 10
- ❌ RHEL 7: Upgrade required (support ended)
12.11 Monitoring RHEL 10 Evolution
Stay Updated
# Check RHEL 10 minor version
cat /etc/redhat-release
# Red Hat Enterprise Linux release 10.2 (Coughlan)
# Check for updates
sudo dnf check-update
# Monitor Red Hat announcements
# - https://access.redhat.com/articles/3078
# - RHEL 10 release notes
# - Red Hat security advisories
# Subscribe to Red Hat newsletters
# Follow release notes for 10.3, 10.4, etc.
Features to Watch For
Expected in RHEL 10.x minor releases:
- Post-quantum cryptography support
- Additional crypto-policy enhancements
- Further container integration
- Enhanced automation tools
- Additional FIPS 140-3 modules
12.12 Practical RHEL 10 Certificate Setup
Complete Example: Modern HTTPS Setup
#!/bin/bash
# Complete modern HTTPS setup on RHEL 10
echo "=== RHEL 10 Modern HTTPS Setup ==="
# 1. Install packages
sudo dnf install -y httpd mod_ssl epel-release certbot python3-certbot-apache
# 2. Enable services
sudo systemctl enable --now httpd
# 3. Request Let's Encrypt certificate with certbot
sudo certbot --apache -d $(hostname -f)
# 4. Verify the certificate
sudo certbot certificates
# 5. Update Apache configuration
# certbot usually updates Apache automatically; adjust manually only if needed
sudo sed -i "s|SSLCertificateFile.*|SSLCertificateFile /etc/letsencrypt/live/$(hostname -f)/fullchain.pem|" \
/etc/httpd/conf.d/ssl.conf
sudo sed -i "s|SSLCertificateKeyFile.*|SSLCertificateKeyFile /etc/letsencrypt/live/$(hostname -f)/privkey.pem|" \
/etc/httpd/conf.d/ssl.conf
# 6. Crypto-policy already optimal (DEFAULT)
update-crypto-policies --show
# 7. Open firewall
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
# 8. Reload Apache
sudo systemctl reload httpd
# 9. Test
curl -v https://$(hostname -f)/
echo "✅ RHEL 10 modern HTTPS setup complete!"
echo " - TLS 1.3 supported"
echo " - Let's Encrypt certificate"
echo " - Automatic renewal enabled"
echo " - Optimal security (DEFAULT policy)"
12.13 Future-Proofing Strategies
Preparing for RHEL 10.x Evolution
#============================================#
# FUTURE-PROOF CERTIFICATE MANAGEMENT
#============================================#
# 1. Use modern algorithms (ready for PQC transition)
# Prefer EC over RSA
openssl genpkey -algorithm EC -out ec.key -pkeyopt ec_paramgen_curve:P-256
# 2. Keep certificates short-lived (90 days or less)
# Easier to rotate when algorithms change
# 3. Automate everything
# Use certbot for public ACME and certmonger for IPA/internal workflows
# 4. Monitor Red Hat announcements
# Subscribe to security and release notifications
# 5. Test PQC when available
# Be early tester of new features in RHEL 10.x
# 6. Document your setup
# Makes future transitions easier
12.14 Known Issues and Workarounds
Issue 1: Same as RHEL 9 (OpenSSL 3.x)
Most RHEL 9 issues apply to RHEL 10:
- Legacy algorithms need
-provider legacy - SHA-1 blocked
- Custom apps may need OpenSSL 3.x updates
Reference: See Chapter 11 for OpenSSL 3.x issues
Issue 2: Even Stricter Validation
RHEL 10 may catch issues RHEL 9 allowed:
# Example: Marginal certificate that worked on RHEL 9
# might fail on RHEL 10
# Solution: Always use best practices
# - SHA-256+ signatures
# - 2048+ bit keys (4096 recommended)
# - Proper SANs
# - Valid trust chains
12.15 When to Choose RHEL 10
Decision Matrix
| Scenario | RHEL 9 | RHEL 10 | Recommendation |
|---|---|---|---|
| New deployment 2025+ | ✅ Good | ✅ Better | RHEL 10 |
| Existing RHEL 9 | ✅ Keep | ⏸️ Wait | Stay on 9 for now |
| Migrating from RHEL 8 | ✅ Yes | ✅ Consider | Either (9 is safer) |
| Migrating from RHEL 7 | ✅ Yes | ⚠️ Big jump | Go to 9 first |
| 10+ year horizon | ⏸️ 2032 support | ✅ 2035 support | RHEL 10 |
| Cutting edge security | ✅ Good | ✅ Better | RHEL 10 |
| Production critical | ✅ Proven | ⏸️ Newer | RHEL 9 (safer) |
12.16 Key Takeaways
- RHEL 10 = RHEL 9 + incremental improvements
- Same OpenSSL 3.5.5 base - No major API changes
- Stricter security defaults - Good for security
- Post-quantum preparation - Future-ready infrastructure
- No urgent certificate changes - Transition is smooth
- RHEL 9 knowledge transfers - Same tools and commands
- Watch for minor releases - 10.3, 10.4 may add features
12.17 Troubleshooting RHEL 10
Diagnostic Approach
#============================================#
# RHEL 10 CERTIFICATE TROUBLESHOOTING
#============================================#
# Use troubleshooting methodology (Chapter 27) and RHEL 9 patterns (Chapter 11)
# 1. Verify RHEL 10 version
cat /etc/redhat-release
# Red Hat Enterprise Linux release 10.2 (Coughlan)
# 2. Check OpenSSL
openssl version
# OpenSSL 3.5.5
# 3. Check crypto-policy
update-crypto-policies --show
# 4. Test certificate
openssl x509 -in cert.crt -noout -text
# 5. Test connection
openssl s_client -connect server:443 -tls1_3
# 6. Check providers (if issues)
openssl list -providers
# 7. Check logs
sudo journalctl -xe | grep -i cert
No new troubleshooting techniques needed - same as RHEL 9!
12.18 Recommended Migration Path
From RHEL 9 to RHEL 10
#============================================#
# CERTIFICATE-SAFE RHEL 9→10 MIGRATION
#============================================#
# Phase 1: Preparation
# - Backup all certificates
# - Document current configuration
# - Test in lab environment
# Phase 2: Migration
# - Use standard RHEL upgrade process
# - Certificates should transfer seamlessly
# Phase 3: Verification
# - Verify crypto-policy unchanged
# - Test all certificate operations
# - Confirm certmonger tracking maintained
# - Test services using certificates
# Phase 4: Optimization
# - Consider EC keys for new certificates
# - Review and update crypto-policy if needed
# - Monitor for RHEL 10.x enhancements
12.19 Documentation and Resources
Official Resources
## RHEL 10 Certificate Resources
### Official Documentation
- RHEL 10 Release Notes (check for your specific 10.x version)
- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/10
### Security Updates
- https://access.redhat.com/security/
- Subscribe to RHEL security announcements
### Crypto-Policies
- https://access.redhat.com/articles/3642912
- Check for RHEL 10-specific updates
### Support
- Red Hat Customer Portal
- Red Hat Support Cases
- RHEL community forums
12.20 Quick Reference
┌──────────────────────────────────────────────────────────────┐
│ RHEL 10 CERTIFICATE QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ OpenSSL: 3.5.5-2 (same as RHEL 9.8) │
│ TLS: 1.3 preferred, 1.2 supported │
│ GA Release: May 20, 2025 (RHEL 10.0) │
│ Status: Production ready │
│ │
│ Key Change: Incremental security improvements │
│ Migration: Low impact from RHEL 9 │
│ Commands: Same as RHEL 9 │
│ Tools: Same as RHEL 9 │
│ │
│ Future: Post-quantum crypto preparation │
│ Watch for features in 10.3, 10.4+ │
│ │
│ Verify: cat /etc/redhat-release │
│ openssl version │
│ update-crypto-policies --show │
└──────────────────────────────────────────────────────────────┘
✅ If you know RHEL 9 certificates, you know RHEL 10!
⚠️ Always check official docs for your specific 10.x minor version
Chapter Navigation
Chapter 13: Cross-Version Compatibility
Real-World Challenge: Your environment probably has RHEL 7, 8, and 9 systems all talking to each other. How do you make certificates work across all versions?
13.1 The Mixed Environment Reality
Most enterprises don’t upgrade everything at once. You’ll encounter:
Production Environment (Typical):
├── Legacy App Server (RHEL 7)
├── Database Server (RHEL 8)
├── Web Tier (RHEL 9)
├── Management Node (RHEL 10)
└── Clients: Windows, Mac, Linux, Mobile
Challenge: These systems have different:
- TLS version support
- Cipher suites
- Certificate validation rules
- OpenSSL versions
- Crypto-policies (or lack thereof)
13.2 Common Compatibility Issues
Issue 1: TLS Version Mismatches
Scenario: RHEL 9 server (TLS 1.2+ only) ← RHEL 7 client (TLS 1.0/1.1 default)
# RHEL 7 client trying to connect to RHEL 9 server
curl https://rhel9-server.example.com/
# Error: SSL routines:ssl3_get_record:wrong version number
# Why: RHEL 7 tries TLS 1.0 first, RHEL 9 rejects it
Solution:
# Option 1: Update RHEL 7 client to use TLS 1.2
# Edit /etc/httpd/conf.d/ssl.conf (if Apache client)
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Option 2: Temporary LEGACY policy on RHEL 9 (NOT recommended)
# sudo update-crypto-policies --set LEGACY # On RHEL 9 server
# Option 3: Best - Upgrade RHEL 7 systems!
Issue 2: Cipher Suite Mismatches
Scenario: Modern server doesn’t support old client ciphers
# RHEL 7 client → RHEL 9 server
openssl s_client -connect rhel9-server:443 -cipher '3DES'
# Error: no shared cipher
Why: 3DES is blocked in RHEL 8+ DEFAULT policy
Solution:
# Check what ciphers are available
openssl ciphers -v 'HIGH:!aNULL:!MD5' | head
# Test specific cipher on client
openssl s_client -connect server:443 -cipher 'AES256-GCM-SHA384'
# On RHEL 9 server, if you MUST support old clients:
sudo update-crypto-policies --set LEGACY # Temporary!
Issue 3: Certificate Validation Differences
Scenario: SHA-1 signed certificate
Certificate with SHA-1 signature:
├── ✅ Works on RHEL 7
├── ❌ Rejected by RHEL 8 DEFAULT
├── ❌ Rejected by RHEL 9
└── ❌ Rejected by RHEL 10
Solution:
# Reissue certificate with SHA-256 or better
openssl req -new -key server.key -out server.csr -sha256
# Verify signature algorithm
openssl x509 -in cert.crt -noout -text | grep "Signature Algorithm"
# Should show: sha256WithRSAEncryption (or better)
13.3 Compatibility Matrix
Client → Server Compatibility
| Client ↓ Server → | RHEL 7 Server | RHEL 8 Server (DEFAULT) | RHEL 9 Server (DEFAULT) | RHEL 10 Server |
|---|---|---|---|---|
| RHEL 7 Client | ✅ Full | ⚠️ TLS 1.0/1.1 issue | ⚠️ TLS 1.0/1.1 issue | ⚠️ TLS 1.0/1.1 issue |
| RHEL 8 Client | ✅ Full | ✅ Full | ✅ Full | ✅ Full |
| RHEL 9 Client | ⚠️ Weak cipher warning | ✅ Full | ✅ Full | ✅ Full |
| RHEL 10 Client | ⚠️ Weak cipher warning | ✅ Full | ✅ Full | ✅ Full |
| Windows 10 | ✅ Full | ✅ Full | ✅ Full | ✅ Full |
| Windows Server 2012 | ✅ Full | ⚠️ May need TLS 1.0/1.1 | ⚠️ May need TLS 1.0/1.1 | ⚠️ May need TLS 1.0/1.1 |
| Old Java 7 | ✅ Full | ❌ No TLS 1.2 | ❌ No TLS 1.2 | ❌ No TLS 1.2 |
Legend:
- ✅ Works without changes
- ⚠️ Works with configuration changes
- ❌ Incompatible without major updates
13.4 Certificate Requirements for Maximum Compatibility
The “Universal” Certificate Profile
To work across all RHEL versions (7-10) and external clients:
# Certificate Requirements:
✅ RSA key: 2048 bits minimum (4096 for future-proofing)
✅ Signature: SHA-256 or better (not SHA-1!)
✅ Subject Alternative Names (SANs) required
✅ Validity: ≤ 365 days (browser requirement)
✅ Key Usage: Proper extensions set
❌ Avoid: SHA-1 signatures
❌ Avoid: RSA < 2048 bits
❌ Avoid: Missing SANs
❌ Avoid: CN-only certificates
Generate a Compatible Certificate
#============================================#
# STEP 1: Generate Key (works on all RHEL versions)
#============================================#
# RSA 2048 (minimum, compatible)
openssl genpkey -algorithm RSA -out universal.key -pkeyopt rsa_keygen_bits:2048
# Or RSA 4096 (better, still compatible)
openssl genpkey -algorithm RSA -out universal.key -pkeyopt rsa_keygen_bits:4096
#============================================#
# STEP 2: Create CSR with SANs
#============================================#
openssl req -new -key universal.key -out universal.csr \
-subj "/C=US/ST=State/L=City/O=Company/CN=server.example.com" \
-addext "subjectAltName=DNS:server.example.com,DNS:www.example.com,IP:10.0.0.100" \
-addext "keyUsage=digitalSignature,keyEncipherment" \
-addext "extendedKeyUsage=serverAuth,clientAuth"
#============================================#
# STEP 3: Verify CSR
#============================================#
openssl req -in universal.csr -noout -text | grep -A2 "Subject Alternative Name"
# Should show your SANs
openssl req -in universal.csr -noout -text | grep "Public-Key"
# Should show: Public-Key: (2048 bit) or higher
13.5 Testing Cross-Version Compatibility
Test Suite Script
#!/bin/bash
# test-cert-compatibility.sh
# Tests if certificate works from various RHEL versions
SERVER_HOST="server.example.com"
SERVER_PORT="443"
CERT_FILE="/etc/pki/tls/certs/server.crt"
echo "=== Certificate Compatibility Test Suite ==="
echo ""
#============================================#
# TEST 1: Certificate Properties
#============================================#
echo "1. Certificate Properties:"
echo " Signature Algorithm:"
openssl x509 -in "$CERT_FILE" -noout -text | grep "Signature Algorithm" | head -1
echo " Key Size:"
openssl x509 -in "$CERT_FILE" -noout -text | grep "Public-Key"
echo " SANs:"
openssl x509 -in "$CERT_FILE" -noout -ext subjectAltName 2>/dev/null || echo " No SANs found!"
echo ""
#============================================#
# TEST 2: TLS Version Support
#============================================#
echo "2. TLS Version Support:"
for version in tls1 tls1_1 tls1_2 tls1_3; do
if openssl s_client -connect "$SERVER_HOST:$SERVER_PORT" -"$version" </dev/null 2>&1 | grep -q "Cipher"; then
echo " ${version//_/.}: ✅ Supported"
else
echo " ${version//_/.}: ❌ Not supported"
fi
done
echo ""
#============================================#
# TEST 3: Cipher Suite Compatibility
#============================================#
echo "3. Common Cipher Tests:"
# Modern cipher (RHEL 8+)
if openssl s_client -connect "$SERVER_HOST:$SERVER_PORT" -cipher 'ECDHE-RSA-AES256-GCM-SHA384' </dev/null 2>&1 | grep -q "Cipher"; then
echo " Modern cipher (ECDHE-RSA-AES256-GCM-SHA384): ✅"
else
echo " Modern cipher: ❌"
fi
# Legacy cipher (RHEL 7)
if openssl s_client -connect "$SERVER_HOST:$SERVER_PORT" -cipher 'AES256-SHA' </dev/null 2>&1 | grep -q "Cipher"; then
echo " Legacy cipher (AES256-SHA): ✅ (may indicate LEGACY policy)"
else
echo " Legacy cipher: ❌ (good for security)"
fi
#============================================#
# TEST 4: Certificate Validation
#============================================#
echo ""
echo "4. Certificate Validation:"
if openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt "$CERT_FILE" | grep -q "OK"; then
echo " Trust chain: ✅ Valid"
else
echo " Trust chain: ❌ Invalid"
fi
echo ""
echo "=== Test Complete ==="
Usage:
chmod +x test-cert-compatibility.sh
sudo ./test-cert-compatibility.sh
13.6 Handling Specific Compatibility Scenarios
Scenario 1: RHEL 7 Client → RHEL 9 Server
Problem: Connection fails with TLS version error
Client-Side Fix (RHEL 7):
# For curl
curl --tlsv1.2 https://rhel9-server/
# For wget
wget --secure-protocol=TLSv1_2 https://rhel9-server/
# For applications using OpenSSL, set environment variable
export OPENSSL_CONF=/etc/pki/tls/openssl-tls12.cnf
# Create custom config
cat > /etc/pki/tls/openssl-tls12.cnf << 'EOF'
openssl_conf = default_conf
[default_conf]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT@SECLEVEL=1
EOF
Server-Side Fix (RHEL 9) - NOT RECOMMENDED:
# Only if absolutely necessary and temporarily!
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd # Or your service
Scenario 2: Mixed CA Trust
Problem: Corporate CA trusted on some systems but not others
Solution: Consistent trust store across all versions
#============================================#
# DEPLOYMENT SCRIPT (run on all RHEL versions)
#============================================#
#!/bin/bash
# deploy-corporate-ca.sh
CA_CERT_URL="http://pki.example.com/ca-chain.crt"
CA_CERT_FILE="/etc/pki/ca-trust/source/anchors/corporate-ca-chain.crt"
# Download CA certificate
curl -o "$CA_CERT_FILE" "$CA_CERT_URL"
# Update trust store (works on all RHEL versions)
update-ca-trust extract
# Verify
if trust list | grep -q "Corporate Root CA"; then
echo "✅ Corporate CA installed successfully"
else
echo "❌ Corporate CA installation failed"
exit 1
fi
Scenario 3: Application Using Old TLS Library
Problem: Java 7 application can’t connect to modern servers
Check Java TLS Support:
# Check Java version
java -version
# Test TLS support
java -Djavax.net.debug=ssl:handshake -jar app.jar 2>&1 | grep "TLS"
Options:
# Option 1: Upgrade Java (best)
sudo dnf install java-11-openjdk
# Option 2: Enable TLS 1.2 in Java 7 (if upgrade impossible)
# Add to Java startup:
-Dhttps.protocols=TLSv1.2
# Option 3: Use wrapper script
#!/bin/bash
export JAVA_OPTS="-Dhttps.protocols=TLSv1.2 -Djavax.net.ssl.trustStore=/etc/pki/java/cacerts"
java $JAVA_OPTS -jar /path/to/app.jar
13.7 Crypto-Policy Compatibility
Understanding Policy Impact Across Versions
#============================================#
# RHEL 7 (No crypto-policies)
#============================================#
# Manual configuration in each app
# Apache: /etc/httpd/conf.d/ssl.conf
# NGINX: /etc/nginx/nginx.conf
# Postfix: /etc/postfix/main.cf
#============================================#
# RHEL 8/9/10 (crypto-policies)
#============================================#
# System-wide control
update-crypto-policies --set DEFAULT
# To support RHEL 7 clients, might need:
update-crypto-policies --set LEGACY # Temporarily!
Policy Equivalents for Mixed Environments
If you need to maintain compatibility:
Option A: Use LEGACY on modern systems (not recommended long-term)
# On RHEL 8/9/10 servers
sudo update-crypto-policies --set LEGACY
Option B: Configure RHEL 7 to match DEFAULT (recommended)
# On RHEL 7, manually configure to match RHEL 8+ DEFAULT
# Apache example:
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!CAMELLIA
SSLHonorCipherOrder on
13.8 Migration Path: Gradual Upgrade
Phase 1: Prepare RHEL 7 (Pre-Migration)
# 1. Reissue all certificates with SHA-256+
# 2. Test TLS 1.2 compatibility
# 3. Update cipher configurations
# 4. Document current certificate inventory
Phase 2: Deploy RHEL 8 (Transition)
# 1. Start with LEGACY policy
sudo update-crypto-policies --set LEGACY
# 2. Deploy services
# 3. Test thoroughly
# 4. Gradually switch to DEFAULT
sudo update-crypto-policies --set DEFAULT
Phase 3: Upgrade to RHEL 9 (Modernization)
# 1. All clients should be RHEL 8+ or TLS 1.2 capable
# 2. Use DEFAULT policy
# 3. Monitor for compatibility issues
# 4. Consider FUTURE policy after stabilization
13.9 Troubleshooting Cross-Version Issues
Diagnostic Commands
#============================================#
# ON CLIENT
#============================================#
# Test specific TLS version
openssl s_client -connect server:443 -tls1_2
# Test with verbose output
curl -v --tlsv1.2 https://server/
# Check client OpenSSL
openssl version
openssl ciphers -v
#============================================#
# ON SERVER
#============================================#
# Check crypto-policy (RHEL 8+)
update-crypto-policies --show
# Check OpenSSL config
openssl version
cat /etc/crypto-policies/back-ends/opensslcnf.config
# Test server certificate
openssl s_client -connect localhost:443 -servername $(hostname)
# Check service logs
sudo journalctl -xe | grep -i tls
sudo tail -f /var/log/httpd/ssl_error_log
Common Error Messages
| Error | Cause | Solution |
|---|---|---|
| “wrong version number” | TLS version mismatch | Update client to TLS 1.2+ |
| “no shared cipher” | Cipher incompatibility | Check crypto-policy or cipher config |
| “certificate verify failed” | Trust or validation issue | Check CA trust, certificate validity |
| “sslv3 alert handshake failure” | Protocol incompatibility | Update TLS versions |
| “unsafe legacy renegotiation” | Old OpenSSL on client | Update client OpenSSL |
13.10 Best Practices for Mixed Environments
1. Standardize Certificate Issuance
# Certificate Standard (example)
Algorithm: RSA
Key Size: 2048 bits minimum (4096 preferred)
Signature: SHA-256 or better
Validity: 365 days maximum
SANs: Always include
Extensions: Proper key usage set
2. Maintain Consistent Trust Stores
# Deploy script for all systems
for host in rhel7-hosts rhel8-hosts rhel9-hosts; do
ssh "$host" 'sudo cp /path/to/ca.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust'
done
3. Test Before Deploying
# Test matrix
RHEL 7 client → RHEL 7 server ✓
RHEL 7 client → RHEL 8 server ✓
RHEL 7 client → RHEL 9 server ✓
RHEL 8 client → RHEL 7 server ✓
RHEL 8 client → RHEL 9 server ✓
RHEL 9 client → RHEL 8 server ✓
4. Document Your Environment
## Certificate Compatibility Matrix
### Servers:
- App Server 1: RHEL 7.9, TLS 1.0-1.2, RSA 2048
- Database: RHEL 8.10, DEFAULT policy, RSA 2048
- Web Tier: RHEL 9.8, DEFAULT policy, RSA 4096
### Known Limitations:
- RHEL 7 systems require TLS 1.0/1.1 for legacy app X
- Database requires specific cipher: AES256-GCM-SHA384
### Upgrade Plan:
- Q1 2025: Migrate App Server 1 to RHEL 8
- Q2 2025: Update all certificates to RSA 4096
13.11 Key Takeaways
- Mixed environments are normal - Plan for compatibility
- TLS 1.2+ is the minimum for modern systems
- SHA-256+ signatures required for RHEL 8+
- Crypto-policies changed everything (RHEL 8+)
- Test across all versions before deploying
- Document everything - especially exceptions
- Upgrade path is gradual - don’t rush, test thoroughly
Quick Reference
┌──────────────────────────────────────────────────────────────┐
│ CROSS-VERSION COMPATIBILITY CHECKLIST │
├──────────────────────────────────────────────────────────────┤
│ ✅ RSA 2048+ bits │
│ ✅ SHA-256+ signature │
│ ✅ SANs included │
│ ✅ TLS 1.2+ support │
│ ✅ Modern ciphers │
│ ✅ Consistent CA trust │
│ ✅ Tested across all versions │
└──────────────────────────────────────────────────────────────┘
Test command:
openssl s_client -connect server:443 -tls1_2 -servername server
Check policy (RHEL 8+):
update-crypto-policies --show
Chapter Navigation
Chapter 14: Apache httpd on RHEL
Most Common: Apache (httpd) is the most widely deployed web server on RHEL. Master Apache HTTPS configuration across all RHEL versions.
14.1 Apache on RHEL Overview
Package Name: httpd
SSL/TLS Module: mod_ssl
Config Location: /etc/httpd/conf.d/ssl.conf
Certificate Path: /etc/pki/tls/certs/
Key Path: /etc/pki/tls/private/
Version Comparison
| RHEL Version | Apache Version | OpenSSL | Config Approach |
|---|---|---|---|
| RHEL 7 | 2.4.6 | 1.0.2k | Manual SSL configuration |
| RHEL 8 | 2.4.37+ | 1.1.1k | Manual + crypto-policies |
| RHEL 9 | 2.4.53+ | 3.5.5 | Crypto-policies preferred |
| RHEL 10 | 2.4.62+ | 3.5.5 | Crypto-policies optimal |
14.2 Installation
RHEL 7
#============================================#
# INSTALL APACHE WITH SSL (RHEL 7)
#============================================#
sudo yum install httpd mod_ssl -y
sudo systemctl enable httpd
sudo systemctl start httpd
# Open firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
systemctl status httpd
RHEL 8/9/10
#============================================#
# INSTALL APACHE WITH SSL (RHEL 8/9/10)
#============================================#
sudo dnf install httpd mod_ssl -y
sudo systemctl enable httpd
sudo systemctl start httpd
# Open firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
systemctl status httpd
ss -tlnp | grep :443 # Check if listening on 443
14.3 Basic SSL Configuration
Default SSL Configuration
# Main SSL config file
/etc/httpd/conf.d/ssl.conf
# Key directives:
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
Complete Virtual Host Example
#============================================#
# /etc/httpd/conf.d/ssl.conf
# Or /etc/httpd/conf.d/mysite-ssl.conf
#============================================#
<VirtualHost *:443>
ServerName www.example.com
ServerAlias example.com
DocumentRoot /var/www/html
# Enable SSL/TLS
SSLEngine on
# Certificate files
SSLCertificateFile /etc/pki/tls/certs/www.example.com.crt
SSLCertificateKeyFile /etc/pki/tls/private/www.example.com.key
SSLCertificateChainFile /etc/pki/tls/certs/chain.crt
# TLS protocols (RHEL 7 - manual config)
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Cipher suite (RHEL 7 - manual)
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!RC4
SSLHonorCipherOrder on
# HSTS (recommended)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Logging
ErrorLog /var/log/httpd/ssl_error_log
CustomLog /var/log/httpd/ssl_access_log combined
</VirtualHost>
14.4 Version-Specific Configuration
RHEL 7: Manual SSL Configuration
#============================================#
# APACHE SSL - RHEL 7 BEST PRACTICES
#============================================#
<VirtualHost *:443>
ServerName www.example.com
SSLEngine on
# Certificates
SSLCertificateFile /etc/pki/tls/certs/www.crt
SSLCertificateKeyFile /etc/pki/tls/private/www.key
SSLCertificateChainFile /etc/pki/tls/certs/chain.crt
# REQUIRED: Disable old TLS versions manually
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# REQUIRED: Strong ciphers only
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder on
# Security headers
Header always set Strict-Transport-Security "max-age=31536000"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
</VirtualHost>
RHEL 8/9/10: Crypto-Policies Integrated
#============================================#
# APACHE SSL - RHEL 8/9/10 WITH CRYPTO-POLICIES
#============================================#
<VirtualHost *:443>
ServerName www.example.com
SSLEngine on
# Certificates
SSLCertificateFile /etc/pki/tls/certs/www.crt
SSLCertificateKeyFile /etc/pki/tls/private/www.key
SSLCertificateChainFile /etc/pki/tls/certs/chain.crt
# NO NEED to set SSLProtocol or SSLCipherSuite!
# Crypto-policies handles it automatically
# (unless you have specific requirements)
# Security headers (still manual)
Header always set Strict-Transport-Security "max-age=31536000"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
</VirtualHost>
Key Difference: On RHEL 8+, crypto-policies automatically configures TLS versions and ciphers!
Checking Crypto-Policy Integration
#============================================#
# VERIFY CRYPTO-POLICY (RHEL 8/9/10)
#============================================#
# Check current policy
update-crypto-policies --show
# View Apache-specific policy
cat /etc/crypto-policies/back-ends/httpd.config
# Apache automatically includes this
grep -r "crypto-policies" /etc/httpd/
14.5 Certificate Generation for Apache
Complete Workflow
#============================================#
# GENERATE CERTIFICATE FOR APACHE (ALL VERSIONS)
#============================================#
# Step 1: Generate private key
sudo openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/www.example.com.key \
-pkeyopt rsa_keygen_bits:2048
# Step 2: Set permissions
sudo chmod 600 /etc/pki/tls/private/www.example.com.key
sudo chown root:root /etc/pki/tls/private/www.example.com.key
# Step 3: Generate CSR with SANs
sudo openssl req -new \
-key /etc/pki/tls/private/www.example.com.key \
-out /tmp/www.example.com.csr \
-subj "/C=US/ST=State/L=City/O=Company/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:example.com"
# Step 4: Submit CSR to CA, receive certificate
# Step 5: Install certificate
sudo cp www.example.com.crt /etc/pki/tls/certs/
sudo chmod 644 /etc/pki/tls/certs/www.example.com.crt
# Step 6: If using intermediate certificates, install chain
sudo cp chain.crt /etc/pki/tls/certs/www.example.com-chain.crt
# Step 7: Update Apache config (see section 13.3)
# Step 8: Test configuration
sudo apachectl configtest
# Step 9: Reload Apache
sudo systemctl reload httpd
# Step 10: Test HTTPS
curl -v https://www.example.com/
openssl s_client -connect www.example.com:443 -servername www.example.com
14.6 certmonger Integration (Automation!)
Using certmonger with Apache
#============================================#
# AUTOMATE APACHE CERTIFICATES WITH CERTMONGER
#============================================#
# Install certmonger
# RHEL 8/9/10
sudo dnf install certmonger
# RHEL 7
# sudo yum install certmonger
sudo systemctl enable --now certmonger
# FreeIPA / internal CA workflow
sudo ipa-getcert request \
-f /etc/pki/tls/certs/www.example.com.crt \
-k /etc/pki/tls/private/www.example.com.key \
-D www.example.com \
-K host/www.example.com@REALM \
-C "systemctl reload httpd" # Auto-reload Apache after renewal!
# Check status
sudo getcert list
# For public Let's Encrypt certificates, use certbot in section 14.7.
Benefits:
- ✅ Automatic renewal
- ✅ No downtime (reload, not restart)
- ✅ Tracks expiration
- ✅ Email alerts on failure
14.7 Let’s Encrypt with certbot
⚠️ IMPORTANT: EPEL Required
certbot is NOT available in official RHEL repositories. It requires EPEL (Extra Packages for Enterprise Linux), a community-maintained repository.
For production RHEL environments, consider:
- FreeIPA with certmonger (recommended for RHEL)
- Manual certificate management
- Commercial CA with certmonger
#============================================#
# CERTBOT SETUP (REQUIRES EPEL!)
#============================================#
# Step 1: Enable EPEL
# RHEL 7:
# sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y
# RHEL 8:
# sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y
# RHEL 9/10:
sudo dnf install epel-release -y
# Step 2: Install certbot
# RHEL 7:
# sudo yum install certbot python2-certbot-apache -y
# RHEL 8/9/10:
sudo dnf install certbot python3-certbot-apache -y
# Step 3: Obtain certificate (automated!)
sudo certbot --apache -d www.example.com -d example.com
# Step 4: Certbot automatically:
# - Generates certificate
# - Configures Apache
# - Sets up renewal timer
# - Enables HTTPS redirect
# Step 5: Test automatic renewal
sudo certbot renew --dry-run
# Check renewal timer
systemctl list-timers | grep certbot
Pros:
- ✅ Fully automated
- ✅ Free certificates
- ✅ Apache config handled automatically
Cons:
- ❌ Requires EPEL (not officially supported by Red Hat)
- ❌ External dependency (Let’s Encrypt)
- ⚠️ Domain must be publicly accessible
14.8 Troubleshooting Apache HTTPS
Common Issues Checklist
#============================================#
# APACHE SSL TROUBLESHOOTING CHECKLIST
#============================================#
# 1. Check if mod_ssl is loaded
sudo httpd -M | grep ssl_module
# Should show: ssl_module (shared)
# 2. Check configuration syntax
sudo apachectl configtest
# Should show: Syntax OK
# 3. Check certificate files exist
ls -l /etc/pki/tls/certs/www.crt
ls -l /etc/pki/tls/private/www.key
# 4. Verify permissions
ls -l /etc/pki/tls/private/www.key
# Should be: -rw------- (600)
# 5. Check SELinux context
ls -Z /etc/pki/tls/certs/www.crt
ls -Z /etc/pki/tls/private/www.key
# Should show: cert_t
# 6. Test certificate/key pair match
CERT_MOD=$(openssl x509 -noout -modulus -in /etc/pki/tls/certs/www.crt | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in /etc/pki/tls/private/www.key | openssl md5)
[ "$CERT_MOD" = "$KEY_MOD" ] && echo "✅ Match" || echo "❌ No match!"
# 7. Check if port 443 is listening
ss -tlnp | grep :443
# 8. Check firewall
sudo firewall-cmd --list-services | grep https
# 9. Test locally
curl -vk https://localhost/
# 10. Check logs
sudo tail -f /var/log/httpd/ssl_error_log
Common Errors and Solutions
| Error Message | Cause | Solution |
|---|---|---|
| “SSLCertificateFile: file does not exist” | Wrong path | Fix path in ssl.conf |
| “Permission denied” on key file | Wrong permissions | chmod 600 on key |
| “certificate verify failed” | Chain issue | Install intermediate certs |
| “SSLCertificateKeyFile: file does not exist” | Missing key | Generate or restore key |
| “Private key does not match certificate” | Cert/key mismatch | Regenerate CSR with correct key |
| “SSL Library Error” | mod_ssl not loaded | Install mod_ssl package |
| “ca md too weak” (RHEL 9+) | SHA-1 signature | Reissue with SHA-256+ |
| “name mismatch” | Hostname doesn’t match CN/SAN | Fix certificate SANs |
14.9 Version-Specific Troubleshooting
RHEL 7 Specific
#============================================#
# RHEL 7 APACHE ISSUES
#============================================#
# Issue: Modern browsers reject TLS 1.0/1.1
# Solution: Disable old TLS in ssl.conf
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# Issue: Weak ciphers flagged by scan
# Solution: Use strong ciphers
SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5
SSLHonorCipherOrder on
# Issue: No SANs in certificate
# Solution: Reissue with SANs (see 13.5)
# Test
openssl s_client -connect localhost:443 -tls1_2
RHEL 8/9/10 Specific
#============================================#
# RHEL 8/9/10 APACHE ISSUES
#============================================#
# Issue: Service fails after crypto-policy change
# Diagnosis:
update-crypto-policies --show
sudo journalctl -xe -u httpd | grep -i tls
# Solution 1: Verify policy is correct
sudo update-crypto-policies --set DEFAULT
# Solution 2: Check if you manually override policy
grep -E "SSLProtocol|SSLCipherSuite" /etc/httpd/conf.d/*.conf
# If found, remove (let crypto-policy handle it)
# Issue: "no shared cipher" error
# Diagnosis: Client too old or policy too strict
# Temporary solution:
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd
# Proper solution: Update client or create custom policy module
14.10 Security Best Practices
Hardened Apache SSL Configuration
#============================================#
# HARDENED SSL CONFIG (ALL VERSIONS)
#============================================#
<VirtualHost *:443>
ServerName secure.example.com
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/secure.crt
SSLCertificateKeyFile /etc/pki/tls/private/secure.key
# RHEL 7: Manual TLS config
# SSLProtocol TLSv1.2 TLSv1.3
# SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256
# SSLHonorCipherOrder on
# RHEL 8/9/10: Crypto-policies handle above automatically
# HSTS (force HTTPS for 1 year)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking
Header always set X-Frame-Options "DENY"
# Prevent MIME-type sniffing
Header always set X-Content-Type-Options "nosniff"
# Disable server signature
ServerSignature Off
ServerTokens Prod
# OCSP Stapling (RHEL 8/9/10)
SSLUseStapling on
SSLStaplingCache "shmcb:/var/run/ocsp(128000)"
# Client certificate auth (optional)
# SSLVerifyClient require
# SSLVerifyDepth 3
# SSLCACertificateFile /etc/pki/tls/certs/client-ca.crt
</VirtualHost>
# Outside VirtualHost (global SSL settings)
SSLStaplingCache "shmcb:/var/run/ocsp(128000)"
14.11 HTTP to HTTPS Redirect
Force HTTPS
#============================================#
# REDIRECT HTTP → HTTPS
#============================================#
# Method 1: Separate VirtualHost
<VirtualHost *:80>
ServerName www.example.com
Redirect permanent / https://www.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName www.example.com
# ... SSL config ...
</VirtualHost>
# Method 2: mod_rewrite
<VirtualHost *:80>
ServerName www.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
14.12 Testing Apache HTTPS
Comprehensive Testing
#============================================#
# APACHE HTTPS TESTING SUITE
#============================================#
# Test 1: Configuration syntax
sudo apachectl configtest
# Test 2: SSL module loaded
sudo httpd -M | grep ssl
# Test 3: Port listening
ss -tlnp | grep :443
# Test 4: Local connection
curl -vk https://localhost/
# Test 5: Actual hostname
curl -v https://www.example.com/
# Test 6: Certificate validation
openssl s_client -connect www.example.com:443 -servername www.example.com
# Test 7: TLS 1.2
openssl s_client -connect www.example.com:443 -tls1_2
# Test 8: TLS 1.3 (RHEL 8+)
openssl s_client -connect www.example.com:443 -tls1_3
# Test 9: Check certificate details from server
echo | openssl s_client -connect www.example.com:443 -servername www.example.com 2>&1 | \
openssl x509 -noout -text | head -30
# Test 10: Online test (external)
# Use: https://www.ssllabs.com/ssltest/
14.13 Performance Optimization
SSL/TLS Performance Tuning
#============================================#
# PERFORMANCE TUNING
#============================================#
<VirtualHost *:443>
# ... basic config ...
# Session cache (improves performance)
SSLSessionCache "shmcb:/var/cache/httpd/ssl_scache(512000)"
SSLSessionCacheTimeout 300
# OCSP Stapling (reduces client-side lookup)
SSLUseStapling on
SSLStaplingCache "shmcb:/var/run/ocsp(128000)"
# Keep-Alive (reuse connections)
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
# HTTP/2 (RHEL 8/9/10)
Protocols h2 h2c http/1.1
</VirtualHost>
14.14 Monitoring Apache HTTPS
What to Monitor
#============================================#
# APACHE HTTPS MONITORING
#============================================#
# Certificate expiration
openssl s_client -connect localhost:443 -servername $(hostname -f) 2>/dev/null | \
openssl x509 -noout -dates
# Service status
systemctl status httpd
# Connection count
ss -tn | grep :443 | wc -l
# Error log monitoring
sudo tail -f /var/log/httpd/ssl_error_log
# certmonger status (if used)
sudo getcert list -f /etc/pki/tls/certs/www.crt
# Access log analysis
sudo tail -f /var/log/httpd/ssl_access_log | grep -E "HTTP/[12]"
14.15 Quick Troubleshooting Guide
Apache HTTPS Not Working?
├─ Apache won't start?
│ ├─ Check: apachectl configtest
│ ├─ Check: journalctl -xe -u httpd
│ └─ Fix: Configuration errors
│
├─ Can't connect to port 443?
│ ├─ Check: ss -tlnp | grep :443
│ ├─ Check: firewall-cmd --list-services
│ └─ Fix: Open firewall, start httpd
│
├─ Certificate warnings in browser?
│ ├─ Check: Certificate expiration
│ ├─ Check: Hostname match (CN/SANs)
│ ├─ Check: Trust chain
│ └─ Fix: Renew cert, fix SANs, install CA
│
├─ "No shared cipher" error?
│ ├─ Check: update-crypto-policies --show
│ ├─ Check: Client TLS version
│ └─ Fix: Update policy or client
│
└─ Permission errors?
├─ Check: ls -lZ /etc/pki/tls/private/*.key
├─ Check: SELinux denials
└─ Fix: chmod 600, restorecon
14.16 Key Takeaways
- Apache + mod_ssl is the standard RHEL web server
- RHEL 7: Manual TLS configuration required
- RHEL 8/9/10: Crypto-policies simplify configuration
- certmonger integration enables automation
- certbot requires EPEL (not officially supported)
- Always use SANs in certificates
- Test thoroughly before production deployment
Quick Reference Card
┌───────────────────────────────────────────────────────────────────┐
│ APACHE HTTPD HTTPS QUICK REFERENCE │
├───────────────────────────────────────────────────────────────────┤
│ Install: dnf install httpd mod_ssl │
│ Config: /etc/httpd/conf.d/ssl.conf │
│ Certs: /etc/pki/tls/certs/ │
│ Keys: /etc/pki/tls/private/ (mode 600!) │
│ │
│ Test config: apachectl configtest │
│ Reload: systemctl reload httpd │
│ Logs: /var/log/httpd/ssl_error_log │
│ │
│ certmonger: ipa-getcert request ... -C "systemctl reload httpd" │
│ certbot: certbot --apache (requires EPEL!) │
│ │
│ Test: curl -v https://localhost/ │
│ openssl s_client -connect host:443 │
└───────────────────────────────────────────────────────────────────┘
⚠️ RHEL 8/9/10: Let crypto-policies handle TLS/cipher config
⚠️ certbot requires EPEL (not officially supported)
🧪 Hands-On Lab
Lab 06: Apache HTTPS Setup
Configure Apache with SSL/TLS across RHEL versions
- 📁 Location:
labs/en_US/06-apache-https/ - ⏱️ Time: 30-40 minutes
- 🎯 Level: Intermediate
Chapter Navigation
Chapter 15: NGINX on RHEL
High Performance: NGINX is a popular high-performance web server and reverse proxy. Learn how to configure NGINX with TLS certificates on RHEL.
15.1 NGINX on RHEL Overview
Package Name: nginx
Config Location: /etc/nginx/nginx.conf
Certificate Path: /etc/pki/tls/certs/ or /etc/nginx/certs/
Key Path: /etc/pki/tls/private/ or /etc/nginx/certs/
Installation Sources by RHEL Version
| RHEL Version | NGINX Source | How to Install |
|---|---|---|
| RHEL 7 | EPEL (community) | Enable EPEL, then yum install nginx |
| RHEL 8 | AppStream (official) | dnf module install nginx:1.20 |
| RHEL 9 | AppStream (official) | dnf install nginx |
| RHEL 10 | AppStream (official) | dnf install nginx |
Note: RHEL 7 requires EPEL for NGINX. RHEL 8+ includes NGINX in official repos.
15.2 Installation
RHEL 7
#============================================#
# INSTALL NGINX (RHEL 7 - REQUIRES EPEL)
#============================================#
# Step 1: Enable EPEL
sudo yum install epel-release -y
# Step 2: Install NGINX
sudo yum install nginx -y
# Step 3: Enable and start
sudo systemctl enable nginx
sudo systemctl start nginx
# Step 4: Open firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
systemctl status nginx
curl http://localhost/
RHEL 8
#============================================#
# INSTALL NGINX (RHEL 8 - FROM APPSTREAM)
#============================================#
# List available NGINX modules
dnf module list nginx
# Install specific version
sudo dnf module install nginx:1.20 -y
# Or install default
sudo dnf install nginx -y
# Enable and start
sudo systemctl enable nginx
sudo systemctl start nginx
# Open firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
RHEL 9/10
#============================================#
# INSTALL NGINX (RHEL 9/10)
#============================================#
sudo dnf install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
# Open firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
systemctl status nginx
ss -tlnp | grep :443
15.3 Basic HTTPS Configuration
Minimal TLS Setup
#============================================#
# /etc/nginx/nginx.conf or /etc/nginx/conf.d/default.conf
#============================================#
server {
listen 80;
server_name www.example.com;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
# Certificate files
ssl_certificate /etc/pki/tls/certs/www.example.com.crt;
ssl_certificate_key /etc/pki/tls/private/www.example.com.key;
# TLS protocols
ssl_protocols TLSv1.2 TLSv1.3;
# Ciphers
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Root directory
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Hardened Production Configuration
#============================================#
# PRODUCTION-GRADE NGINX HTTPS CONFIG
#============================================#
server {
listen 443 ssl http2;
server_name api.example.com;
# Certificates
ssl_certificate /etc/pki/tls/certs/api.example.com.crt;
ssl_certificate_key /etc/pki/tls/private/api.example.com.key;
# TLS versions
ssl_protocols TLSv1.2 TLSv1.3;
# Strong ciphers
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
# DH parameters (optional, for perfect forward secrecy)
ssl_dhparam /etc/nginx/dhparam.pem;
# SSL session optimization
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/pki/tls/certs/chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Security headers
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
# Logging
access_log /var/log/nginx/api_access.log;
error_log /var/log/nginx/api_error.log;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
15.4 Certificate Setup
Generate Certificates for NGINX
#============================================#
# CERTIFICATE GENERATION FOR NGINX
#============================================#
# Step 1: Create certs directory (optional)
sudo mkdir -p /etc/nginx/certs
sudo chmod 755 /etc/nginx/certs
# Step 2: Generate private key
sudo openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/api.example.com.key \
-pkeyopt rsa_keygen_bits:2048
# Step 3: Set permissions
sudo chmod 600 /etc/pki/tls/private/api.example.com.key
sudo chown root:nginx /etc/pki/tls/private/api.example.com.key
# Step 4: Generate CSR
sudo openssl req -new \
-key /etc/pki/tls/private/api.example.com.key \
-out /tmp/api.example.com.csr \
-subj "/CN=api.example.com" \
-addext "subjectAltName=DNS:api.example.com,DNS:www.api.example.com"
# Step 5: Submit to CA, receive certificate
# Step 6: Install certificate
sudo cp api.example.com.crt /etc/pki/tls/certs/
sudo chmod 644 /etc/pki/tls/certs/api.example.com.crt
15.5 certmonger Integration
Automated Renewal with certmonger
#============================================#
# CERTMONGER + NGINX
#============================================#
# Install certmonger
# RHEL 8/9/10
sudo dnf install certmonger
# RHEL 7
# sudo yum install certmonger
sudo systemctl enable --now certmonger
# Request certificate from FreeIPA / internal CA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/nginx.example.com.crt \
-k /etc/pki/tls/private/nginx.example.com.key \
-D nginx.example.com \
-K host/nginx.example.com@REALM \
-C "systemctl reload nginx" # Auto-reload on renewal!
# Monitor status
sudo getcert list
# For public Let's Encrypt certificates, use certbot in section 15.6.
15.6 Let’s Encrypt with certbot
⚠️ IMPORTANT: EPEL Required
certbot is NOT available in official RHEL repositories. It requires EPEL, a community-maintained repository.
Installation: All RHEL versions require EPEL, but the enablement command differs by version. See Chapter 24 for the full certbot workflow.
RHEL 7
# Step 1: Enable EPEL
sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y
# Step 2: Install certbot with NGINX plugin
sudo yum install certbot python2-certbot-nginx -y
RHEL 8
# Step 1: Enable EPEL
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y
# Or, with an active subscription:
# sudo dnf install epel-release -y
# Step 2: Install certbot with NGINX plugin
sudo dnf install certbot python3-certbot-nginx -y
RHEL 9/10
# Step 1: Enable EPEL
sudo dnf install epel-release -y
# Step 2: Install certbot with NGINX plugin
sudo dnf install certbot python3-certbot-nginx -y
Obtain and configure the certificate (all versions)
# Step 3: Obtain and install certificate (automated!)
sudo certbot --nginx -d www.example.com -d example.com
# Certbot will:
# ✅ Generate certificate from Let's Encrypt
# ✅ Update NGINX configuration automatically
# ✅ Set up HTTP to HTTPS redirect
# ✅ Configure auto-renewal
# Step 4: Verify auto-renewal timer
systemctl list-timers | grep certbot
# Step 5: Test renewal (dry run)
sudo certbot renew --dry-run
# Certificate renews automatically every 60 days!
Remember: EPEL is community-supported, not Red Hat supported. For enterprise production, consider FreeIPA + certmonger.
15.7 Reverse Proxy with TLS
NGINX as TLS Termination Proxy
#============================================#
# NGINX REVERSE PROXY WITH TLS
#============================================#
upstream backend_servers {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 443 ssl http2;
server_name proxy.example.com;
# TLS termination here
ssl_certificate /etc/pki/tls/certs/proxy.crt;
ssl_certificate_key /etc/pki/tls/private/proxy.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Proxy to backends (HTTP)
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
15.8 Troubleshooting NGINX HTTPS
Diagnostic Commands
#============================================#
# NGINX HTTPS TROUBLESHOOTING
#============================================#
# Test configuration syntax
sudo nginx -t
# Show full configuration (with includes)
sudo nginx -T
# Check SSL certificate paths
sudo nginx -T | grep ssl_certificate
# Verify certificate file
sudo openssl x509 -in /etc/pki/tls/certs/nginx.crt -noout -text
# Verify key file
sudo openssl rsa -in /etc/pki/tls/private/nginx.key -check
# Check cert/key pair match
CERT=$(openssl x509 -noout -modulus -in /etc/pki/tls/certs/nginx.crt | openssl md5)
KEY=$(openssl rsa -noout -modulus -in /etc/pki/tls/private/nginx.key | openssl md5)
[ "$CERT" = "$KEY" ] && echo "✅ Match" || echo "❌ Mismatch!"
# Check if NGINX is listening on 443
ss -tlnp | grep :443
# Check SELinux context
ls -Z /etc/pki/tls/certs/nginx.crt
ls -Z /etc/pki/tls/private/nginx.key
# Test HTTPS locally
curl -vk https://localhost/
# Check logs
sudo tail -f /var/log/nginx/error.log
Common NGINX HTTPS Errors
| Error | Cause | Solution |
|---|---|---|
| “SSL: error:0200100D…” | Permission denied on key | chmod 600 on key file |
| “no ssl configured for the server” | Missing ssl on listen | Add listen 443 ssl; |
| “cannot load certificate” | File not found or invalid | Check path and cert format |
| “PEM_read_bio:no start line” | Wrong format | Ensure cert is PEM format |
| “key values mismatch” | Cert/key don’t match | Regenerate with correct key |
| “nginx: [emerg] bind() failed” | Port already in use | Check ss -tlnp | grep :443 |
15.9 Version-Specific Configuration
RHEL 7: Manual TLS Configuration
#============================================#
# NGINX RHEL 7 - MANUAL SSL CONFIG
#============================================#
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/pki/tls/certs/www.crt;
ssl_certificate_key /etc/pki/tls/private/www.key;
# REQUIRED: Manually disable weak TLS
ssl_protocols TLSv1.2; # No TLS 1.0/1.1!
# REQUIRED: Manually set strong ciphers
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5';
ssl_prefer_server_ciphers on;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
root /usr/share/nginx/html;
}
RHEL 8/9/10: Crypto-Policies Aware
#============================================#
# NGINX RHEL 8/9/10 - WITH CRYPTO-POLICIES
#============================================#
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/pki/tls/certs/www.crt;
ssl_certificate_key /etc/pki/tls/private/www.key;
# Minimal TLS config - crypto-policies handle the rest!
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Or completely rely on crypto-policies:
# (remove ssl_protocols and ssl_ciphers)
# NGINX will use system crypto-policy
# Session optimization
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/pki/tls/certs/chain.crt;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
root /usr/share/nginx/html;
}
15.10 Performance Optimization
SSL/TLS Performance Tuning
#============================================#
# NGINX SSL PERFORMANCE OPTIMIZATION
#============================================#
http {
# SSL session cache (reduces handshake overhead)
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Buffer sizes
ssl_buffer_size 4k; # Smaller = lower latency, larger = better throughput
server {
listen 443 ssl http2;
server_name fast.example.com;
ssl_certificate /etc/pki/tls/certs/fast.crt;
ssl_certificate_key /etc/pki/tls/private/fast.key;
# Use HTTP/2 for multiplexing
# (already enabled in listen directive)
# Enable OCSP Stapling (reduces client lookup time)
ssl_stapling on;
ssl_stapling_verify on;
# Keep-alive
keepalive_timeout 70;
keepalive_requests 100;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
15.11 Multiple Certificates (SNI)
Server Name Indication (SNI)
#============================================#
# MULTIPLE DOMAINS WITH DIFFERENT CERTIFICATES
#============================================#
# Site 1
server {
listen 443 ssl http2;
server_name site1.example.com;
ssl_certificate /etc/pki/tls/certs/site1.crt;
ssl_certificate_key /etc/pki/tls/private/site1.key;
root /var/www/site1;
}
# Site 2
server {
listen 443 ssl http2;
server_name site2.example.com;
ssl_certificate /etc/pki/tls/certs/site2.crt;
ssl_certificate_key /etc/pki/tls/private/site2.key;
root /var/www/site2;
}
# Site 3 (wildcard)
server {
listen 443 ssl http2;
server_name *.apps.example.com;
ssl_certificate /etc/pki/tls/certs/wildcard.apps.crt;
ssl_certificate_key /etc/pki/tls/private/wildcard.apps.key;
root /var/www/apps;
}
15.12 Client Certificate Authentication
Mutual TLS (mTLS) with NGINX
#============================================#
# NGINX WITH CLIENT CERTIFICATE AUTH
#============================================#
server {
listen 443 ssl http2;
server_name secure.example.com;
# Server certificates
ssl_certificate /etc/pki/tls/certs/secure.crt;
ssl_certificate_key /etc/pki/tls/private/secure.key;
# Client certificate verification
ssl_client_certificate /etc/pki/tls/certs/client-ca.crt;
ssl_verify_client on; # or 'optional'
ssl_verify_depth 3;
# Pass client cert info to backend
location / {
proxy_pass http://backend;
proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
}
}
15.13 Testing NGINX HTTPS
Comprehensive Test Suite
#============================================#
# NGINX HTTPS TESTING
#============================================#
# Test 1: Configuration syntax
sudo nginx -t
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# Test 2: Show effective configuration
sudo nginx -T | grep -A10 "server_name www.example.com"
# Test 3: Port listening
ss -tlnp | grep nginx
# Test 4: Local HTTPS
curl -vk https://localhost/
# Test 5: Hostname-based
curl -v https://www.example.com/
# Test 6: Check certificate from server
echo | openssl s_client -connect www.example.com:443 -servername www.example.com 2>&1 | \
openssl x509 -noout -subject -dates
# Test 7: TLS 1.2
openssl s_client -connect www.example.com:443 -tls1_2
# Test 8: TLS 1.3 (RHEL 8+)
openssl s_client -connect www.example.com:443 -tls1_3
# Test 9: HTTP/2 support
curl -I --http2 https://www.example.com/
# Test 10: Security headers
curl -I https://www.example.com/ | grep -i "strict-transport"
15.14 Common Issues and Solutions
Issue 1: “Permission denied” on Private Key
# Symptom
sudo nginx -t
# nginx: [emerg] SSL_CTX_use_PrivateKey_file() failed (SSL: error:0200100D:system library:fopen:Permission denied)
# Check permissions
ls -l /etc/pki/tls/private/nginx.key
# Fix
sudo chmod 600 /etc/pki/tls/private/nginx.key
sudo chown root:nginx /etc/pki/tls/private/nginx.key
# If SELinux issue:
sudo restorecon -v /etc/pki/tls/private/nginx.key
Issue 2: Certificate Chain Not Sent
# Test from client
openssl s_client -connect www.example.com:443 -showcerts
# If shows only server cert (not intermediates):
# Create certificate bundle
cat server.crt intermediate.crt > /etc/pki/tls/certs/bundle.crt
# Update NGINX config
ssl_certificate /etc/pki/tls/certs/bundle.crt;
# Reload
sudo systemctl reload nginx
Issue 3: OCSP Stapling Not Working
# Test OCSP stapling
openssl s_client -connect www.example.com:443 -status -tlsextdebug 2>&1 | grep -A17 "OCSP"
# Common causes:
# 1. No resolver configured
# Fix: Add to nginx.conf:
resolver 8.8.8.8 8.8.4.4 valid=300s;
# 2. Missing trusted certificate chain
# Fix:
ssl_trusted_certificate /etc/pki/tls/certs/chain.crt;
# 3. Firewall blocks OCSP requests
# Fix: Allow outbound HTTPS
15.15 Security Best Practices
Checklist
✅ Use TLS 1.2+ only (disable 1.0/1.1)
✅ Strong ciphers with forward secrecy (ECDHE)
✅ Enable HSTS with long max-age
✅ Enable OCSP Stapling
✅ Use HTTP/2
✅ Proper file permissions (600 for keys)
✅ SELinux contexts correct
✅ Certificate validity ≤ 90 days with auto-renewal
✅ Include comprehensive SANs
✅ Security headers enabled
15.16 Key Takeaways
- NGINX available in AppStream (RHEL 8+) or EPEL (RHEL 7)
- Crypto-policies simplify config on RHEL 8/9/10
- certmonger integrates well with automatic reload
- certbot requires EPEL on all RHEL versions
- SNI enables multiple certs on same IP
- mTLS possible for client authentication
- Test thoroughly - syntax, connectivity, security
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ NGINX HTTPS QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Install: dnf install nginx (RHEL 8/9/10) │
│ yum install epel-release nginx (RHEL 7) │
│ │
│ Config: /etc/nginx/nginx.conf │
│ /etc/nginx/conf.d/*.conf │
│ │
│ Basic SSL: listen 443 ssl http2; │
│ ssl_certificate /path/to/cert.crt; │
│ ssl_certificate_key /path/to/key.key; │
│ │
│ Test: nginx -t │
│ Reload: systemctl reload nginx │
│ Logs: /var/log/nginx/error.log │
│ │
│ certbot: certbot --nginx (requires EPEL!) │
│ certmonger: ipa-getcert ... -C "systemctl reload nginx" │
└──────────────────────────────────────────────────────────────┘
⚠️ certbot requires EPEL on all RHEL versions
✅ Use certmonger for enterprise environments
🧪 Hands-On Lab
Lab 07: NGINX HTTPS Setup
Configure NGINX with SSL/TLS and security best practices
- 📁 Location:
labs/en_US/07-nginx-https/ - ⏱️ Time: 30-35 minutes
- 🎯 Level: Intermediate
Chapter Navigation
Chapter 16: Postfix Mail Server TLS
Email Security: Learn how to configure TLS encryption for Postfix mail servers on RHEL, protecting email communications with certificates.
16.1 Postfix on RHEL Overview
Package Name: postfix
Config Location: /etc/postfix/main.cf
Certificate Path: /etc/pki/tls/certs/
Key Path: /etc/pki/tls/private/
Ports: 25 (SMTP), 465 (SMTPS), 587 (Submission with STARTTLS)
Why TLS for Email?
- ✅ Encrypt email in transit (prevent eavesdropping)
- ✅ Authenticate mail servers (prevent impersonation)
- ✅ Meet compliance requirements (HIPAA, PCI-DSS)
- ✅ Prevent spam/phishing (SPF, DKIM, DMARC work better with TLS)
16.2 Installation
All RHEL Versions
#============================================#
# INSTALL POSTFIX
#============================================#
# Install Postfix
sudo dnf install postfix -y
# Stop and disable Sendmail (if present)
sudo systemctl stop sendmail 2>/dev/null
sudo systemctl disable sendmail 2>/dev/null
# Enable Postfix
sudo systemctl enable postfix
sudo systemctl start postfix
# Open firewall
sudo firewall-cmd --permanent --add-service=smtp
sudo firewall-cmd --permanent --add-service=smtps
sudo firewall-cmd --permanent --add-service=smtp-submission
sudo firewall-cmd --reload
# Verify
systemctl status postfix
ss -tlnp | grep -E ':(25|465|587)'
16.3 Server-Side TLS Configuration
Receiving Mail with TLS (SMTPD)
#============================================#
# /etc/postfix/main.cf - SERVER (RECEIVING) TLS
#============================================#
# Certificate files
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.example.com.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.example.com.key
smtpd_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
# TLS security level
smtpd_tls_security_level = may # or 'encrypt' to require TLS
# Logging
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
# Session cache (performance)
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
# Authentication - only over TLS
smtpd_tls_auth_only = yes
# RHEL 7: Specify protocols manually
# smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# RHEL 8/9/10: Crypto-policies handle protocols automatically
# (no need to specify smtpd_tls_protocols)
Security Levels Explained:
| Level | Behavior | Use Case |
|---|---|---|
none | TLS disabled | Not recommended |
may | TLS optional (opportunistic) | Standard (compatible) |
encrypt | TLS required | High security environments |
dane | DNSSEC-based validation | Advanced setups |
16.4 Client-Side TLS Configuration
Sending Mail with TLS (SMTP)
#============================================#
# /etc/postfix/main.cf - CLIENT (SENDING) TLS
#============================================#
# Certificate files (optional for client)
smtp_tls_cert_file = /etc/pki/tls/certs/mail.example.com.crt
smtp_tls_key_file = /etc/pki/tls/private/mail.example.com.key
smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
# TLS security level for outbound
smtp_tls_security_level = may # or 'encrypt' to require
# Logging
smtp_tls_loglevel = 1
# Session cache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# RHEL 7: Specify protocols
# smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# RHEL 8/9/10: Crypto-policies handle it
16.5 Complete Postfix TLS Setup
Full Configuration Example
#============================================#
# COMPLETE POSTFIX TLS CONFIGURATION
#============================================#
# 1. Generate certificate and key
sudo openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/mail.example.com.key \
-pkeyopt rsa_keygen_bits:2048
sudo chmod 600 /etc/pki/tls/private/mail.example.com.key
# 2. Generate CSR
sudo openssl req -new \
-key /etc/pki/tls/private/mail.example.com.key \
-out /tmp/mail.example.com.csr \
-subj "/CN=mail.example.com" \
-addext "subjectAltName=DNS:mail.example.com,DNS:smtp.example.com"
# 3. Get certificate from CA, install it
sudo cp mail.example.com.crt /etc/pki/tls/certs/
sudo chmod 644 /etc/pki/tls/certs/mail.example.com.crt
# 4. Edit /etc/postfix/main.cf
sudo vi /etc/postfix/main.cf
# Add TLS configuration (see sections 16.3 and 16.4)
# 5. Test configuration
sudo postfix check
# 6. Reload Postfix
sudo systemctl reload postfix
# 7. Test SMTP TLS
openssl s_client -starttls smtp -connect mail.example.com:25
16.6 SMTPS (Port 465) Configuration
Enable SMTPS Service
#============================================#
# /etc/postfix/master.cf - ENABLE SMTPS
#============================================#
# Uncomment or add SMTPS service (port 465)
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
# Reload
sudo systemctl reload postfix
# Verify
ss -tlnp | grep :465
Test SMTPS
# Connect to SMTPS (immediate TLS, no STARTTLS)
openssl s_client -connect mail.example.com:465
# Should show TLS handshake immediately
16.7 Submission Port (587) with STARTTLS
Enable Submission Service
#============================================#
# /etc/postfix/master.cf - ENABLE SUBMISSION
#============================================#
# Uncomment or add submission service (port 587)
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
# Reload
sudo systemctl reload postfix
# Verify
ss -tlnp | grep :587
Test Submission Port
# Test STARTTLS on port 587
openssl s_client -starttls smtp -connect mail.example.com:587
# Should show:
# STARTTLS
# 220 2.0.0 Ready to start TLS
16.8 certmonger Integration
Automated Certificate Management for Postfix
#============================================#
# CERTMONGER + POSTFIX
#============================================#
# Install certmonger
# RHEL 8/9/10
sudo dnf install certmonger
# RHEL 7
# sudo yum install certmonger
sudo systemctl enable --now certmonger
# Request certificate from FreeIPA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/mail.example.com.crt \
-k /etc/pki/tls/private/mail.example.com.key \
-D mail.example.com \
-K host/mail.example.com@REALM \
-C "postfix reload" # Auto-reload Postfix after renewal
# Check status
sudo getcert list
# Postfix automatically uses renewed certificate!
16.9 Client Certificate Authentication
Require Client Certificates (mTLS for SMTP)
#============================================#
# /etc/postfix/main.cf - CLIENT CERT AUTH
#============================================#
# Server certificates (as before)
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.key
# Client certificate verification
smtpd_tls_CAfile = /etc/pki/tls/certs/client-ca.crt
smtpd_tls_ask_ccert = yes
smtpd_tls_req_ccert = yes # Require client cert
# Map certificate to user
smtpd_recipient_restrictions =
permit_tls_clientcerts,
reject
# Reload
sudo postfix reload
Test with client certificate:
openssl s_client -starttls smtp \
-connect mail.example.com:25 \
-cert client.crt \
-key client.key \
-CAfile ca.crt
16.10 Troubleshooting Postfix TLS
Diagnostic Commands
#============================================#
# POSTFIX TLS TROUBLESHOOTING
#============================================#
# Check Postfix configuration
sudo postconf | grep -i tls
# Test configuration syntax
sudo postfix check
# View specific TLS settings
sudo postconf smtpd_tls_cert_file smtpd_tls_key_file
# Check certificate files exist
ls -l /etc/pki/tls/certs/mail.crt
ls -l /etc/pki/tls/private/mail.key
# Verify cert/key pair match
CERT_MOD=$(openssl x509 -noout -modulus -in /etc/pki/tls/certs/mail.crt | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in /etc/pki/tls/private/mail.key | openssl md5)
[ "$CERT_MOD" = "$KEY_MOD" ] && echo "✅ Match" || echo "❌ Mismatch!"
# Test SMTP STARTTLS
openssl s_client -starttls smtp -connect mail.example.com:25
# Test SMTPS (port 465)
openssl s_client -connect mail.example.com:465
# Check logs
sudo tail -f /var/log/maillog | grep -i tls
# Check Postfix queue
mailq
# Verbose logging (for troubleshooting)
sudo postconf smtpd_tls_loglevel=2
sudo postfix reload
# Check /var/log/maillog for detailed TLS info
Common Postfix TLS Issues
| Error | Cause | Solution |
|---|---|---|
| “SSL_accept error” | Certificate/key issue | Verify cert/key pair match |
| “No shared cipher” | Cipher incompatibility | Check crypto-policy or client |
| “certificate verify failed” | Trust chain broken | Install intermediate certs |
| “Permission denied” on key | Wrong permissions | chmod 600 on key file |
| “TLS is required but not available” | TLS not enabled | Set smtpd_tls_security_level = may |
| “STARTTLS failed” | Client TLS issue | Check client TLS support |
16.11 Version-Specific Considerations
RHEL 7
#============================================#
# POSTFIX TLS - RHEL 7 SPECIFIC
#============================================#
# /etc/postfix/main.cf
# MUST manually specify TLS protocols
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# MUST manually specify ciphers
smtpd_tls_mandatory_ciphers = high
smtpd_tls_ciphers = high
smtp_tls_mandatory_ciphers = high
smtp_tls_ciphers = high
# Exclude weak ciphers
smtpd_tls_mandatory_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA
RHEL 8/9/10
#============================================#
# POSTFIX TLS - RHEL 8/9/10 WITH CRYPTO-POLICIES
#============================================#
# /etc/postfix/main.cf
# Certificate files
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.key
smtpd_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
# TLS settings (crypto-policies handle protocols/ciphers!)
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_loglevel = 1
# Client TLS
smtp_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtp_tls_key_file = /etc/pki/tls/private/mail.key
smtp_tls_security_level = may
smtp_tls_loglevel = 1
# That's it! Much simpler than RHEL 7
# crypto-policies automatically configure TLS versions and ciphers
Key Difference: RHEL 8+ relies on crypto-policies, making configuration much simpler!
16.12 Testing Postfix TLS
Comprehensive Test Suite
#============================================#
# POSTFIX TLS TESTING
#============================================#
# Test 1: STARTTLS on port 25
openssl s_client -starttls smtp -connect mail.example.com:25
# Look for:
# - "250-STARTTLS" in server capabilities
# - Successful TLS handshake
# - Certificate details
# Test 2: SMTPS on port 465 (direct TLS)
openssl s_client -connect mail.example.com:465
# Test 3: Submission port 587 with STARTTLS
openssl s_client -starttls smtp -connect mail.example.com:587
# Test 4: Verify certificate from server
echo "QUIT" | openssl s_client -starttls smtp -connect mail.example.com:25 2>&1 | \
openssl x509 -noout -subject -dates
# Test 5: Send test email
echo "Test email" | mail -s "TLS Test" test@example.com
# Test 6: Check TLS in logs
sudo tail -f /var/log/maillog | grep TLS
# Test 7: Check if TLS is being used
sudo postconf | grep -i tls | grep -i level
16.13 Security Best Practices
Hardened Postfix TLS Configuration
#============================================#
# HARDENED POSTFIX TLS CONFIG
#============================================#
# /etc/postfix/main.cf
# Server TLS (receiving mail)
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.key
smtpd_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
# REQUIRE TLS for authentication
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
# Client TLS (sending mail)
smtp_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtp_tls_key_file = /etc/pki/tls/private/mail.key
smtp_tls_security_level = may
# RHEL 7: Manually disable weak protocols
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# Mandatory for authenticated connections
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# Cipher settings (RHEL 7)
smtpd_tls_mandatory_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA
# Session caching
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Enhanced logging (temporary for troubleshooting)
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1
# Performance
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
16.14 Dovecot IMAP/POP3 with TLS
Dovecot Configuration
While this chapter focuses on Postfix (SMTP), Dovecot often runs alongside for IMAP/POP3:
#============================================#
# DOVECOT TLS (/etc/dovecot/conf.d/10-ssl.conf)
#============================================#
# Enable SSL
ssl = required
# Certificate files
ssl_cert = </etc/pki/tls/certs/mail.example.com.crt
ssl_key = </etc/pki/tls/private/mail.example.com.key
# CA for client cert verification (optional)
#ssl_ca = </etc/pki/tls/certs/ca-bundle.crt
# RHEL 8/9/10: crypto-policies handle it
# TLS protocols (RHEL 7)
#ssl_min_protocol = TLSv1.2
# Cipher suite preferences
#ssl_cipher_list = HIGH:!aNULL:!MD5
# Reload
sudo systemctl reload dovecot
Test IMAPS:
openssl s_client -connect mail.example.com:993
# Test POP3S
openssl s_client -connect mail.example.com:995
16.15 Common Scenarios
Scenario 1: Opportunistic TLS (Standard)
Goal: Accept both encrypted and unencrypted connections
# /etc/postfix/main.cf
smtpd_tls_security_level = may # Optional TLS
smtp_tls_security_level = may # Optional TLS
# Why: Maximum compatibility
# Drawback: Some connections may not be encrypted
Scenario 2: Mandatory TLS (High Security)
Goal: Reject unencrypted connections
# /etc/postfix/main.cf
smtpd_tls_security_level = encrypt # REQUIRE TLS
smtp_tls_security_level = encrypt # REQUIRE TLS
# Why: Maximum security
# Drawback: Old systems may fail to connect
Scenario 3: Client Certificate for Relay
Goal: Allow relay only with valid client certificate
# /etc/postfix/main.cf
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.key
smtpd_tls_CAfile = /etc/pki/tls/certs/client-ca.crt
smtpd_tls_ask_ccert = yes
smtpd_tls_req_ccert = yes
smtpd_recipient_restrictions =
permit_tls_clientcerts,
reject_unauth_destination
# Only clients with valid cert from client-ca.crt can relay
16.16 Monitoring Postfix TLS
What to Monitor
#============================================#
# POSTFIX TLS MONITORING
#============================================#
# Check TLS usage in logs
sudo grep "TLS connection established" /var/log/maillog | tail -20
# Count TLS vs non-TLS connections
sudo grep "connect from" /var/log/maillog | grep -c "TLS"
# Check for TLS errors
sudo grep "TLS" /var/log/maillog | grep -i error
# Monitor certificate expiration
openssl x509 -in /etc/pki/tls/certs/mail.crt -noout -checkend $((86400*30))
# certmonger status (if used)
sudo getcert list -f /etc/pki/tls/certs/mail.crt
# Check queue for TLS failures
mailq
Monitoring Script
#!/bin/bash
# monitor-postfix-tls.sh
echo "=== Postfix TLS Monitoring ==="
# Certificate expiration
echo "1. Certificate Expiration:"
openssl x509 -in /etc/pki/tls/certs/mail.crt -noout -enddate
# TLS connections today
echo ""
echo "2. TLS Connections Today:"
sudo grep "$(date '+%b %e')" /var/log/maillog | grep "TLS connection" | wc -l
# TLS errors today
echo ""
echo "3. TLS Errors Today:"
sudo grep "$(date '+%b %e')" /var/log/maillog | grep -i "tls.*error" | tail -5
# Service status
echo ""
echo "4. Postfix Status:"
systemctl is-active postfix
# Listening ports
echo ""
echo "5. Listening on:"
ss -tlnp | grep master | grep -E ':(25|465|587)'
16.17 Common Issues and Solutions
Issue 1: Certificate Not Trusted by Clients
Symptom: Clients get “untrusted certificate” warnings
Diagnosis:
# Test certificate chain
openssl s_client -starttls smtp -connect mail.example.com:25 -showcerts
# Check if intermediate certificates included
sudo postconf smtpd_tls_cert_file
# Should include full chain or use smtpd_tls_chain_files
Solution:
# Option 1: Include chain in certificate file
cat server.crt intermediate.crt > /etc/pki/tls/certs/mail-chain.crt
# Update config
sudo postconf -e 'smtpd_tls_cert_file = /etc/pki/tls/certs/mail-chain.crt'
sudo postfix reload
# Option 2: Use separate chain file (Postfix 3.4+)
sudo postconf -e 'smtpd_tls_chain_files = /etc/pki/tls/certs/chain.crt'
Issue 2: “Permission denied” on Private Key
Symptom: Postfix won’t start, logs show permission error
Fix:
# Set correct permissions
sudo chmod 600 /etc/pki/tls/private/mail.key
sudo chown root:postfix /etc/pki/tls/private/mail.key
# Fix SELinux context
sudo restorecon -v /etc/pki/tls/private/mail.key
# Restart Postfix
sudo systemctl restart postfix
Issue 3: TLS Not Offered to Clients
Symptom: STARTTLS not shown in EHLO response
Diagnosis:
# Telnet to server
telnet mail.example.com 25
# Type: EHLO test
# Should show: 250-STARTTLS
# If not shown, check config
sudo postconf smtpd_tls_security_level
# Should be 'may' or 'encrypt', not 'none'
Solution:
sudo postconf -e 'smtpd_tls_security_level = may'
sudo postfix reload
16.18 Version Comparison Table
| Feature | RHEL 7 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|---|
| Postfix Version | 2.10.x | 3.3.x+ | 3.5.x+ | 3.8.x+ |
| OpenSSL | 1.0.2k | 1.1.1k | 3.5.5 | 3.5.5 |
| TLS Config | Manual | Crypto-policies | Crypto-policies | Crypto-policies |
| Default TLS | May include 1.0/1.1 | TLS 1.2+ | TLS 1.2+ | TLS 1.3 preferred |
| certmonger | Basic | Enhanced | Enhanced | Enhanced |
16.19 Quick Setup Script
#!/bin/bash
# setup-postfix-tls.sh - Complete Postfix TLS setup
DOMAIN="example.com"
HOSTNAME="mail.$DOMAIN"
echo "=== Postfix TLS Setup for $HOSTNAME ==="
# 1. Install Postfix
sudo dnf install -y postfix
# 2. Generate certificate (or use certmonger)
sudo openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/$HOSTNAME.key \
-pkeyopt rsa_keygen_bits:2048
sudo chmod 600 /etc/pki/tls/private/$HOSTNAME.key
# 3. Generate self-signed for testing (replace with proper cert!)
sudo openssl req -new -x509 -days 365 \
-key /etc/pki/tls/private/$HOSTNAME.key \
-out /etc/pki/tls/certs/$HOSTNAME.crt \
-subj "/CN=$HOSTNAME" \
-addext "subjectAltName=DNS:$HOSTNAME,DNS:smtp.$DOMAIN"
# 4. Configure Postfix
sudo postconf -e "smtpd_tls_cert_file = /etc/pki/tls/certs/$HOSTNAME.crt"
sudo postconf -e "smtpd_tls_key_file = /etc/pki/tls/private/$HOSTNAME.key"
sudo postconf -e "smtpd_tls_security_level = may"
sudo postconf -e "smtpd_tls_auth_only = yes"
sudo postconf -e "smtpd_tls_loglevel = 1"
sudo postconf -e "smtp_tls_cert_file = /etc/pki/tls/certs/$HOSTNAME.crt"
sudo postconf -e "smtp_tls_key_file = /etc/pki/tls/private/$HOSTNAME.key"
sudo postconf -e "smtp_tls_security_level = may"
# 5. Open firewall
sudo firewall-cmd --permanent --add-service=smtp
sudo firewall-cmd --permanent --add-service=smtps
sudo firewall-cmd --permanent --add-service=smtp-submission
sudo firewall-cmd --reload
# 6. Start Postfix
sudo systemctl enable postfix
sudo systemctl restart postfix
# 7. Test
echo "Testing SMTP STARTTLS..."
echo "QUIT" | openssl s_client -starttls smtp -connect localhost:25 2>&1 | grep -E "(CONNECTED|subject=)"
echo "✅ Postfix TLS setup complete!"
echo "⚠️ Replace self-signed cert with proper certificate from CA"
16.20 Key Takeaways
- Postfix supports TLS on ports 25 (STARTTLS), 465 (SMTPS), 587 (Submission)
- RHEL 7 requires manual TLS config (protocols, ciphers)
- RHEL 8+ uses crypto-policies (much simpler!)
- Security levels:
none,may,encrypt,dane - certmonger works great with Postfix
- Always test with
openssl s_client -starttls smtp - Monitor logs for TLS errors
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ POSTFIX TLS QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Config: /etc/postfix/main.cf │
│ Certs: smtpd_tls_cert_file = /path/to/cert.crt │
│ Keys: smtpd_tls_key_file = /path/to/key.key │
│ Security: smtpd_tls_security_level = may|encrypt │
│ │
│ Test: openssl s_client -starttls smtp -connect :25 │
│ Reload: postfix reload │
│ Check: postfix check │
│ Logs: tail -f /var/log/maillog | grep TLS │
│ │
│ Ports: 25 (SMTP+STARTTLS) │
│ 465 (SMTPS - direct TLS) │
│ 587 (Submission with STARTTLS) │
│ │
│ certmonger: ipa-getcert ... -C "postfix reload" │
└──────────────────────────────────────────────────────────────┘
⚠️ RHEL 7: Manual protocol/cipher config needed
✅ RHEL 8/9/10: Crypto-policies handle TLS settings
🧪 Hands-On Lab
Lab 08: Postfix TLS
Configure TLS for Postfix mail server
- 📁 Location:
labs/en_US/08-postfix-tls/ - ⏱️ Time: 30 minutes
- 🎯 Level: Intermediate
Chapter Navigation
Chapter 17: OpenLDAP & Directory Services
Enterprise Directory: Learn how to secure OpenLDAP directory services with TLS/SSL on RHEL, protecting user authentication and directory queries.
17.1 LDAP vs LDAPS vs STARTTLS
Three Ways to Secure LDAP
| Method | Port | Encryption | Use Case |
|---|---|---|---|
| LDAP | 389 | ❌ None | Legacy, not recommended |
| LDAPS | 636 | ✅ TLS from start | Preferred, encrypted |
| LDAP+STARTTLS | 389 | ✅ Upgrade to TLS | Alternative to LDAPS |
Recommendation: Use LDAPS (port 636) for simplicity and security.
17.2 OpenLDAP Installation
All RHEL Versions
#============================================#
# INSTALL OPENLDAP SERVER
#============================================#
# Install OpenLDAP server
sudo dnf install openldap-servers openldap-clients -y
# Start and enable
sudo systemctl enable slapd
sudo systemctl start slapd
# Open firewall
sudo firewall-cmd --permanent --add-service=ldap
sudo firewall-cmd --permanent --add-service=ldaps
sudo firewall-cmd --reload
# Verify
systemctl status slapd
ss -tlnp | grep -E ':(389|636)'
17.3 Generating Certificates for LDAP
Certificate Requirements
LDAP certificates should include:
- ✅ CN matching LDAP server hostname
- ✅ SANs with LDAP server FQDN
- ✅ Server Authentication key usage
- ✅ Valid trust chain
#============================================#
# GENERATE LDAP SERVER CERTIFICATE
#============================================#
# Step 1: Generate private key
sudo openssl genpkey -algorithm RSA \
-out /etc/openldap/certs/ldap.key \
-pkeyopt rsa_keygen_bits:2048
# Step 2: Set permissions (important!)
sudo chmod 600 /etc/openldap/certs/ldap.key
sudo chown ldap:ldap /etc/openldap/certs/ldap.key
# Step 3: Generate CSR
sudo openssl req -new \
-key /etc/openldap/certs/ldap.key \
-out /tmp/ldap.csr \
-subj "/CN=ldap.example.com" \
-addext "subjectAltName=DNS:ldap.example.com,DNS:dir.example.com"
# Step 4: Get certificate from CA
# Step 5: Install certificate
sudo cp ldap.crt /etc/openldap/certs/
sudo chmod 644 /etc/openldap/certs/ldap.crt
sudo chown ldap:ldap /etc/openldap/certs/ldap.crt
# Step 6: Install CA certificate
sudo cp ca.crt /etc/openldap/certs/
sudo chmod 644 /etc/openldap/certs/ca.crt
17.4 OpenLDAP TLS Configuration
Method 1: cn=config (Dynamic Configuration)
#============================================#
# CONFIGURE TLS WITH cn=config (PREFERRED)
#============================================#
# Create LDIF file
cat > /tmp/tls-config.ldif << EOF
dn: cn=config
changetype: modify
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap.key
-
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/ca.crt
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.2
EOF
# Apply configuration
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/tls-config.ldif
# Restart slapd
sudo systemctl restart slapd
# Verify
sudo slapcat -b "cn=config" | grep -i tls
Method 2: slapd.conf (Legacy)
#============================================#
# CONFIGURE TLS WITH slapd.conf (LEGACY)
#============================================#
# Edit /etc/openldap/slapd.conf
TLSCertificateFile /etc/openldap/certs/ldap.crt
TLSCertificateKeyFile /etc/openldap/certs/ldap.key
TLSCACertificateFile /etc/openldap/certs/ca.crt
# RHEL 7: Manually specify TLS version
TLSProtocolMin 1.2
# Restart
sudo systemctl restart slapd
17.5 Client Configuration
Configure LDAP Client for TLS
#============================================#
# /etc/openldap/ldap.conf - CLIENT CONFIG
#============================================#
# Server URI (use ldaps:// for port 636)
URI ldaps://ldap.example.com
# CA certificate for validation
TLS_CACERT /etc/pki/tls/certs/ca-bundle.crt
# Certificate verification
TLS_REQCERT demand # require valid certificate
# RHEL 7: Specify minimum TLS version
# TLS_PROTOCOL_MIN 1.2
Test LDAP Client Connection
#============================================#
# TEST LDAPS CLIENT CONNECTION
#============================================#
# Test LDAPS (port 636)
ldapsearch -H ldaps://ldap.example.com:636 \
-D "cn=admin,dc=example,dc=com" \
-W \
-b "dc=example,dc=com" \
"(objectClass=*)"
# Test LDAP with STARTTLS (port 389)
ldapsearch -H ldap://ldap.example.com:389 -ZZ \
-D "cn=admin,dc=example,dc=com" \
-W \
-b "dc=example,dc=com"
# -ZZ enforces STARTTLS (fails if unavailable)
# -Z attempts STARTTLS (continues without if unavailable)
17.6 FreeIPA Integration
Note: FreeIPA is covered in detail in Chapter 19. This is a quick overview.
FreeIPA Automatically Handles LDAPS
#============================================#
# FREEIPA LDAPS (AUTOMATIC!)
#============================================#
# FreeIPA automatically configures LDAPS
# No manual certificate config needed!
# Test FreeIPA LDAPS
ldapsearch -H ldaps://ipa.example.com:636 \
-D "uid=admin,cn=users,cn=accounts,dc=example,dc=com" \
-W \
-b "dc=example,dc=com"
# FreeIPA certificates managed by certmonger automatically
sudo getcert list | grep -A10 "Directory Server"
17.7 Testing OpenLDAP TLS
Comprehensive Testing
#============================================#
# OPENLDAP TLS TESTING
#============================================#
# Test 1: Check if slapd is listening
ss -tlnp | grep slapd
# Should show ports 389 and/or 636
# Test 2: Test LDAPS connection with OpenSSL
openssl s_client -connect ldap.example.com:636
# Look for:
# - Successful TLS handshake
# - Certificate details
# - Verify return code: 0 (ok)
# Test 3: Test with ldapsearch (anonymous)
ldapsearch -H ldaps://ldap.example.com:636 \
-x -b "" -s base "(objectClass=*)" namingContexts
# Test 4: Test authenticated query
ldapsearch -H ldaps://ldap.example.com:636 \
-D "cn=admin,dc=example,dc=com" \
-W \
-b "dc=example,dc=com" \
"(uid=*)"
# Test 5: Test STARTTLS
ldapsearch -H ldap://ldap.example.com:389 -ZZ \
-x -b "" -s base
# Test 6: Verify certificate from server
echo | openssl s_client -connect ldap.example.com:636 2>&1 | \
openssl x509 -noout -subject -issuer -dates
17.8 Troubleshooting OpenLDAP TLS
Diagnostic Commands
#============================================#
# OPENLDAP TLS DIAGNOSTICS
#============================================#
# Check slapd configuration
sudo slapcat -b "cn=config" | grep -i tls
# Check certificate files
sudo ls -lZ /etc/openldap/certs/
# Verify permissions
# Key should be readable by 'ldap' user
sudo -u ldap cat /etc/openldap/certs/ldap.key >/dev/null && \
echo "✅ Key readable" || echo "❌ Permission denied"
# Check SELinux context
ls -Z /etc/openldap/certs/*.{crt,key}
# Check slapd logs
sudo journalctl -u slapd -f
# Test with verbose OpenSSL
openssl s_client -connect ldap.example.com:636 -showcerts -debug
# Test STARTTLS
ldapsearch -H ldap://ldap.example.com:389 -ZZ -d 1
Common OpenLDAP TLS Issues
| Error | Cause | Solution |
|---|---|---|
| “TLS: can’t connect” | Certificate/key not readable | Check ownership: chown ldap:ldap |
| “TLS: hostname does not match” | CN/SAN mismatch | Regenerate cert with correct hostname |
| “Certificate verification failed” | CA not trusted | Add CA to client’s trust store |
| “Permission denied” on key | Wrong ownership/permissions | chmod 600, chown ldap:ldap |
| “TLS engine not initialized” | TLS not configured | Add TLS directives to config |
| “error:14094410:SSL routines” | Protocol/cipher mismatch | Check crypto-policy (RHEL 8+) |
17.9 Client Certificate Authentication
Require Client Certificates
#============================================#
# OPENLDAP WITH CLIENT CERT AUTH
#============================================#
# Server configuration (cn=config)
cat > /tmp/client-cert.ldif << EOF
dn: cn=config
changetype: modify
add: olcTLSVerifyClient
olcTLSVerifyClient: demand
-
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/client-ca.crt
EOF
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/client-cert.ldif
# Restart
sudo systemctl restart slapd
Client connection with certificate:
# Client must provide certificate
ldapsearch -H ldaps://ldap.example.com:636 \
-x -b "dc=example,dc=com" \
-ZZ
# Configure client cert in /etc/openldap/ldap.conf:
TLS_CERT /etc/openldap/certs/client.crt
TLS_KEY /etc/openldap/certs/client.key
17.10 Version-Specific Considerations
RHEL 7
#============================================#
# OPENLDAP TLS - RHEL 7
#============================================#
# Manual TLS protocol specification
# In slapd.conf or cn=config:
TLSProtocolMin 1.2
# Or with cn=config:
olcTLSProtocolMin: 3.2 # 3.1=TLS1.0, 3.2=TLS1.1, 3.3=TLS1.2
# Manual cipher configuration
TLSCipherSuite HIGH:!aNULL:!MD5:!3DES
# Test
openssl s_client -connect ldap.example.com:636 -tls1_2
RHEL 8/9/10
#============================================#
# OPENLDAP TLS - RHEL 8/9/10
#============================================#
# Crypto-policies automatically configure TLS
# No need to specify TLSProtocolMin or ciphers!
# Just configure certificates:
olcTLSCertificateFile: /etc/openldap/certs/ldap.crt
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap.key
olcTLSCACertificateFile: /etc/openldap/certs/ca.crt
# Crypto-policy handles the rest
update-crypto-policies --show
17.11 certmonger with OpenLDAP
Automated Certificate Management
#============================================#
# CERTMONGER + OPENLDAP
#============================================#
# Install certmonger
# RHEL 8/9/10
sudo dnf install certmonger
# RHEL 7
# sudo yum install certmonger
sudo systemctl enable --now certmonger
# Request certificate from FreeIPA
sudo ipa-getcert request \
-f /etc/openldap/certs/ldap.crt \
-k /etc/openldap/certs/ldap.key \
-D ldap.example.com \
-K ldap/ldap.example.com@REALM \
-C "systemctl restart slapd" # Restart slapd after renewal
# Set proper ownership
sudo chown ldap:ldap /etc/openldap/certs/ldap.{crt,key}
sudo chmod 600 /etc/openldap/certs/ldap.key
# Monitor
sudo getcert list
17.12 Troubleshooting LDAPS
Diagnostic Steps
#============================================#
# LDAPS TROUBLESHOOTING
#============================================#
# Step 1: Verify slapd is listening on 636
ss -tlnp | grep 636
# Step 2: Check certificate configuration
sudo slapcat -b "cn=config" | grep olcTLS
# Step 3: Test certificate file
sudo openssl x509 -in /etc/openldap/certs/ldap.crt -noout -text
# Step 4: Test key file
sudo openssl rsa -in /etc/openldap/certs/ldap.key -check
# Step 5: Verify cert/key match
CERT_MOD=$(openssl x509 -noout -modulus -in /etc/openldap/certs/ldap.crt | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in /etc/openldap/certs/ldap.key | openssl md5)
[ "$CERT_MOD" = "$KEY_MOD" ] && echo "✅ Match" || echo "❌ Mismatch!"
# Step 6: Check permissions
ls -l /etc/openldap/certs/
# Key should be owned by ldap:ldap with mode 600
# Step 7: Test connection
openssl s_client -connect ldap.example.com:636
# Step 8: Check logs
sudo journalctl -u slapd | grep -i tls
# Step 9: Test from client
ldapsearch -H ldaps://ldap.example.com:636 -x -b "" -s base
# Step 10: Check SELinux
sudo ausearch -m avc -ts recent | grep ldap
17.13 Common Issues and Solutions
Issue 1: “TLS: can’t accept” Error
Symptom: LDAPS connection refused
Diagnosis:
sudo journalctl -u slapd | grep "TLS: can't accept"
Causes & Solutions:
# Cause 1: Key not readable by ldap user
sudo chown ldap:ldap /etc/openldap/certs/ldap.key
sudo chmod 600 /etc/openldap/certs/ldap.key
# Cause 2: SELinux blocking
sudo restorecon -Rv /etc/openldap/certs/
# Or check for denials:
sudo ausearch -m avc -ts recent | grep ldap
# Cause 3: Certificate/key mismatch
# Regenerate CSR with correct key
Issue 2: “TLS: hostname does not match”
Symptom: Client gets hostname mismatch error
Diagnosis:
# Check certificate CN and SANs
openssl x509 -in /etc/openldap/certs/ldap.crt -noout -subject -ext subjectAltName
Solution:
# Reissue certificate with correct hostname in SANs
openssl req -new -key /etc/openldap/certs/ldap.key -out /tmp/ldap.csr \
-subj "/CN=ldap.example.com" \
-addext "subjectAltName=DNS:ldap.example.com,DNS:ldap,IP:10.0.0.10"
# Or on client side: allow looser verification (NOT recommended for production)
# In /etc/openldap/ldap.conf:
TLS_REQCERT allow # instead of 'demand'
Issue 3: Certificate Not Trusted
Symptom: “Certificate verification failed”
Solution:
# Add CA to system trust store
sudo cp ldap-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Or specify CA in client config
# /etc/openldap/ldap.conf:
TLS_CACERT /etc/openldap/certs/ca.crt
17.14 Security Best Practices
Hardened OpenLDAP TLS Configuration
#============================================#
# HARDENED LDAP TLS CONFIG (cn=config)
#============================================#
dn: cn=config
changetype: modify
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap.key
-
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/ca.crt
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.3
-
replace: olcTLSCipherSuite
olcTLSCipherSuite: HIGH:!aNULL:!MD5:!RC4
-
add: olcTLSVerifyClient
olcTLSVerifyClient: never
Apply:
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f hardened-tls.ldif
sudo systemctl restart slapd
17.15 Integration with System Services
SSSD with LDAPS (System Authentication)
#============================================#
# CONFIGURE SSSD TO USE LDAPS
#============================================#
# /etc/sssd/sssd.conf
[domain/example.com]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://ldap.example.com:636
ldap_search_base = dc=example,dc=com
ldap_tls_cacert = /etc/pki/tls/certs/ca-bundle.crt
ldap_tls_reqcert = demand
# Restart SSSD
sudo systemctl restart sssd
# Test
id ldapuser@example.com
17.16 Monitoring LDAP TLS
Monitoring Commands
#============================================#
# MONITOR LDAP TLS
#============================================#
# Check certificate expiration
openssl s_client -connect ldap.example.com:636 2>/dev/null | \
openssl x509 -noout -dates
# Check TLS connections
sudo journalctl -u slapd | grep "TLS established"
# Count LDAPS vs LDAP connections
sudo journalctl -u slapd --since today | grep -c "conn=.*LDAPS"
sudo journalctl -u slapd --since today | grep -c "conn=.*LDAP"
# Check for TLS errors
sudo journalctl -u slapd | grep -i "tls.*error"
# certmonger status (if used)
sudo getcert list -d /etc/openldap/certs
17.17 Key Takeaways
- LDAPS (port 636) is simpler than STARTTLS
- Certificate ownership critical - Must be readable by
ldapuser - cn=config preferred over slapd.conf (modern RHEL)
- FreeIPA handles LDAPS automatically - Much easier!
- Client configuration matters - Set TLS_CACERT correctly
- Test thoroughly - Use
openssl s_clientandldapsearch -ZZ - certmonger automates certificate renewal
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ OPENLDAP TLS QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Config: cn=config (dynamic) or slapd.conf (legacy) │
│ Certs: /etc/openldap/certs/ │
│ Ownership: chown ldap:ldap *.{crt,key} │
│ Permissions: chmod 600 ldap.key │
│ │
│ LDAPS: Port 636 (TLS from start) │
│ STARTTLS: Port 389 (upgrade to TLS) │
│ │
│ Test LDAPS: openssl s_client -connect host:636 │
│ Test STLS: ldapsearch -H ldap://host:389 -ZZ │
│ │
│ Client: /etc/openldap/ldap.conf │
│ TLS_CACERT /path/to/ca.crt │
│ TLS_REQCERT demand │
│ │
│ Logs: journalctl -u slapd | grep TLS │
└──────────────────────────────────────────────────────────────┘
⚠️ Key must be owned by 'ldap' user!
✅ Use FreeIPA for easier LDAP+TLS management
🧪 Hands-On Lab
Lab 09: OpenLDAP LDAPS
Configure LDAPS for secure directory services
- 📁 Location:
labs/en_US/09-openldap-ldaps/ - ⏱️ Time: 35-40 minutes
- 🎯 Level: Intermediate
Chapter Navigation
| ← Previous: Chapter 16 - Postfix Mail Server TLS | Next: Chapter 18 - Database TLS (PostgreSQL, MySQL) → |
|---|
Chapter 18: Database TLS (PostgreSQL, MySQL)
Data in Transit: Protect database connections with TLS encryption. Learn how to configure PostgreSQL and MySQL/MariaDB with certificates on RHEL.
18.1 Why Database TLS?
Protect Sensitive Data:
- ✅ Encrypt database queries and results
- ✅ Prevent eavesdropping on credentials
- ✅ Authenticate database servers
- ✅ Enable client certificate authentication
- ✅ Meet compliance requirements (PCI-DSS, HIPAA)
Threat Model:
- Without TLS: Passwords and data travel in cleartext
- With TLS: All communication encrypted
18.2 PostgreSQL with SSL/TLS
Installation
#============================================#
# INSTALL POSTGRESQL
#============================================#
# RHEL 7/8/9/10
sudo dnf install postgresql-server -y
# Initialize database
sudo postgresql-setup --initdb
# Enable and start
sudo systemctl enable postgresql
sudo systemctl start postgresql
# Verify
systemctl status postgresql
ss -tlnp | grep 5432
Generate PostgreSQL Certificates
#============================================#
# GENERATE POSTGRESQL CERTIFICATES
#============================================#
# Step 1: Generate server key
sudo -u postgres openssl genpkey -algorithm RSA \
-out /var/lib/pgsql/data/server.key \
-pkeyopt rsa_keygen_bits:2048
# Step 2: Set permissions (critical!)
sudo chmod 600 /var/lib/pgsql/data/server.key
sudo chown postgres:postgres /var/lib/pgsql/data/server.key
# Step 3: Generate CSR
sudo -u postgres openssl req -new \
-key /var/lib/pgsql/data/server.key \
-out /tmp/postgres.csr \
-subj "/CN=db.example.com" \
-addext "subjectAltName=DNS:db.example.com,DNS:postgres.example.com"
# Step 4: Get certificate from CA
# Step 5: Install certificate
sudo cp postgres.crt /var/lib/pgsql/data/server.crt
sudo chmod 600 /var/lib/pgsql/data/server.crt
sudo chown postgres:postgres /var/lib/pgsql/data/server.crt
# Step 6: Install CA certificate
sudo cp ca.crt /var/lib/pgsql/data/root.crt
sudo chmod 644 /var/lib/pgsql/data/root.crt
Configure PostgreSQL for SSL
#============================================#
# CONFIGURE POSTGRESQL SSL
#============================================#
# Edit /var/lib/pgsql/data/postgresql.conf
sudo -u postgres vi /var/lib/pgsql/data/postgresql.conf
# Enable SSL
ssl = on
# Certificate files (relative to data directory)
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'root.crt'
# RHEL 7: Specify minimum TLS version
# ssl_min_protocol_version = 'TLSv1.2'
# RHEL 8/9/10: Uses system crypto-policy
# (no need to specify ssl_min_protocol_version)
# Optional: Prefer server ciphers
ssl_prefer_server_ciphers = on
# Restart PostgreSQL
sudo systemctl restart postgresql
Configure Client Authentication
#============================================#
# /var/lib/pgsql/data/pg_hba.conf
#============================================#
# Require SSL for all connections
hostssl all all 0.0.0.0/0 md5
# Require client certificate
hostssl all alice 0.0.0.0/0 cert clientcert=verify-full
# Reload configuration
sudo systemctl reload postgresql
HBA Types:
host: Allow non-SSLhostssl: Require SSLhostnossl: Explicitly forbid SSL
Client Cert Options:
md5: SSL required, password authcert: SSL + client certificate requiredclientcert=verify-full: Verify client cert against CA
Test PostgreSQL SSL
#============================================#
# TEST POSTGRESQL SSL
#============================================#
# Test 1: Connect with SSL required
psql "host=db.example.com port=5432 user=testuser dbname=testdb sslmode=require"
# Test 2: Connect with full verification
psql "host=db.example.com port=5432 user=testuser dbname=testdb sslmode=verify-full sslrootcert=/etc/pki/tls/certs/ca-bundle.crt"
# Test 3: With client certificate
psql "host=db.example.com port=5432 user=alice dbname=mydb sslmode=verify-full sslcert=/home/alice/.postgresql/client.crt sslkey=/home/alice/.postgresql/client.key sslrootcert=/etc/pki/tls/certs/ca-bundle.crt"
# Test 4: Check SSL from within PostgreSQL
psql -h db.example.com -U testuser -d testdb -c "SELECT ssl, version FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
# Test 5: OpenSSL test
openssl s_client -connect db.example.com:5432 -starttls postgres
SSL Modes:
disable: No SSLallow: Try SSL, fallback to non-SSLprefer: Prefer SSL, fallback allowedrequire: Require SSL (don’t verify cert)verify-ca: Require SSL, verify CAverify-full: Require SSL, verify hostname + CA
18.3 MySQL/MariaDB with SSL/TLS
Installation
#============================================#
# INSTALL MARIADB (MYSQL REPLACEMENT ON RHEL 8+)
#============================================#
# RHEL 8/9/10
sudo dnf install mariadb-server -y
# Start and enable
sudo systemctl enable mariadb
sudo systemctl start mariadb
# Secure installation
sudo mysql_secure_installation
# Verify
systemctl status mariadb
ss -tlnp | grep 3306
Generate MySQL/MariaDB Certificates
#============================================#
# GENERATE MYSQL/MARIADB CERTIFICATES
#============================================#
# Create certificate directory
sudo mkdir -p /etc/mysql/certs
sudo chmod 755 /etc/mysql/certs
# Step 1: Generate server key
sudo openssl genpkey -algorithm RSA \
-out /etc/mysql/certs/server.key \
-pkeyopt rsa_keygen_bits:2048
sudo chmod 600 /etc/mysql/certs/server.key
sudo chown mysql:mysql /etc/mysql/certs/server.key
# Step 2: Generate CSR
sudo openssl req -new \
-key /etc/mysql/certs/server.key \
-out /tmp/mysql.csr \
-subj "/CN=db.example.com" \
-addext "subjectAltName=DNS:db.example.com,DNS:mysql.example.com"
# Step 3: Get certificate from CA
# Step 4: Install certificate and CA
sudo cp mysql.crt /etc/mysql/certs/server.crt
sudo cp ca.crt /etc/mysql/certs/ca.crt
sudo chmod 644 /etc/mysql/certs/{server.crt,ca.crt}
sudo chown mysql:mysql /etc/mysql/certs/{server.crt,ca.crt}
Configure MySQL/MariaDB for SSL
#============================================#
# CONFIGURE MYSQL/MARIADB SSL
#============================================#
# Edit /etc/my.cnf.d/server.cnf (or /etc/my.cnf)
sudo vi /etc/my.cnf.d/server.cnf
[mysqld]
# SSL/TLS configuration
ssl-ca=/etc/mysql/certs/ca.crt
ssl-cert=/etc/mysql/certs/server.crt
ssl-key=/etc/mysql/certs/server.key
# Require secure transport (optional, forces TLS for all)
require_secure_transport=ON
# RHEL 7: Specify TLS version
# tls_version=TLSv1.2,TLSv1.3
# Restart MySQL/MariaDB
sudo systemctl restart mariadb
Verify SSL is Enabled
#============================================#
# VERIFY MYSQL SSL
#============================================#
# Connect and check SSL status
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# Should show:
# have_ssl | YES
# ssl_ca | /etc/mysql/certs/ca.crt
# ssl_cert | /etc/mysql/certs/server.crt
# ssl_key | /etc/mysql/certs/server.key
# Check active connections
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl_cipher';"
Test MySQL SSL Connection
#============================================#
# TEST MYSQL SSL CONNECTION
#============================================#
# Connect with SSL
mysql --ssl-ca=/etc/mysql/certs/ca.crt \
--ssl-mode=REQUIRED \
-h db.example.com \
-u testuser \
-p
# Verify SSL in use
mysql> \s
# Look for: "SSL: Cipher in use is ..."
# Or check from command line
mysql -h db.example.com -u testuser -p -e "STATUS" | grep SSL
18.4 Client Certificate Authentication
PostgreSQL with Client Certificates
#============================================#
# POSTGRESQL CLIENT CERT AUTH
#============================================#
# Server side: pg_hba.conf
hostssl all alice 0.0.0.0/0 cert clientcert=verify-full
# Generate client certificate
openssl genpkey -algorithm RSA -out alice.key -pkeyopt rsa_keygen_bits:2048
openssl req -new -key alice.key -out alice.csr -subj "/CN=alice"
# Get signed by CA
# Client connection
psql "host=db.example.com user=alice dbname=mydb sslmode=verify-full sslcert=alice.crt sslkey=alice.key sslrootcert=ca.crt"
MySQL/MariaDB with Client Certificates
#============================================#
# MYSQL/MARIADB CLIENT CERT AUTH
#============================================#
# Create user requiring X.509
mysql -u root -p << EOF
CREATE USER 'alice'@'%' REQUIRE X509;
GRANT ALL ON mydb.* TO 'alice'@'%';
FLUSH PRIVILEGES;
EOF
# Client connection
mysql --ssl-ca=ca.crt \
--ssl-cert=alice.crt \
--ssl-key=alice.key \
-h db.example.com \
-u alice \
-p mydb
18.5 Troubleshooting Database TLS
PostgreSQL Troubleshooting
#============================================#
# POSTGRESQL SSL TROUBLESHOOTING
#============================================#
# Check SSL is enabled
sudo -u postgres psql -c "SHOW ssl;"
# Should show: on
# View SSL settings
sudo -u postgres psql -c "SHOW ssl_cert_file; SHOW ssl_key_file; SHOW ssl_ca_file;"
# Check certificate files
ls -l /var/lib/pgsql/data/server.{crt,key}
# Verify ownership
# Should be: postgres:postgres
# Check permissions
# server.key should be 600
# Test connection with SSL debugging
psql "host=db.example.com sslmode=require" -d postgres -U testuser --set=sslcompression=on
# Check PostgreSQL logs
sudo tail -f /var/lib/pgsql/data/log/postgresql-*.log | grep -i ssl
MySQL/MariaDB Troubleshooting
#============================================#
# MYSQL/MARIADB SSL TROUBLESHOOTING
#============================================#
# Check SSL variables
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# If have_ssl = NO, check:
# 1. Certificate files exist
ls -l /etc/mysql/certs/
# 2. Permissions
# Should be readable by mysql user
# 3. Restart database
sudo systemctl restart mariadb
# Check error log
sudo tail -f /var/log/mariadb/mariadb.log | grep -i ssl
# Test connection
mysql --ssl-ca=/etc/mysql/certs/ca.crt \
--ssl-mode=REQUIRED \
-h localhost \
-u root \
-p \
-e "STATUS" | grep SSL
18.6 Common Issues and Solutions
Issue 1: PostgreSQL “Permission denied” on server.key
Symptom: PostgreSQL won’t start, logs show permission error
Fix:
# Set correct permissions
sudo chmod 600 /var/lib/pgsql/data/server.key
sudo chown postgres:postgres /var/lib/pgsql/data/server.key
# Fix SELinux context
sudo restorecon -Rv /var/lib/pgsql/data/
# Restart
sudo systemctl restart postgresql
Issue 2: MySQL “SSL connection error”
Diagnosis:
# Check if SSL is available
mysql -u root -p -e "SHOW VARIABLES LIKE 'have_ssl';"
# Should show: YES
# If shows: DISABLED
# Check certificate paths in my.cnf
Fix:
# Verify paths in /etc/my.cnf.d/server.cnf
[mysqld]
ssl-ca=/etc/mysql/certs/ca.crt
ssl-cert=/etc/mysql/certs/server.crt
ssl-key=/etc/mysql/certs/server.key
# Restart
sudo systemctl restart mariadb
Issue 3: Client Certificate Rejected
Symptom: Connection fails with client cert
PostgreSQL Fix:
# Check pg_hba.conf
cat /var/lib/pgsql/data/pg_hba.conf | grep hostssl
# Ensure client CA is installed
sudo cp client-ca.crt /var/lib/pgsql/data/root.crt
# Reload
sudo systemctl reload postgresql
MySQL Fix:
# Verify user requires X.509
mysql -u root -p -e "SELECT user, host, ssl_type FROM mysql.user WHERE user='alice';"
# Should show: X509
# Verify CA file configured
mysql -u root -p -e "SHOW VARIABLES LIKE 'ssl_ca';"
18.7 Version-Specific Considerations
PostgreSQL Versions on RHEL
| RHEL Version | PostgreSQL | SSL Support | Notes |
|---|---|---|---|
| RHEL 7 | 9.2 | ✅ Yes | Manual TLS version config |
| RHEL 8 | 10.x+ | ✅ Yes | System crypto-policy |
| RHEL 9 | 13.x+ | ✅ Yes | Enhanced, crypto-policy |
| RHEL 10 | 15.x+ | ✅ Yes | Latest, crypto-policy |
MySQL/MariaDB Versions on RHEL
| RHEL Version | Database | SSL Support | Notes |
|---|---|---|---|
| RHEL 7 | MariaDB 5.5 | ✅ Yes | Manual config |
| RHEL 8 | MariaDB 10.3+ | ✅ Yes | Crypto-policy aware |
| RHEL 9 | MariaDB 10.5+ | ✅ Yes | Modern TLS |
| RHEL 10 | MariaDB 10.11+ | ✅ Yes | Latest features |
18.8 Performance Considerations
PostgreSQL SSL Performance
#============================================#
# POSTGRESQL SSL PERFORMANCE TUNING
#============================================#
# /var/lib/pgsql/data/postgresql.conf
# SSL enabled
ssl = on
# SSL compression (disabled for security, CRIME attack)
ssl_compression = off
# SSL ciphers (RHEL 7 - manual)
# ssl_ciphers = 'HIGH:!aNULL:!MD5'
# RHEL 8/9/10: crypto-policy handles ciphers
# Connection pooling helps (use pgBouncer)
# SSL termination at proxy can improve performance
MySQL SSL Performance
#============================================#
# MYSQL SSL PERFORMANCE
#============================================#
# [mysqld] in /etc/my.cnf.d/server.cnf
# SSL enabled
ssl-ca=/etc/mysql/certs/ca.crt
ssl-cert=/etc/mysql/certs/server.crt
ssl-key=/etc/mysql/certs/server.key
# Disable weak ciphers (RHEL 7)
# tls_version=TLSv1.2,TLSv1.3
# ssl_cipher='ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256'
# RHEL 8/9/10: crypto-policy handles it
# Connection pooling (use ProxySQL or similar)
18.9 Monitoring Database TLS
PostgreSQL Monitoring
#============================================#
# MONITOR POSTGRESQL SSL
#============================================#
# Check SSL connections
sudo -u postgres psql -c "SELECT datname, usename, ssl, version FROM pg_stat_ssl JOIN pg_stat_activity ON pg_stat_ssl.pid = pg_stat_activity.pid;"
# Count SSL vs non-SSL
sudo -u postgres psql -c "SELECT ssl, COUNT(*) FROM pg_stat_ssl GROUP BY ssl;"
# Check certificate expiration
openssl x509 -in /var/lib/pgsql/data/server.crt -noout -checkend $((86400*30))
# Monitor connections
sudo -u postgres psql -c "SELECT COUNT(*) FROM pg_stat_activity WHERE ssl = true;"
MySQL Monitoring
#============================================#
# MONITOR MYSQL SSL
#============================================#
# Check SSL status
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl%';"
# Count SSL connections
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl_accepts';"
# Current SSL connections
mysql -u root -p -e "SELECT user, host, connection_type FROM information_schema.processlist WHERE connection_type = 'SSL/TLS';"
# Certificate expiration
openssl x509 -in /etc/mysql/certs/server.crt -noout -checkend $((86400*30))
18.10 Complete Setup Scripts
PostgreSQL SSL Setup Script
#!/bin/bash
# setup-postgresql-ssl.sh
echo "=== PostgreSQL SSL Setup ==="
# Generate self-signed cert (replace with proper CA cert!)
sudo -u postgres openssl req -new -x509 -days 365 -nodes \
-out /var/lib/pgsql/data/server.crt \
-keyout /var/lib/pgsql/data/server.key \
-subj "/CN=$(hostname -f)"
# Set permissions
sudo chmod 600 /var/lib/pgsql/data/server.{crt,key}
sudo chown postgres:postgres /var/lib/pgsql/data/server.{crt,key}
# Enable SSL in postgresql.conf
sudo -u postgres psql -c "ALTER SYSTEM SET ssl = on;"
# Restart
sudo systemctl restart postgresql
# Test
sudo -u postgres psql -c "SHOW ssl;"
echo "✅ PostgreSQL SSL enabled"
echo "⚠️ Replace self-signed cert with proper certificate from CA"
18.11 Key Takeaways
- Both PostgreSQL and MySQL support SSL/TLS
- File ownership critical - postgres:postgres or mysql:mysql
- Permissions: 600 for keys, 644 for certs
- pg_hba.conf controls PostgreSQL access (hostssl)
- sslmode important for PostgreSQL clients
- Client certificates enable strong authentication
- Test thoroughly before enforcing TLS
- Monitor SSL usage - Ensure clients actually use it
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ DATABASE TLS QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ === POSTGRESQL === │
│ Config: /var/lib/pgsql/data/postgresql.conf │
│ Access: /var/lib/pgsql/data/pg_hba.conf │
│ Certs: /var/lib/pgsql/data/server.{crt,key} │
│ Owner: postgres:postgres │
│ Enable: ssl = on │
│ Test: psql "sslmode=require" │
│ │
│ === MYSQL/MARIADB === │
│ Config: /etc/my.cnf.d/server.cnf │
│ Certs: /etc/mysql/certs/server.{crt,key} │
│ Owner: mysql:mysql │
│ Enable: ssl-ca, ssl-cert, ssl-key in [mysqld] │
│ Test: mysql --ssl-mode=REQUIRED │
│ │
│ Permissions: chmod 600 *.key │
│ chmod 644 *.crt │
└──────────────────────────────────────────────────────────────┘
⚠️ File ownership and permissions are critical!
✅ Use hostssl in pg_hba.conf to require SSL
🧪 Hands-On Lab
Lab 10: PostgreSQL TLS
Configure TLS for PostgreSQL database connections
- 📁 Location:
labs/en_US/10-postgresql-tls/ - ⏱️ Time: 25-30 minutes
- 🎯 Level: Intermediate
Chapter Navigation
| ← Previous: Chapter 17 - OpenLDAP & Directory Services | Next: Chapter 19 - FreeIPA Certificate Services → |
|---|
Chapter 19: FreeIPA Certificate Services
Enterprise CA: FreeIPA is Red Hat’s integrated identity and certificate management solution. It’s the recommended way to run an internal CA on RHEL.
19.1 What is FreeIPA?
FreeIPA (Identity, Policy, Audit) is an integrated security information management solution combining:
- 🔐 Identity Management (LDAP directory)
- 🎫 Authentication (Kerberos)
- 🔏 Certificate Authority (Dogtag PKI)
- 📋 Policy Management (sudo, HBAC)
- 🔍 DNS (BIND integration)
Why FreeIPA for Certificates?
Instead of:
- ❌ Manually managing certificates per server
- ❌ External CA costs
- ❌ Complex PKI infrastructure
FreeIPA Provides:
- ✅ Internal CA (free!)
- ✅ Automatic certificate enrollment
- ✅ Auto-renewal via certmonger
- ✅ Certificate profiles
- ✅ Web UI and CLI management
- ✅ Integration with RHEL services
19.2 FreeIPA Installation (Server)
Prerequisites
#============================================#
# PREREQUISITES FOR FREEIPA SERVER
#============================================#
# Requirements:
# - RHEL 7/8/9/10
# - Minimum 2 GB RAM (4 GB recommended)
# - Fully qualified hostname
# - Proper DNS resolution
# - Static IP address
# Verify hostname
hostnamectl
# Must show FQDN: ipa.example.com
# Verify DNS
nslookup $(hostname -f)
# Set hostname if needed
sudo hostnamectl set-hostname ipa.example.com
Install FreeIPA Server
#============================================#
# INSTALL FREEIPA SERVER
#============================================#
# Install packages
sudo dnf install ipa-server ipa-server-dns -y
# Run installation wizard
sudo ipa-server-install \
--realm EXAMPLE.COM \
--domain example.com \
--ds-password 'DirectoryPassword123!' \
--admin-password 'AdminPassword123!' \
--hostname ipa.example.com \
--setup-dns \
--forwarder 8.8.8.8 \
--forwarder 8.8.4.4 \
--unattended
# Installation takes 5-15 minutes
# Open firewall
sudo firewall-cmd --add-service={http,https,dns,ntp,freeipa-ldap,freeipa-ldaps,freeipa-replication} --permanent
sudo firewall-cmd --reload
# Verify
sudo ipactl status
# Should show multiple services running:
# - Directory Service (389-ds)
# - Certificate Authority (pki-tomcatd)
# - Kerberos KDC
# - Apache Web Server
# - DNS (named)
Access FreeIPA Web UI
# Get Kerberos ticket
kinit admin
# Password: AdminPassword123!
# Access Web UI
# https://ipa.example.com/
# Username: admin
# Password: AdminPassword123!
19.3 Enrolling Clients
Client Installation
#============================================#
# ENROLL CLIENT TO FREEIPA
#============================================#
# On client system (web01.example.com)
# Install IPA client
sudo dnf install ipa-client -y
# Enroll
sudo ipa-client-install \
--domain example.com \
--realm EXAMPLE.COM \
--server ipa.example.com \
--principal admin \
--password 'AdminPassword123!' \
--mkhomedir \
--unattended
# Verify
sudo ipa-client-install --uninstall # Just kidding, don't run this!
# Verify enrollment
ipa ping
# Pong!
# Check certificate tracking
sudo getcert list
# Shows certmonger tracking host certificate
19.4 Requesting Certificates from FreeIPA
Method 1: Web UI
- Navigate to https://ipa.example.com/
- Identity → Hosts → Select host → Actions → New Certificate
- Or: Identity → Services → Add service → Request certificate
Method 2: CLI (Recommended)
#============================================#
# REQUEST CERTIFICATE FROM FREEIPA
#============================================#
# For HTTP service on web01
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web01.crt \
-k /etc/pki/tls/private/web01.key \
-K HTTP/web01.example.com@EXAMPLE.COM \
-D web01.example.com \
-C "systemctl reload httpd"
# For custom service
sudo ipa-getcert request \
-f /etc/pki/tls/certs/myapp.crt \
-k /etc/pki/tls/private/myapp.key \
-K myapp/web01.example.com@EXAMPLE.COM \
-D myapp.example.com
# Check status
sudo getcert list
# Wait for MONITORING status (cert issued)
Method 3: ipa cert-request (Advanced)
#============================================#
# ADVANCED: IPA CERT-REQUEST
#============================================#
# Generate CSR
openssl req -new -key server.key -out server.csr \
-subj "/CN=server.example.com"
# Request certificate via IPA
ipa cert-request server.csr \
--principal HTTP/server.example.com@EXAMPLE.COM
# Get certificate ID from output
# Certificate: MIIDXTCCAkWgAwIBAgI...
# Request ID: 12345
# Retrieve certificate
ipa cert-show 12345 --out server.crt
19.5 Certificate Profiles
Available Profiles
#============================================#
# FREEIPA CERTIFICATE PROFILES
#============================================#
# List available profiles
ipa certprofile-find
# Common profiles:
# - caIPAserviceCert: Service certificates (HTTP, LDAP, etc.)
# - IECUserRoles: User certificates
# - smimeUserCert: S/MIME email certificates
# - caSelfSignedCert: Self-signed CA
# View profile details
ipa certprofile-show caIPAserviceCert
# Create custom profile
ipa certprofile-import MyCustomProfile \
--file custom-profile.cfg \
--store TRUE
Using Specific Profile
# Request with specific profile
sudo ipa-getcert request \
-f /etc/pki/tls/certs/custom.crt \
-k /etc/pki/tls/private/custom.key \
-K HTTP/web01.example.com@EXAMPLE.COM \
-T caIPAserviceCert # Specify profile
19.6 Automatic Renewal
How It Works
FreeIPA + certmonger = Automatic Certificate Lifecycle!
#============================================#
# AUTOMATIC RENEWAL WITH FREEIPA
#============================================#
# certmonger automatically:
# 1. Tracks certificate expiration
# 2. Submits renewal request to IPA
# 3. Obtains renewed certificate
# 4. Saves to file
# 5. Runs post-save command (e.g., reload httpd)
# Check renewal status
sudo getcert list
# Example output:
# Request ID '20240101000000':
# status: MONITORING
# stuck: no
# key pair storage: type=FILE,location='/etc/pki/tls/private/web.key'
# certificate: type=FILE,location='/etc/pki/tls/certs/web.crt'
# CA: IPA
# issuer: CN=Certificate Authority,O=EXAMPLE.COM
# subject: CN=web01.example.com,O=EXAMPLE.COM
# expires: 2025-01-01 00:00:00 UTC
# pre-save command:
# post-save command: systemctl reload httpd
# track: yes
# auto-renew: yes
# Renewal happens automatically ~28 days before expiry!
Manual Renewal (If Needed)
# Force renewal now
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# Or by request ID
sudo ipa-getcert resubmit -i 20240101000000
# Check if successful
sudo getcert list -f /etc/pki/tls/certs/web.crt
19.7 FreeIPA as Enterprise CA
CA Certificate Management
#============================================#
# FREEIPA CA MANAGEMENT
#============================================#
# View CA certificate
ipa ca-show ipa
# Export CA certificate
ipa ca-show ipa --certificate --out /tmp/ipa-ca.crt
# Install on clients (automatic during ipa-client-install)
# Manual: Copy to trust store
sudo cp /tmp/ipa-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Renew CA certificate (when needed)
sudo ipa-cacert-manage renew
# Check CA expiration
sudo getcert list -d /var/lib/ipa | grep "CA:"
Sub-CAs (Advanced)
#============================================#
# FREEIPA SUB-CA (RHEL 8+)
#============================================#
# Create sub-CA
ipa ca-add subca \
--subject "CN=SubCA,O=EXAMPLE.COM" \
--desc "Department Sub-CA"
# Issue certificate from sub-CA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/dept.crt \
-k /etc/pki/tls/private/dept.key \
-X subca \
-K HTTP/dept.example.com@EXAMPLE.COM
19.8 Service Integration Examples
Apache with FreeIPA Certificates
#============================================#
# APACHE + FREEIPA COMPLETE SETUP
#============================================#
# 1. Enroll system to IPA (if not already)
sudo ipa-client-install
# 2. Request certificate for Apache
sudo ipa-getcert request \
-f /etc/pki/tls/certs/$(hostname -f).crt \
-k /etc/pki/tls/private/$(hostname -f).key \
-K HTTP/$(hostname -f)@EXAMPLE.COM \
-D $(hostname -f) \
-C "systemctl reload httpd"
# 3. Wait for certificate
until sudo getcert list -f /etc/pki/tls/certs/$(hostname -f).crt | grep -q "MONITORING"; do
sleep 5
echo "Waiting for certificate..."
done
# 4. Configure Apache to use it
# /etc/httpd/conf.d/ssl.conf:
# SSLCertificateFile /etc/pki/tls/certs/$(hostname -f).crt
# SSLCertificateKeyFile /etc/pki/tls/private/$(hostname -f).key
# 5. Reload Apache
sudo systemctl reload httpd
# Certificate auto-renews!
LDAP with FreeIPA Certificates
# FreeIPA's own LDAP service automatically uses IPA certificates
# No manual configuration needed!
# Test
ldapsearch -H ldaps://ipa.example.com:636 -x -b "dc=example,dc=com"
19.9 Troubleshooting FreeIPA Certificates
Common Issues
Issue 1: CA_UNREACHABLE
# Symptom
sudo getcert list
# status: CA_UNREACHABLE
# Diagnosis
# 1. Check IPA server connectivity
ipa ping
# 2. Check Kerberos ticket
klist
# 3. Renew ticket if expired
kinit -k host/$(hostname -f)@EXAMPLE.COM
# 4. Check IPA services
ssh ipa.example.com "sudo ipactl status"
# 5. Retry
sudo ipa-getcert resubmit -i <request-id>
Issue 2: Certificate Request Denied
# Check request status
sudo getcert list -v
# Common causes:
# 1. Service principal doesn't exist
ipa service-find HTTP/$(hostname -f)
# If not found, add it:
ipa service-add HTTP/$(hostname -f)
# 2. Host not enrolled
ipa host-show $(hostname -f)
# 3. Insufficient permissions
# Must request as enrolled host principal
Issue 3: Certificate Not Renewed
# Check certmonger logs
sudo journalctl -u certmonger | tail -50
# Check IPA CA status
sudo ipactl status | grep "CA"
# Force renewal
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# Check CA certificate expiration
sudo openssl x509 -in /etc/ipa/ca.crt -noout -dates
19.10 Advanced Features
IdM ACME Support (RHEL 9+)
FreeIPA can expose its own internal ACME server. This is not Let’s Encrypt.
#============================================#
# ENABLE ACME ON FREEIPA (RHEL 9+)
#============================================#
# On IPA server (RHEL 9+)
sudo ipa-acme-manage enable
# Verify ACME is available
curl https://ipa.example.com/acme/directory
# On client: Use certbot or another ACME client against the IPA ACME directory
sudo certbot register --server https://ipa.example.com/acme/directory
sudo certbot certonly --server https://ipa.example.com/acme/directory \
-d web01.example.com
# This uses your IdM / FreeIPA CA, not the public Let's Encrypt service
Certificate Hold/Revocation
#============================================#
# REVOKE CERTIFICATES
#============================================#
# Put certificate on hold (temporary)
ipa cert-revoke 12345 --revocation-reason 6
# Revoke permanently
ipa cert-revoke 12345 --revocation-reason 1
# Reasons:
# 0: unspecified
# 1: keyCompromise
# 2: cACompromise
# 4: superseded
# 6: certificateHold (can be removed)
# Remove from hold
ipa cert-remove-hold 12345
# Check revocation status
ipa cert-show 12345
19.11 Monitoring FreeIPA PKI
Health Checks
#============================================#
# FREEIPA PKI HEALTH MONITORING
#============================================#
# Check IPA overall status
sudo ipactl status
# Check CA subsystem
sudo systemctl status pki-tomcatd@pki-tomcat
# Check certificate expirations
ipa-healthcheck --source ipahealthcheck.ipa.certs
# Check certificate tracking
sudo getcert list | grep -E "(Request|status|expires)"
# Monitor CA certificate
openssl x509 -in /etc/ipa/ca.crt -noout -dates
# Check for expiring certificates
ipa cert-find --validnotafter-from=$(date -d '+60 days' +%Y-%m-%d)
19.12 Backup and Recovery
Backup IPA Server
#============================================#
# BACKUP FREEIPA (INCLUDING CA)
#============================================#
# Full backup
sudo ipa-backup --data --online
# Backup location
ls -lh /var/lib/ipa/backup/
# Include CA keys (offline backup only!)
sudo ipactl stop
sudo ipa-backup --data --gpg
# Enter GPG passphrase
sudo ipactl start
Restore IPA Server
# Restore from backup
sudo ipactl stop
sudo ipa-restore /var/lib/ipa/backup/ipa-full-YYYY-MM-DD-HH-MM-SS/
sudo ipactl start
19.13 Best Practices
FreeIPA Certificate Best Practices
✅ Use FreeIPA for all internal certificates
✅ Let certmonger handle renewal (don't manually renew)
✅ Use service principals (HTTP/host, ldap/host, etc.)
✅ Add SANs when requesting certificates
✅ Set post-save commands (-C flag) for service reload
✅ Monitor IPA server health regularly
✅ Backup IPA server weekly (including CA keys)
✅ Have at least 2 IPA replicas (HA)
✅ Monitor CA certificate expiration
✅ Test certificate renewal before expiry
✅ Use certificate profiles for standardization
19.14 Integration Examples
Complete Service Setup with FreeIPA
#!/bin/bash
# setup-service-with-ipa.sh
# Complete workflow for service certificate from FreeIPA
SERVICE_NAME="HTTP" # Or LDAP, postgresql, etc.
HOST=$(hostname -f)
PRINCIPAL="${SERVICE_NAME}/${HOST}@EXAMPLE.COM"
CERT_FILE="/etc/pki/tls/certs/${HOST}.crt"
KEY_FILE="/etc/pki/tls/private/${HOST}.key"
POST_COMMAND="systemctl reload httpd"
echo "=== Requesting Certificate from FreeIPA ==="
# 1. Ensure service principal exists
if ! ipa service-show "${SERVICE_NAME}/${HOST}" &>/dev/null; then
echo "Creating service principal..."
ipa service-add "${SERVICE_NAME}/${HOST}"
fi
# 2. Request certificate
sudo ipa-getcert request \
-f "$CERT_FILE" \
-k "$KEY_FILE" \
-K "$PRINCIPAL" \
-D "$HOST" \
-C "$POST_COMMAND"
# 3. Wait for certificate
echo "Waiting for certificate issuance..."
until sudo getcert list -f "$CERT_FILE" | grep -q "MONITORING"; do
sleep 5
done
# 4. Verify
echo "✅ Certificate issued!"
sudo openssl x509 -in "$CERT_FILE" -noout -subject -issuer -dates
# 5. Certificate will auto-renew!
echo "✅ Certificate tracking enabled - auto-renewal active"
19.15 Key Takeaways
- FreeIPA is Red Hat’s recommended internal CA
- Combines identity + certificates + authentication
- certmonger integration is automatic
- Certificates auto-renew (no manual work!)
- Use service principals (HTTP/host, ldap/host)
- IdM ACME can expose an internal ACME endpoint in RHEL 9+; keep it distinct from Let’s Encrypt
- Web UI and CLI both available
- Scales to enterprise - Supports replicas, sub-CAs
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ FREEIPA CERTIFICATE SERVICES QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Install: dnf install ipa-server │
│ Setup: ipa-server-install │
│ Status: ipactl status │
│ Web UI: https://ipa.example.com/ │
│ │
│ Enroll: ipa-client-install │
│ Request: ipa-getcert request -K service/host@REALM │
│ List: getcert list │
│ Resubmit: ipa-getcert resubmit -f /path/to/cert.crt │
│ │
│ Principal: HTTP/host.example.com@REALM │
│ ldap/host.example.com@REALM │
│ postgresql/host.example.com@REALM │
│ │
│ Auto-renewal: Automatic via certmonger │
│ ACME: ipa-acme-manage enable (RHEL 9+) │
└──────────────────────────────────────────────────────────────┘
✅ Best for internal enterprise certificate management
✅ Fully integrated with RHEL
✅ No manual renewal needed!
Chapter Navigation
| ← Previous: Chapter 18 - Database TLS (PostgreSQL, MySQL) | Next: Chapter 20 - Other RHEL Services with Certificates → |
|---|
Chapter 20: Other RHEL Services with Certificates
Beyond the Basics: Many other RHEL services use certificates. This chapter covers Cockpit, VPN services, container registries, and more.
20.1 Services Covered
This chapter provides quick-start guides for:
- 🖥️ Cockpit (Web-based admin console)
- 🔒 OpenVPN (VPN service)
- 🛡️ strongSwan (IPsec VPN)
- 📦 Container Registry (Podman/Docker registry)
- 📡 HAProxy (Load balancer)
- 🔌 Redis with TLS (using stunnel)
- ⚙️ Ansible Tower/AWX (Automation platform)
20.2 Cockpit Web Console
What is Cockpit?
Cockpit is RHEL’s built-in web-based administration interface.
Default: Uses self-signed certificate Goal: Replace with proper certificate
Configure Cockpit with Certificates
#============================================#
# COCKPIT WITH PROPER CERTIFICATE
#============================================#
# Install Cockpit
sudo dnf install cockpit -y
sudo systemctl enable --now cockpit.socket
# Open firewall
sudo firewall-cmd --add-service=cockpit --permanent
sudo firewall-cmd --reload
# Cockpit certificate location
ls -l /etc/cockpit/ws-certs.d/
# Method 1: Place certificate with specific name
# Cockpit uses certificates in /etc/cockpit/ws-certs.d/
# Filename format: NN-name.cert (where NN = priority, lower = higher priority)
sudo cat server.crt server.key > /etc/cockpit/ws-certs.d/01-server.cert
sudo chmod 644 /etc/cockpit/ws-certs.d/01-server.cert
# Restart Cockpit
sudo systemctl restart cockpit.socket
# Method 2: Use certmonger
sudo ipa-getcert request \
-f /etc/cockpit/ws-certs.d/01-cockpit.cert \
-k /etc/cockpit/ws-certs.d/01-cockpit.cert \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl restart cockpit.socket" \
-F /etc/cockpit/ws-certs.d/01-cockpit.cert # Combined cert+key
# Access Cockpit
# https://server.example.com:9090/
Note: Cockpit expects combined cert+key in single file!
20.3 OpenVPN
Server Configuration with Certificates
#============================================#
# OPENVPN SERVER WITH CERTIFICATES
#============================================#
# Install OpenVPN (from EPEL on RHEL 7, repos on RHEL 8+)
sudo dnf install openvpn -y
# Generate or obtain certificates:
# - CA certificate
# - Server certificate + key
# - Client certificates
# /etc/openvpn/server/server.conf
port 1194
proto udp
dev tun
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh2048.pem
tls-auth /etc/openvpn/server/ta.key 0
cipher AES-256-GCM
auth SHA256
server 10.8.0.0 255.255.255.0
# Start OpenVPN
sudo systemctl enable --now openvpn-server@server
# Open firewall
sudo firewall-cmd --add-port=1194/udp --permanent
sudo firewall-cmd --reload
Test OpenVPN
# Check if running
systemctl status openvpn-server@server
# Test from client
openvpn --config client.ovpn --verb 3
20.4 strongSwan IPsec VPN
Configure with Certificates
#============================================#
# STRONGSWAN WITH CERTIFICATES
#============================================#
# Install
sudo dnf install strongswan -y
# Certificate locations
# CA: /etc/strongswan/ipsec.d/cacerts/
# Server/Client certs: /etc/strongswan/ipsec.d/certs/
# Private keys: /etc/strongswan/ipsec.d/private/
# Copy certificates
sudo cp ca.crt /etc/strongswan/ipsec.d/cacerts/
sudo cp server.crt /etc/strongswan/ipsec.d/certs/
sudo cp server.key /etc/strongswan/ipsec.d/private/
sudo chmod 600 /etc/strongswan/ipsec.d/private/server.key
# /etc/strongswan/ipsec.conf
config setup
charondebug="ike 2, knl 2, cfg 2"
conn example-ipsec
left=%any
leftid=@server.example.com
leftcert=server.crt
leftsubnet=10.0.0.0/24
right=%any
rightid=@client.example.com
rightcert=client.crt
auto=add
type=tunnel
keyexchange=ikev2
# Start strongSwan
sudo systemctl enable --now strongswan
# Check status
sudo swanctl --list-sas
20.5 Container Registry with TLS
Podman/Docker Registry
#============================================#
# CONTAINER REGISTRY WITH TLS
#============================================#
# Install registry
sudo dnf install -y podman
sudo podman pull docker.io/library/registry:2
# Create certificate for registry
sudo mkdir -p /etc/registry/certs
sudo openssl genpkey -algorithm RSA \
-out /etc/registry/certs/registry.key \
-pkeyopt rsa_keygen_bits:2048
sudo openssl req -new -x509 -days 365 \
-key /etc/registry/certs/registry.key \
-out /etc/registry/certs/registry.crt \
-subj "/CN=registry.example.com" \
-addext "subjectAltName=DNS:registry.example.com"
# Run registry with TLS
sudo podman run -d \
--name registry \
-p 5000:5000 \
-v /etc/registry/certs:/certs:ro \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
registry:2
# Test
curl https://registry.example.com:5000/v2/_catalog
Client Configuration
# Add registry CA to system trust
sudo cp /etc/registry/certs/registry.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Test pull
podman pull registry.example.com:5000/myimage:latest
20.6 HAProxy TLS Termination
Load Balancer with TLS
#============================================#
# HAPROXY TLS TERMINATION
#============================================#
# Install HAProxy
sudo dnf install haproxy -y
# HAProxy requires combined cert+key+chain in ONE file
cat server.crt intermediate.crt server.key > /etc/haproxy/certs/bundle.pem
sudo chmod 600 /etc/haproxy/certs/bundle.pem
# /etc/haproxy/haproxy.cfg
frontend https-in
bind *:443 ssl crt /etc/haproxy/certs/bundle.pem
mode http
default_backend web_servers
# Force HTTPS
http-request redirect scheme https unless { ssl_fc }
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000"
backend web_servers
mode http
balance roundrobin
server web1 10.0.1.10:80 check
server web2 10.0.1.11:80 check
server web3 10.0.1.12:80 check
# Start HAProxy
sudo systemctl enable --now haproxy
# Test
curl -v https://loadbalancer.example.com/
20.7 Redis with TLS (via stunnel)
Redis TLS Proxy
#============================================#
# REDIS WITH TLS USING STUNNEL
#============================================#
# Install Redis and stunnel
sudo dnf install redis stunnel -y
# Configure Redis (listen on localhost only)
# /etc/redis/redis.conf
bind 127.0.0.1
# Start Redis
sudo systemctl enable --now redis
# Configure stunnel
# /etc/stunnel/redis.conf
[redis-tls]
accept = 0.0.0.0:6380
connect = 127.0.0.1:6379
cert = /etc/pki/tls/certs/redis.crt
key = /etc/pki/tls/private/redis.key
CAfile = /etc/pki/tls/certs/ca-bundle.crt
# Optional: Require client certificates
verify = 2
CApath = /etc/pki/tls/certs/
# Start stunnel
sudo systemctl enable --now stunnel@redis
# Test
openssl s_client -connect localhost:6380
# Then type: PING
# Should respond: +PONG
20.8 Ansible Tower/AWX
Tower/AWX with Custom Certificate
#============================================#
# ANSIBLE TOWER/AWX CUSTOM CERTIFICATE
#============================================#
# Tower certificate location
# /etc/tower/tower.cert
# /etc/tower/tower.key
# Replace with proper certificate
sudo cp tower.example.com.crt /etc/tower/tower.cert
sudo cp tower.example.com.key /etc/tower/tower.key
sudo chmod 600 /etc/tower/tower.key
# Restart Tower services
sudo ansible-tower-service restart
# Or for AWX (containerized)
# Update docker-compose.yml or k8s secrets
# Test
curl -v https://tower.example.com/
20.9 SSH with Certificates (Advanced)
SSH Certificate Authentication
Note: Different from SSH keys! This uses X.509 certificates.
#============================================#
# SSH WITH X.509 CERTIFICATES (ADVANCED)
#============================================#
# Requires: openssh-server with X.509 patch or ssh-keysign
# Generate certificate for SSH user
openssl genpkey -algorithm RSA -out ssh-user.key -pkeyopt rsa_keygen_bits:2048
openssl req -new -key ssh-user.key -out ssh-user.csr -subj "/CN=user@example.com"
# Get signed by CA
# Configure sshd (experimental, not standard RHEL)
# /etc/ssh/sshd_config
# X509KeyAlgorithm x509v3-rsa2048-sha256
# X509TrustAnchor /etc/ssh/ca.crt
# Standard approach: Use SSH keys, not X.509
# X.509 SSH support is limited on RHEL
Recommendation: Use standard SSH key-based authentication for SSH, use X.509 for other services.
20.10 Monitoring Multiple Services
Multi-Service Certificate Check
#!/bin/bash
# check-all-services.sh
# Check certificates for all services
echo "=== Multi-Service Certificate Check ==="
# Apache
echo "1. Apache (port 443):"
timeout 3 openssl s_client -connect localhost:443 </dev/null 2>&1 | grep -E "(subject=|issuer=)" | head -2
# NGINX (if installed)
echo "2. NGINX (port 8443 or custom):"
timeout 3 openssl s_client -connect localhost:8443 </dev/null 2>&1 | grep -E "(subject=|issuer=)" | head -2
# Postfix
echo "3. Postfix SMTPS (port 465):"
timeout 3 openssl s_client -connect localhost:465 </dev/null 2>&1 | grep -E "(subject=|issuer=)" | head -2
# LDAPS
echo "4. LDAP (port 636):"
timeout 3 openssl s_client -connect localhost:636 </dev/null 2>&1 | grep -E "(subject=|issuer=)" | head -2
# PostgreSQL
echo "5. PostgreSQL (port 5432):"
sudo -u postgres psql -c "SHOW ssl;" 2>/dev/null
# Cockpit
echo "6. Cockpit (port 9090):"
timeout 3 openssl s_client -connect localhost:9090 </dev/null 2>&1 | grep -E "(subject=|issuer=)" | head -2
# certmonger tracking
echo ""
echo "7. certmonger tracked certificates:"
sudo getcert list | grep -c "Request ID"
echo "total certificates tracked"
echo ""
echo "=== Check Complete ==="
20.11 Service-Specific Quick References
Cockpit
# Install: dnf install cockpit
# Cert location: /etc/cockpit/ws-certs.d/
# Format: Combined cert+key
# Reload: systemctl restart cockpit.socket
# Test: https://server:9090/
OpenVPN
# Install: dnf install openvpn (EPEL)
# Cert location: /etc/openvpn/server/
# Files: ca.crt, server.crt, server.key
# Start: systemctl start openvpn-server@server
# Test: openvpn --config client.ovpn
strongSwan
# Install: dnf install strongswan
# Cert location: /etc/strongswan/ipsec.d/
# Subdirs: cacerts/, certs/, private/
# Start: systemctl start strongswan
# Test: swanctl --list-sas
HAProxy
# Install: dnf install haproxy
# Cert format: Combined PEM (cert+key+chain)
# Location: /etc/haproxy/certs/
# Config: bind *:443 ssl crt /path/to/bundle.pem
# Test: curl -v https://loadbalancer/
Container Registry
# Run: podman run -d -p 5000:5000 registry:2
# Certs: Mount as volumes (-v)
# Env vars: REGISTRY_HTTP_TLS_CERTIFICATE
# REGISTRY_HTTP_TLS_KEY
# Test: curl https://registry:5000/v2/_catalog
20.12 Wildcard Certificates for Multiple Services
When to Use Wildcards
Scenario: Multiple subdomains on same server
web.example.com → Apache
api.example.com → NGINX
admin.example.com → Cockpit
mail.example.com → Postfix
Solution: Use wildcard certificate *.example.com
Generate Wildcard Certificate
#============================================#
# WILDCARD CERTIFICATE
#============================================#
# Generate key
openssl genpkey -algorithm RSA -out wildcard.key -pkeyopt rsa_keygen_bits:2048
# Generate CSR
openssl req -new -key wildcard.key -out wildcard.csr \
-subj "/CN=*.example.com" \
-addext "subjectAltName=DNS:*.example.com,DNS:example.com"
# Submit to CA, receive wildcard.crt
# Use for multiple services
sudo cp wildcard.crt /etc/pki/tls/certs/
sudo cp wildcard.key /etc/pki/tls/private/
sudo chmod 600 /etc/pki/tls/private/wildcard.key
# Configure each service to use it
# Apache: SSLCertificateFile /etc/pki/tls/certs/wildcard.crt
# NGINX: ssl_certificate /etc/pki/tls/certs/wildcard.crt
# Postfix: smtpd_tls_cert_file = /etc/pki/tls/certs/wildcard.crt
Pros:
- ✅ One certificate for multiple subdomains
- ✅ Easier management
- ✅ Cost-effective (if purchasing)
Cons:
- ⚠️ If compromised, affects all subdomains
- ⚠️ Doesn’t work for multi-level (..example.com)
- ⚠️ Some security policies prohibit wildcards
20.13 Service Certificate Matrix
Certificate Requirements by Service
| Service | CN/SAN | Client Cert | Auto-Renew | Special Notes |
|---|---|---|---|---|
| Apache | Required | Optional (mTLS) | certmonger | Most common |
| NGINX | Required | Optional (mTLS) | certmonger | High performance |
| Postfix | Required | Optional | certmonger | SMTP/SMTPS |
| OpenLDAP | Required | Optional | certmonger | Must be ldap user readable |
| PostgreSQL | Required | Optional | Manual or script | postgres user ownership |
| MySQL | Required | Optional | Manual or script | mysql user ownership |
| FreeIPA | Automatic | N/A | Automatic | Self-managing |
| Cockpit | Required | No | certmonger | Combined cert+key file |
| OpenVPN | Required | Required | Manual | Complex PKI |
| strongSwan | Required | Required | Manual | IPsec specific |
| HAProxy | Required | No | certmonger | Combined PEM format |
| Registry | Required | Optional | Manual | Container specific |
20.14 Troubleshooting Quick Guide
Generic Service TLS Troubleshooting
#============================================#
# UNIVERSAL TLS TROUBLESHOOTING
#============================================#
# 1. Identify service and port
ss -tlnp | grep <service>
# 2. Check if TLS is enabled
# (service-specific command)
# 3. Test TLS connection
openssl s_client -connect localhost:<port>
# Or with STARTTLS:
openssl s_client -connect localhost:<port> -starttls <protocol>
# 4. Check certificate files
ls -lZ /path/to/certs/
# 5. Verify ownership/permissions
# - Certificate: 644, owned by service user
# - Key: 600, owned by service user
# 6. Check configuration
# (service-specific config file)
# 7. Check logs
sudo journalctl -u <service> | grep -i tls
sudo tail -f /var/log/<service>/ | grep -i tls
# 8. Test from remote client
openssl s_client -connect server.example.com:<port>
20.15 Centralized Certificate Management
Using certmonger for All Services
#============================================#
# CENTRAL CERTIFICATE MANAGEMENT STRATEGY
#============================================#
# Track all service certificates with certmonger
# Apache
sudo ipa-getcert request -f /etc/pki/tls/certs/apache.crt \
-k /etc/pki/tls/private/apache.key \
-K HTTP/$(hostname -f)@REALM \
-C "systemctl reload httpd"
# NGINX
sudo ipa-getcert request -f /etc/pki/tls/certs/nginx.crt \
-k /etc/pki/tls/private/nginx.key \
-K HTTP/$(hostname -f)@REALM \
-C "systemctl reload nginx"
# Postfix
sudo ipa-getcert request -f /etc/pki/tls/certs/postfix.crt \
-k /etc/pki/tls/private/postfix.key \
-K smtp/$(hostname -f)@REALM \
-C "postfix reload"
# OpenLDAP
sudo ipa-getcert request -f /etc/openldap/certs/ldap.crt \
-k /etc/openldap/certs/ldap.key \
-K ldap/$(hostname -f)@REALM \
-o ldap:ldap \
-m 600 \
-C "systemctl restart slapd"
# Monitor all
sudo getcert list
Benefits:
- ✅ Single tool for all services
- ✅ Automatic renewal
- ✅ Centralized monitoring
- ✅ Consistent approach
20.16 Key Takeaways
- Many services use certificates beyond just web servers
- Each service has unique requirements - Check ownership, permissions
- certmonger works with most services for automation
- Wildcard certificates can simplify multi-service setups
- Test each service independently
- Centralized tracking with certmonger recommended
- Document service-specific configurations
Quick Reference Card
┌───────────────────────────────────────────────────────────────┐
│ OTHER SERVICES CERTIFICATE QUICK REFERENCE │
├───────────────────────────────────────────────────────────────┤
│ Cockpit: /etc/cockpit/ws-certs.d/NN-name.cert │
│ (combined cert+key) │
│ │
│ OpenVPN: /etc/openvpn/server/{ca,server}.{crt,key} │
│ Complex PKI with client certs │
│ │
│ strongSwan: /etc/strongswan/ipsec.d/{cacerts,certs,private}/ │
│ IPsec-specific configuration │
│ │
│ HAProxy: Combined PEM (cert+key+chain in one file) │
│ /etc/haproxy/certs/bundle.pem │
│ │
│ Registry: Environment variables for container │
│ REGISTRY_HTTP_TLS_CERTIFICATE/KEY │
│ │
│ Generic: Check ownership, permissions, SELinux │
│ Test with: openssl s_client -connect :port │
└───────────────────────────────────────────────────────────────┘
✅ Use certmonger for automation where possible
✅ Each service has unique file format/location requirements
Chapter Navigation
| ← Previous: Chapter 19 - FreeIPA Certificate Services | Next: Chapter 21 - Service Certificate Best Practices → |
|---|
Chapter 21: Service Certificate Best Practices
Critical for Operations: Learn the best practices that prevent 90% of certificate problems before they happen.
21.1 The Cost of Poor Certificate Management
Real-world impacts:
- ❌ Expired certificate → Website down (revenue loss)
- ❌ Wrong permissions → Service fails to start (downtime)
- ❌ No backup → CA failure means manual reissue (hours/days)
- ❌ Poor naming → Confusion during incident (delayed response)
- ❌ No monitoring → Surprise expiration (emergency response)
This chapter prevents these issues.
21.2 File Organization Best Practices
Standard Directory Structure
/etc/pki/tls/
├── certs/ # Certificate files (public)
│ ├── service-name.crt # Actual certificates
│ ├── service-name-chain.crt # With intermediate chain
│ └── ca-bundle.crt # CA bundle
│
├── private/ # Private keys (protected!)
│ └── service-name.key # Private keys (mode 600)
│
├── csr/ # Certificate requests (optional)
│ └── service-name.csr # CSRs for tracking
│
└── backup/ # Backups (optional but recommended)
└── YYYY-MM-DD/
├── service-name.crt
└── service-name.key
Naming Conventions
Good naming prevents confusion:
# ✅ GOOD - Clear, descriptive
/etc/pki/tls/certs/web01-example-com.crt
/etc/pki/tls/certs/mail-smtp-example-com.crt
/etc/pki/tls/certs/ldap-primary-example-com.crt
# ❌ BAD - Unclear, generic
/etc/pki/tls/certs/cert1.crt
/etc/pki/tls/certs/new.crt
/etc/pki/tls/certs/temp.crt
Naming pattern:
[service]-[hostname/function]-[domain].crt
[service]-[hostname/function]-[domain].key
Examples:
apache-web01-example-com.crt
nginx-www-example-com.crt
postfix-mail-example-com.crt
ldap-dir01-example-com.crt
postgresql-db-primary-example-com.crt
File Permission Standards
#============================================#
# CRITICAL: Proper Permissions
#============================================#
# Certificates (public) - readable by all
/etc/pki/tls/certs/*.crt → 644 (rw-r--r--)
/etc/pki/tls/certs/ → 755 (rwxr-xr-x)
# Private keys (secret!) - only readable by owner
/etc/pki/tls/private/*.key → 600 (rw-------)
/etc/pki/tls/private/ → 711 (rwx--x--x)
# Service-specific keys - owned by service user
/etc/pki/tls/private/apache.key → 600, owner: root or apache
/etc/pki/tls/private/postgres.key → 600, owner: postgres
Set permissions script:
#!/bin/bash
# set-cert-permissions.sh
# Sets proper permissions on certificate files
CERT_DIR="/etc/pki/tls/certs"
KEY_DIR="/etc/pki/tls/private"
# Certificate directory
chmod 755 "$CERT_DIR"
chmod 644 "$CERT_DIR"/*.crt 2>/dev/null
# Private key directory
chmod 711 "$KEY_DIR"
chmod 600 "$KEY_DIR"/*.key 2>/dev/null
# Verify
echo "Certificate permissions:"
ls -ld "$CERT_DIR" "$CERT_DIR"/*.crt 2>/dev/null
echo ""
echo "Private key permissions:"
ls -ld "$KEY_DIR" "$KEY_DIR"/*.key 2>/dev/null
# Check for overly permissive keys
echo ""
echo "Checking for security issues:"
find "$KEY_DIR" -type f -not -perm 600 -ls 2>/dev/null && \
echo "⚠️ WARNING: Some keys have incorrect permissions!" || \
echo "✅ All keys properly protected"
21.3 Certificate Lifecycle Management
Renewal Timeline
Certificate Lifecycle (365 day validity):
Day 0: Certificate issued
Day 30: First renewal reminder (335 days left)
Day 60: Second reminder (305 days left)
Day 300: Critical renewal window starts (65 days left)
Day 330: URGENT - Renewal needed (35 days left)
Day 350: CRITICAL - Renewal overdue (15 days left)
Day 365: EXPIRED - Service outage!
Recommended Actions:
- Days 300-330: Plan and execute renewal
- Days 330-350: Emergency renewal if missed
- Days 350+: Incident response, temporary cert
Renewal Strategies
Strategy 1: Automated (Recommended)
# Using certmonger (RHEL)
sudo getcert request \
-f /etc/pki/tls/certs/web01-example-com.crt \
-k /etc/pki/tls/private/web01-example-com.key \
-D web.example.com \
-K host/web.example.com@REALM \
-C "systemctl reload httpd" # Auto-reload service
# Auto-renewal happens at 2/3 of cert lifetime
# 365 day cert → renews at day 243 (122 days remaining)
Strategy 2: Scheduled Manual Renewal
# Cron job for manual renewal check
# /etc/cron.weekly/check-certificates
#!/bin/bash
# Check certificates expiring in 60 days
find /etc/pki/tls/certs/ -name "*.crt" | while read cert; do
if openssl x509 -in "$cert" -noout -checkend $((86400*60)); then
echo "✅ $cert: OK"
else
echo "⚠️ $cert: Expires within 60 days!"
# Send alert
mail -s "Certificate Expiring Soon: $cert" admin@example.com
fi
done
Strategy 3: Calendar Reminders
# For environments without automation
# Create calendar entries:
# - 90 days before expiration: Start renewal
# - 60 days before: Verify renewal in progress
# - 30 days before: Complete renewal
# - 7 days before: Emergency if not done
21.4 Certificate Metadata Tracking
Certificate Inventory
Maintain a certificate inventory (spreadsheet or database):
Service,Hostname,Certificate_Path,Key_Path,Issuer,Issue_Date,Expiry_Date,SANs,Owner,Notes
Apache,web01,/etc/pki/tls/certs/web01-example-com.crt,/etc/pki/tls/private/web01.key,Internal CA,2024-01-01,2025-01-01,"web01.example.com,www.example.com",John Doe,Production
NGINX,web02,/etc/pki/tls/certs/web02-example-com.crt,/etc/pki/tls/private/web02.key,Let's Encrypt,2024-06-15,2024-09-15,"web02.example.com",Jane Smith,Staging
Generate inventory script:
#!/bin/bash
# generate-cert-inventory.sh
# Creates certificate inventory from system
echo "Service,Hostname,Certificate_Path,Issuer,Issue_Date,Expiry_Date,Days_Remaining"
# Scan common certificate locations
for cert in /etc/pki/tls/certs/*.crt /etc/httpd/conf/ssl/*.crt /etc/nginx/ssl/*.crt; do
[ -f "$cert" ] || continue
subject=$(openssl x509 -in "$cert" -noout -subject 2>/dev/null | sed 's/subject=//')
issuer=$(openssl x509 -in "$cert" -noout -issuer 2>/dev/null | sed 's/issuer=//')
notbefore=$(openssl x509 -in "$cert" -noout -startdate 2>/dev/null | cut -d= -f2)
notafter=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
# Calculate days remaining
expiry_epoch=$(date -d "$notafter" +%s 2>/dev/null)
now_epoch=$(date +%s)
days_remaining=$(( ($expiry_epoch - $now_epoch) / 86400 ))
# Determine service from path
service="Unknown"
[[ "$cert" =~ httpd ]] && service="Apache"
[[ "$cert" =~ nginx ]] && service="NGINX"
echo "$service,$(hostname),$cert,\"$issuer\",$notbefore,$notafter,$days_remaining"
done
21.5 Backup and Recovery
What to Backup
Critical files to backup:
✅ Private keys (.key files)
✅ Certificates (.crt files)
✅ CA certificates
✅ Certificate chains
✅ CSRs (for reference)
✅ Configuration files (Apache ssl.conf, etc.)
⚠️ NOT passwords or passphrases (store separately in vault)
Backup Script
#!/bin/bash
# backup-certificates.sh
# Backs up all certificates and keys
BACKUP_DIR="/var/backups/certificates"
DATE=$(date +%Y-%m-%d)
BACKUP_PATH="$BACKUP_DIR/$DATE"
# Create backup directory
mkdir -p "$BACKUP_PATH"
# Backup certificates
echo "Backing up certificates..."
cp -a /etc/pki/tls/certs/*.crt "$BACKUP_PATH/" 2>/dev/null
# Backup private keys (encrypted!)
echo "Backing up private keys..."
tar czf - /etc/pki/tls/private/*.key 2>/dev/null | \
openssl enc -aes-256-cbc -salt -out "$BACKUP_PATH/keys.tar.gz.enc" -pass pass:CHANGEME
# Backup configuration files
echo "Backing up configs..."
cp -a /etc/httpd/conf.d/ssl.conf "$BACKUP_PATH/" 2>/dev/null
cp -a /etc/nginx/nginx.conf "$BACKUP_PATH/" 2>/dev/null
# Create inventory
ls -lh "$BACKUP_PATH"
# Set permissions
chmod 700 "$BACKUP_PATH"
echo "✅ Backup complete: $BACKUP_PATH"
echo "⚠️ Remember to change encryption password!"
Recovery Procedure
#============================================#
# CERTIFICATE RECOVERY PROCEDURE
#============================================#
# 1. Stop affected service
sudo systemctl stop httpd
# 2. Restore certificate
sudo cp /var/backups/certificates/2024-11-15/web.crt /etc/pki/tls/certs/
# 3. Restore private key (decrypt)
cd /var/backups/certificates/2024-11-15/
openssl enc -aes-256-cbc -d -in keys.tar.gz.enc -pass pass:CHANGEME | \
sudo tar xzf - -C /
# 4. Set permissions
sudo chmod 600 /etc/pki/tls/private/*.key
sudo chmod 644 /etc/pki/tls/certs/*.crt
# 5. Verify files
sudo openssl x509 -in /etc/pki/tls/certs/web-example-com.crt -noout -text
sudo openssl rsa -in /etc/pki/tls/private/web-example-com.key -check
# 6. Start service
sudo systemctl start httpd
# 7. Test
curl -v https://localhost/
21.6 Security Best Practices
Private Key Protection
#============================================#
# PRIVATE KEY SECURITY CHECKLIST
#============================================#
✅ Permissions: 600 (or 400 for extra protection)
✅ Ownership: root or service user only
✅ Location: /etc/pki/tls/private/ (mode 711)
✅ SELinux: Proper context (cert_t)
✅ Backup: Encrypted at rest
✅ Never: Email, paste in tickets, commit to git
✅ Never: Share between systems (generate new)
✅ Audit: Log access with auditd
# Verify security
ls -lZ /etc/pki/tls/private/*.key
# -rw------- root root unconfined_u:object_r:cert_t:s0 server.key
Key Generation Best Practices
#============================================#
# GENERATE SECURE KEYS
#============================================#
# RSA 2048 (minimum for RHEL 8+)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048
# RSA 4096 (recommended for long-lived certs)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:4096
# EC P-256 (modern, smaller, fast)
openssl genpkey -algorithm EC -out server.key -pkeyopt ec_paramgen_curve:P-256
# Immediately set permissions!
chmod 600 server.key
# ❌ NEVER do this:
# openssl genrsa -out server.key 1024 # Too weak!
# chmod 644 server.key # Too permissive!
Certificate Validation Before Deployment
#!/bin/bash
# validate-certificate.sh
# Validates certificate before deployment
CERT=$1
KEY=$2
echo "=== Pre-Deployment Certificate Validation ==="
# Check 1: Certificate file exists and readable
if [ ! -f "$CERT" ]; then
echo "❌ Certificate file not found: $CERT"
exit 1
fi
# Check 2: Private key exists and readable
if [ ! -f "$KEY" ]; then
echo "❌ Private key not found: $KEY"
exit 1
fi
# Check 3: Certificate is valid X.509
if ! openssl x509 -in "$CERT" -noout 2>/dev/null; then
echo "❌ Invalid X.509 certificate"
exit 1
fi
# Check 4: Certificate not expired
if ! openssl x509 -in "$CERT" -noout -checkend 0; then
echo "❌ Certificate is expired!"
exit 1
fi
# Check 5: Certificate/key pair match
CERT_MOD=$(openssl x509 -noout -modulus -in "$CERT" | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in "$KEY" 2>/dev/null | openssl md5)
if [ "$CERT_MOD" != "$KEY_MOD" ]; then
echo "❌ Certificate and key do not match!"
exit 1
fi
# Check 6: SANs present (required for modern browsers)
if ! openssl x509 -in "$CERT" -noout -ext subjectAltName 2>/dev/null | grep -q "DNS:"; then
echo "⚠️ WARNING: No Subject Alternative Names found"
fi
# Check 7: Strong signature algorithm
SIG_ALG=$(openssl x509 -in "$CERT" -noout -text | grep "Signature Algorithm" | head -2)
if echo "$SIG_ALG" | grep -qi "sha1\|md5"; then
echo "❌ Weak signature algorithm: $SIG_ALG"
exit 1
fi
# Check 8: Adequate key size
KEY_SIZE=$(openssl x509 -in "$CERT" -noout -text | grep "Public-Key:" | grep -oP '\d+')
if [ "$KEY_SIZE" -lt 2048 ]; then
echo "❌ Key size too small: $KEY_SIZE bits (minimum 2048)"
exit 1
fi
echo ""
echo "✅ Certificate validation passed!"
echo " Subject: $(openssl x509 -in "$CERT" -noout -subject)"
echo " Issuer: $(openssl x509 -in "$CERT" -noout -issuer)"
echo " Expires: $(openssl x509 -in "$CERT" -noout -enddate | cut -d= -f2)"
echo " Key Size: $KEY_SIZE bits"
21.7 Multi-Service Coordination
When Multiple Services Share Certificates
# Scenario: Load balancer + multiple web servers
# Problem: Certificate on LB, services behind need same CN/SANs
# Solution 1: Use same certificate on all (if hostnames match)
# web01, web02, web03 all use cert for: web.example.com
# Solution 2: Wildcard certificate
# *.example.com works for web01.example.com, web02.example.com, etc.
# Solution 3: Comprehensive SANs
# Single cert with SANs: web.example.com, web01.example.com, web02.example.com
Certificate Deployment Workflow
#============================================#
# MULTI-SERVER DEPLOYMENT
#============================================#
# Step 1: Generate certificate on management node
openssl genpkey -algorithm RSA -out web.key -pkeyopt rsa_keygen_bits:2048
openssl req -new -key web.key -out web.csr \
-subj "/CN=web.example.com" \
-addext "subjectAltName=DNS:web.example.com,DNS:web01.example.com,DNS:web02.example.com"
# Step 2: Get certificate from CA
# (submit web.csr to CA, receive web.crt)
# Step 3: Validate locally
./validate-certificate.sh web.crt web.key
# Step 4: Distribute securely
for host in web01 web02 web03; do
scp web.crt root@$host:/etc/pki/tls/certs/
scp web.key root@$host:/etc/pki/tls/private/
ssh root@$host "chmod 644 /etc/pki/tls/certs/web-example-com.crt"
ssh root@$host "chmod 600 /etc/pki/tls/private/web-example-com.key"
done
# Step 5: Reload services
for host in web01 web02 web03; do
ssh root@$host "systemctl reload httpd"
done
# Step 6: Test each server
for host in web01 web02 web03; do
echo "Testing $host..."
curl -vk https://$host/ 2>&1 | grep "subject:"
done
21.8 Documentation Standards
Certificate Documentation Template
## Certificate: web.example.com
### Basic Information
- **Service:** Apache (httpd)
- **Server:** web01.example.com
- **Certificate Path:** `/etc/pki/tls/certs/web-example-com.crt`
- **Key Path:** `/etc/pki/tls/private/web-example-com.key`
- **Owner:** Web Team (webadmin@example.com)
### Certificate Details
- **Common Name (CN):** web.example.com
- **SANs:** web.example.com, www.example.com
- **Issuer:** Internal CA (ca.example.com)
- **Issue Date:** 2024-01-01
- **Expiry Date:** 2025-01-01
- **Key Type:** RSA 2048
### Renewal Process
- **Method:** certmonger automatic
- **Renewal Window:** 65 days before expiry
- **Post-Renewal:** `systemctl reload httpd`
- **Contact:** webadmin@example.com
### Service Configuration
- **Config File:** `/etc/httpd/conf.d/ssl.conf`
- **Service:** `httpd.service`
- **Restart Command:** `systemctl reload httpd`
### Troubleshooting
- **Logs:** `/var/log/httpd/ssl_error_log`
- **Test Command:** `curl -v https://web.example.com/`
- **Common Issues:** None reported
### Change History
- 2024-01-01: Initial deployment
- 2024-06-15: Added www.example.com SAN
21.9 Monitoring and Alerting
What to Monitor
✅ Certificate expiration (60, 30, 7 days before)
✅ Certificate validity (not expired, not yet valid)
✅ Certificate/key pair match
✅ Certificate trust chain
✅ Service health (is it using the cert?)
✅ certmonger tracking status
✅ Renewal success/failure
Simple Monitoring Script
#!/bin/bash
# monitor-certificates.sh
# Simple certificate monitoring
WARN_DAYS=30
CRIT_DAYS=7
EMAIL="admin@example.com"
check_cert() {
local cert=$1
local name=$(basename "$cert")
# Check if expires within warning period
if ! openssl x509 -in "$cert" -noout -checkend $((86400*WARN_DAYS)); then
if ! openssl x509 -in "$cert" -noout -checkend $((86400*CRIT_DAYS)); then
echo "🚨 CRITICAL: $name expires within $CRIT_DAYS days!"
return 2
else
echo "⚠️ WARNING: $name expires within $WARN_DAYS days"
return 1
fi
fi
return 0
}
# Check all certificates
WARNINGS=0
CRITICALS=0
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
check_cert "$cert"
ret=$?
[ $ret -eq 1 ] && ((WARNINGS++))
[ $ret -eq 2 ] && ((CRITICALS++))
done
# Alert if issues found
if [ $CRITICALS -gt 0 ] || [ $WARNINGS -gt 0 ]; then
echo "Certificate issues found: $CRITICALS critical, $WARNINGS warnings" | \
mail -s "Certificate Alert: $(hostname)" "$EMAIL"
fi
21.10 Incident Response Procedures
Certificate Expiration Incident
#============================================#
# EXPIRED CERTIFICATE EMERGENCY
#============================================#
# Step 1: Assess impact
systemctl status httpd
journalctl -xe | grep -i cert
# Step 2: Quick fix - Get temporary cert
# Option A: Self-signed (for internal only!)
openssl req -x509 -nodes -days 30 -newkey rsa:2048 \
-keyout /etc/pki/tls/private/temp-web01-example-com.key \
-out /etc/pki/tls/certs/temp-web01-example-com.crt \
-subj "/CN=$(hostname)"
# Option B: Restore from backup
cp /var/backups/certificates/latest/*.crt /etc/pki/tls/certs/
cp /var/backups/certificates/latest/*.key /etc/pki/tls/private/
# Step 3: Update service config to use temp cert
# Edit /etc/httpd/conf.d/ssl.conf
# SSLCertificateFile /etc/pki/tls/certs/temp-web01-example-com.crt
# SSLCertificateKeyFile /etc/pki/tls/private/temp-web01-example-com.key
# Step 4: Restart service
systemctl restart httpd
# Step 5: Get proper certificate ASAP
# Follow normal cert request process
# Step 6: Document incident
# What happened, why, how fixed, prevention
21.11 Best Practices Checklist
## Certificate Management Checklist
### File Organization
- [ ] Standard directory structure used
- [ ] Consistent naming convention
- [ ] Proper file permissions (600 for keys, 644 for certs)
- [ ] SELinux contexts correct
### Lifecycle Management
- [ ] Renewal process defined and documented
- [ ] Renewal reminders set (60, 30, 7 days)
- [ ] Automated renewal if possible (certmonger)
- [ ] Post-renewal actions defined
### Security
- [ ] Private keys protected (600 permissions)
- [ ] Keys never shared/emailed
- [ ] Strong key algorithm (RSA 2048+ or EC P-256)
- [ ] Strong signature (SHA-256+)
### Backup
- [ ] Certificates backed up
- [ ] Private keys backed up (encrypted)
- [ ] Backup tested and validated
- [ ] Restore procedure documented
### Documentation
- [ ] Certificate inventory maintained
- [ ] Each certificate documented
- [ ] Procedures written
- [ ] Contacts listed
### Monitoring
- [ ] Expiration monitoring enabled
- [ ] Alerts configured
- [ ] Health checks in place
- [ ] Incident response plan ready
### Validation
- [ ] Pre-deployment validation
- [ ] Post-deployment testing
- [ ] Regular audits scheduled
21.12 Key Takeaways
- Organization prevents confusion - Consistent structure and naming
- Permissions are critical - 600 for keys, 644 for certs
- Automate renewal - Use certmonger whenever possible
- Backup everything - But encrypt private keys
- Document thoroughly - Your future self will thank you
- Monitor proactively - Don’t wait for expiration
- Validate before deploy - Catch issues early
- Plan for incidents - Have recovery procedures ready
Quick Reference
┌─────────────────────────────────────────────────────────────┐
│ SERVICE CERTIFICATE BEST PRACTICES │
├─────────────────────────────────────────────────────────────┤
│ Files: /etc/pki/tls/certs/*.crt (644) │
│ /etc/pki/tls/private/*.key (600) │
│ Naming: [service]-[host]-[domain].[crt|key] │
│ Renewal: Automate with certmonger │
│ Backup: Daily, encrypted, tested │
│ Monitor: 60, 30, 7 days before expiry │
│ Validate: Before every deployment │
│ Document: Everything, everyone │
└─────────────────────────────────────────────────────────────┘
Chapter Navigation
| ← Previous: Chapter 20 - Other RHEL Services with Certificates | Next: Chapter 22 - certmonger Mastery → |
|---|
Chapter 22: certmonger Mastery
Set It and Forget It: certmonger is RHEL’s built-in certificate automation tool. Master it and you’ll never manually renew a certificate again.
22.1 What is certmonger?
certmonger is a certificate tracking and automatic renewal daemon for RHEL.
Think of it as:
- 📋 Certificate tracker - Monitors expiration dates
- 🔄 Auto-renewer - Renews before expiry
- 🔗 CA integrator - Works with FreeIPA, local/internal CAs, and custom external CA helpers
- ⚙️ Service integration - Runs commands after renewal
Why certmonger?
Without certmonger:
❌ Manual tracking of expiration dates
❌ Calendar reminders to renew
❌ Manual CSR generation
❌ Manual service restart after renewal
❌ Risk of missing renewals → outages
With certmonger:
✅ Automatic tracking
✅ Automatic renewal
✅ Automatic service reload
✅ Centralized monitoring
✅ No manual intervention!
22.2 Installation and Setup
Installation by RHEL Version
#============================================#
# INSTALL CERTMONGER
#============================================#
# RHEL 8/9/10
sudo dnf install certmonger -y
# RHEL 7
# sudo yum install certmonger -y
# Enable and start
sudo systemctl enable certmonger
sudo systemctl start certmonger
# Verify
systemctl status certmonger
sudo getcert list # Should show empty list initially
22.3 Basic Usage
Requesting a Certificate
#============================================#
# BASIC CERTIFICATE REQUEST
#============================================#
# Self-signed (for testing)
sudo getcert request \
-f /etc/pki/tls/certs/test.crt \
-k /etc/pki/tls/private/test.key
# From FreeIPA / IdM
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f)
# For public Let's Encrypt certificates, use certbot (Chapter 24).
# certmonger is the native choice for IPA, local CA, and helper-based workflows.
Checking Status
#============================================#
# CHECK CERTIFICATE STATUS
#============================================#
# List all tracked certificates
sudo getcert list
# Check specific certificate by file
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Check by request ID
sudo getcert list -i 20240101000000
# Verbose output
sudo getcert list -v
Status Values:
MONITORING: ✅ Certificate issued, tracking expirationSUBMITTING: 🔄 Submitting request to CACA_UNREACHABLE: ❌ Can’t reach CA serverCA_REJECTED: ❌ CA rejected requestNEED_KEY_GEN_PIN: ⏸️ Waiting for PIN (HSM/token)PRE_SAVE_COMMAND: 🔄 Running pre-save scriptPOST_SAVE_COMMAND: 🔄 Running post-save script
22.4 Advanced Options
Complete Request with All Options
#============================================#
# COMPLETE CERTMONGER REQUEST
#============================================#
sudo ipa-getcert request \
-f /etc/pki/tls/certs/web.example.com.crt \ # Certificate file
-k /etc/pki/tls/private/web.example.com.key \ # Private key file
-K HTTP/web.example.com@EXAMPLE.COM \ # Kerberos principal
-D web.example.com \ # DNS name (SAN)
-D www.example.com \ # Additional SAN
-D api.example.com \ # Another SAN
-U id-kp-serverAuth \ # Extended key usage
-N CN=web.example.com,O=Example,C=US \ # Subject DN
-g 2048 \ # Key size
-G rsa \ # Key type
-T caIPAserviceCert \ # IPA profile
-C "systemctl reload httpd" \ # Post-save command
-B "systemctl stop httpd" \ # Pre-save command
-v \ # Verbose
-w # Wait for completion
# Check status
sudo getcert list -f /etc/pki/tls/certs/web.example.com.crt
Key Options Explained:
| Option | Purpose | Example |
|---|---|---|
-f | Certificate file path | /etc/pki/tls/certs/web.crt |
-k | Private key file path | /etc/pki/tls/private/web.key |
-K | Kerberos principal | HTTP/web.example.com@REALM |
-D | DNS SAN | web.example.com |
-N | Subject DN | CN=web,O=Example |
-C | Post-save command | systemctl reload httpd |
-B | Pre-save command | systemctl stop httpd |
-c | CA name | IPA or external-ca |
-T | Certificate profile | caIPAserviceCert |
-g | Key size | 2048 or 4096 |
-G | Key type | rsa or ec |
22.5 Working with Different CAs
FreeIPA (Recommended for Internal)
#============================================#
# CERTMONGER + FREEIPA
#============================================#
# Prerequisites: System enrolled to IPA
ipa-client-install
# Request certificate
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
# certmonger automatically:
# ✅ Submits request to IPA CA
# ✅ Obtains certificate
# ✅ Saves to file
# ✅ Runs reload command
# ✅ Tracks expiration
# ✅ Renews ~28 days before expiry
Public ACME Requires certbot
#============================================#
# PUBLIC ACME VS NATIVE CERTMONGER WORKFLOWS
#============================================#
# Public Let's Encrypt certificate:
# Use certbot, not a fake certmonger CA profile.
sudo certbot certonly --apache -d public.example.com
# Native FreeIPA / IdM certificate:
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f) \
-C "systemctl reload httpd"
External CA (Manual Submission)
#============================================#
# CERTMONGER WITH EXTERNAL CA
#============================================#
# Configure external CA helper
sudo getcert add-ca -c external-ca \
-e '/usr/local/bin/external-ca-submit.sh'
# Request certificate
sudo getcert request \
-c external-ca \
-f /etc/pki/tls/certs/external.crt \
-k /etc/pki/tls/private/external.key
# Helper script must:
# 1. Read CSR from stdin
# 2. Submit to external CA
# 3. Return certificate on stdout
22.6 Managing Tracked Certificates
Modify Tracking
#============================================#
# MODIFY EXISTING CERTIFICATE TRACKING
#============================================#
# Update post-save command without rekeying
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt
sudo getcert start-tracking \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-C "systemctl reload httpd"
# Re-add -c, -K, -D, etc. if your original tracking entry used them
# Add additional SAN
sudo getcert resubmit -f /etc/pki/tls/certs/web.crt \
-D additional.example.com
# Stop tracking (keep certificate)
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt
# Remove completely
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt -r
# Start tracking existing certificate
sudo getcert start-tracking \
-f /etc/pki/tls/certs/existing.crt \
-k /etc/pki/tls/private/existing.key
Force Renewal
#============================================#
# FORCE IMMEDIATE RENEWAL
#============================================#
# By file path
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# By request ID
sudo getcert resubmit -i 20240101000000
# Wait for renewal
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Wait for status: MONITORING
22.7 Renewal Timing
Understanding Renewal Windows
Certificate Lifecycle (365 days):
Day 0: Certificate issued
│
│ [Normal operation]
│
Day 243: Renewal window starts (certmonger attempts renewal)
│ (2/3 of cert lifetime: 365 × 2/3 ≈ 243 days)
│
│ [Renewal attempts every 8 hours if CA available]
│
Day 335: Warning if not yet renewed (30 days left)
│
Day 350: Critical if not yet renewed (15 days left)
│
Day 365: Certificate expires → SERVICE OUTAGE if not renewed!
Default Behavior:
- Renewal starts at 2/3 of certificate lifetime
- 365-day cert → Renews at day 243 (122 days remaining)
- 90-day cert → Renews at day 60 (30 days remaining)
22.8 Post-Save Commands
Reload vs Restart
#============================================#
# POST-SAVE COMMAND STRATEGIES
#============================================#
# PREFER: reload (no downtime)
-C "systemctl reload httpd"
-C "systemctl reload nginx"
-C "postfix reload"
# SOMETIMES NEEDED: restart
-C "systemctl restart slapd" # OpenLDAP requires restart
-C "systemctl restart postgresql" # PostgreSQL requires restart
# MULTIPLE COMMANDS: Use script
-C "/usr/local/bin/after-cert-renewal.sh"
# Script example:
#!/bin/bash
# /usr/local/bin/after-cert-renewal.sh
systemctl reload httpd
systemctl reload nginx
systemctl reload postfix
logger "Certificates renewed via certmonger"
22.9 Troubleshooting certmonger
Common Issues
Issue 1: CA_UNREACHABLE
# Symptom
sudo getcert list
# status: CA_UNREACHABLE
# Diagnosis
# For FreeIPA:
ipa ping # Check IPA connectivity
klist # Check Kerberos ticket
# Fix
kinit -k host/$(hostname -f)@REALM # Renew host ticket
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# Check IPA server
ssh ipa-server "sudo ipactl status"
Issue 2: CA_REJECTED
# Symptom
sudo getcert list
# status: CA_REJECTED
# ca-error: Server at https://ipa.example.com/ipa/xml unwilling to issue certificate
# Common causes:
# 1. Service principal doesn't exist
ipa service-show HTTP/$(hostname -f)
# If not found:
ipa service-add HTTP/$(hostname -f)
# 2. Host not enrolled
ipa host-show $(hostname -f)
# 3. Permission issue
# Check IPA permissions
# Retry
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
Issue 3: Renewal Not Happening
# Check certmonger is running
systemctl status certmonger
# Check certificate status
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Check certmonger logs
sudo journalctl -u certmonger -f
# Force renewal
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# Check renewal window
# Certificate renews at 2/3 of lifetime
# Check "expires" date in getcert list output
22.10 IdM ACME vs Public Let’s Encrypt
Keep the Workflows Separate
#============================================#
# CHOOSE THE RIGHT TOOL FOR THE RIGHT CA
#============================================#
# Public Let's Encrypt certificate:
# Use certbot (see Chapter 24).
sudo certbot certonly --apache -d public.example.com -d www.public.example.com
# Native FreeIPA / IdM certificate:
# Use certmonger + ipa-getcert.
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.example.com.crt \
-k /etc/pki/tls/private/internal.example.com.key \
-K HTTP/internal.example.com@REALM \
-D internal.example.com \
-C "systemctl reload httpd"
# If IdM ACME is enabled, its ACME directory is your IPA server,
# not Let's Encrypt:
sudo certbot certonly \
--server https://ipa.example.com/acme/directory \
-d host.example.com
Important distinction:
- Let’s Encrypt = public internet ACME CA
- IdM/FreeIPA ACME = your internal IPA CA exposing an ACME endpoint
- certmonger = native RHEL tracker/renewer for IPA and helper-based workflows
22.11 Monitoring certmonger
Status Monitoring
#============================================#
# MONITOR CERTMONGER
#============================================#
# Overview of all certificates
sudo getcert list
# Count certificates by status
sudo getcert list | grep "status:" | sort | uniq -c
# Find certificates expiring soon (30 days)
for cert in $(sudo getcert list | grep "certificate:" | sed -n "s/.*location='\\([^']*\\)'.*/\\1/p"); do
if ! openssl x509 -in "$cert" -noout -checkend $((86400*30)) 2>/dev/null; then
echo "⚠️ Expires soon: $cert"
fi
done
# Check certmonger logs
sudo journalctl -u certmonger --since today
# Watch for renewal activity
sudo journalctl -u certmonger -f
# Check next renewal time
sudo getcert list | grep -A15 "Request ID" | grep "expires"
Health Check Script
#!/bin/bash
# certmonger-health-check.sh
echo "=== certmonger Health Check ==="
# certmonger running?
if systemctl is-active --quiet certmonger; then
echo "✅ certmonger is running"
else
echo "❌ certmonger is NOT running!"
exit 1
fi
# Count tracked certificates
TOTAL=$(sudo getcert list | grep -c "Request ID")
echo "📋 Tracking $TOTAL certificates"
# Check status breakdown
echo ""
echo "Status breakdown:"
sudo getcert list | grep "status:" | sort | uniq -c
# Check for problems
PROBLEMS=$(sudo getcert list | grep "status:" | grep -v "MONITORING" | wc -l)
if [ $PROBLEMS -gt 0 ]; then
echo ""
echo "⚠️ $PROBLEMS certificates need attention:"
sudo getcert list | grep -B5 "status:" | grep -E "(Request ID|status:)" | grep -v "MONITORING"
fi
# Check expiration warnings
echo ""
echo "Certificates expiring in 30 days:"
sudo getcert list | grep -A10 "Request ID" | grep "expires:" | \
while read line; do
# Parse and check expiry
# (simplified - production script would parse dates properly)
echo "$line"
done
22.12 certmonger Configuration
Main Configuration File
#============================================#
# CERTMONGER CONFIGURATION
#============================================#
# Config location
/etc/certmonger/certmonger.conf
# Database location (tracked certificates)
/var/lib/certmonger/
# List configured CAs
sudo getcert list-cas
# Add custom CA
sudo getcert add-ca -c my-ca \
-e '/usr/local/bin/my-ca-submit.sh'
# Remove CA
sudo getcert remove-ca -c my-ca
22.13 Best Practices
certmonger Best Practices
✅ **Always use post-save commands** (-C flag) to reload services
✅ **Track all production certificates** with certmonger
✅ **Monitor status weekly** with `getcert list`
✅ **Test renewal** before expiry with `resubmit`
✅ **Use verbose mode** (-v) when troubleshooting
✅ **Set up monitoring** for CA_UNREACHABLE status
✅ **Document request IDs** in your certificate inventory
✅ **Use IPA/certmonger for internal** certs, certbot for public Let's Encrypt
✅ **Keep certmonger logs** for audit trail
✅ **Test post-save commands** independently before use
What to Track with certmonger
# ✅ TRACK with certmonger:
- Web server certificates (Apache, NGINX)
- Mail server certificates (Postfix, Dovecot)
- LDAP server certificates (OpenLDAP)
- Application certificates (APIs, microservices)
- Service certificates (any TLS-enabled service)
# ❌ DON'T track with certmonger:
- CA root certificates (managed separately)
- Client certificates for users (different lifecycle)
- Test/temporary certificates
- Certificates you manage with other tools (certbot)
22.14 certmonger vs certbot
When to Use Which?
| Feature | certmonger | certbot |
|---|---|---|
| RHEL Native | ✅ Yes (included) | ❌ No (EPEL required) |
| FreeIPA Support | ✅ Native | ❌ No |
| Public Let’s Encrypt ACME | ❌ Use certbot instead | ✅ Yes (all versions) |
| Internal CA | ✅ Excellent | ❌ No |
| Apache/NGINX Config | ⏸️ Manual | ✅ Automatic |
| Service Integration | ✅ Post-save commands | ⏸️ Limited |
| Renewal Timing | 2/3 of lifetime | 30 days before |
| Red Hat Support | ✅ Yes | ❌ No (EPEL) |
Recommendation:
- Internal/Enterprise: Use certmonger + FreeIPA
- Public/Simple: Use certbot (but know it requires EPEL)
- Public certificates on any RHEL version: Use certbot for Let’s Encrypt
22.15 Advanced Scenarios
Scenario 1: High Availability Certificate Renewal
# Multiple servers with same service
# Server 1:
sudo ipa-getcert request \
-f /etc/pki/tls/certs/shared-service.crt \
-k /etc/pki/tls/private/shared-service.key \
-K HTTP/service.example.com@REALM \
-D service.example.com \
-C "systemctl reload httpd"
# Server 2: Same configuration
# Result: Each server manages its own cert independently
# Or: Use shared cert (copy files, not recommended)
Scenario 2: Wildcard Certificate
# Request wildcard from FreeIPA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/wildcard.crt \
-k /etc/pki/tls/private/wildcard.key \
-K HTTP/*.example.com@REALM \
-D *.example.com \
-D example.com \
-C "/usr/local/bin/reload-all-services.sh"
Scenario 3: EC Keys (Elliptic Curve)
# Request with EC key
sudo ipa-getcert request \
-f /etc/pki/tls/certs/ec-cert.crt \
-k /etc/pki/tls/private/ec-cert.key \
-K HTTP/$(hostname -f)@REALM \
-G ec \
-g nistp256 # or nistp384, nistp521
22.16 Key Takeaways
- certmonger is RHEL’s certificate automation
- Set it and forget it - Automatic renewal
- Works best with FreeIPA, internal CAs, and helper-based renewals
- Post-save commands reload services automatically
- Tracks expiration and renews at 2/3 of lifetime
- MONITORING status means all is well
- getcert list is your main monitoring tool
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ CERTMONGER MASTERY QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Install: dnf install certmonger (yum on RHEL 7) │
│ Start: systemctl enable --now certmonger │
│ │
│ Request: ipa-getcert request -f cert -k key -K principal│
│ List: getcert list │
│ Status: getcert list -f /path/to/cert.crt │
│ Resubmit: ipa-getcert resubmit -f /path/to/cert.crt │
│ Stop track: getcert stop-tracking -f /path/to/cert.crt │
│ │
│ Status: MONITORING = ✅ Good │
│ CA_UNREACHABLE = ❌ Check IPA/CA │
│ CA_REJECTED = ❌ Check principal/permissions │
│ │
│ Logs: journalctl -u certmonger -f │
│ Renewal: Automatic at 2/3 of cert lifetime │
│ Post-save: -C "systemctl reload <service>" │
└──────────────────────────────────────────────────────────────┘
✅ Native RHEL tool (Red Hat supported)
✅ Perfect for FreeIPA integration
✅ Best fit for FreeIPA and tracked renewal workflows
🧪 Hands-On Lab
Lab 11: certmonger Basics
Automate certificate renewal with certmonger
- 📁 Location:
labs/en_US/11-certmonger-basics/ - ⏱️ Time: 30-35 minutes
- 🎯 Level: Intermediate
Chapter Navigation
| ← Previous: Chapter 21 - Service Certificate Best Practices | Next: Chapter 23 - Crypto-Policies Deep Dive → |
|---|
Chapter 23: Crypto-Policies Deep Dive
Revolutionary Feature: RHEL 8+ crypto-policies provide system-wide cryptographic configuration. Master this and you control security across all applications with one command.
23.1 The Problem crypto-policies Solves
Before Crypto-Policies (RHEL 7)
Configure security individually for EVERY application:
Apache: /etc/httpd/conf.d/ssl.conf
SSLProtocol, SSLCipherSuite
NGINX: /etc/nginx/nginx.conf
ssl_protocols, ssl_ciphers
Postfix: /etc/postfix/main.cf
smtpd_tls_protocols, smtpd_tls_mandatory_ciphers
OpenLDAP: olcTLSProtocolMin, olcTLSCipherSuite
PostgreSQL: ssl_min_protocol_version
OpenSSH: /etc/ssh/sshd_config
Ciphers, MACs, KexAlgorithms
... and 20+ more applications!
Result: Inconsistent security, configuration nightmare
After Crypto-Policies (RHEL 8/9/10)
✅ Set ONE system-wide policy
✅ All applications automatically comply
✅ Consistent security across the system
✅ Change policy in seconds, not hours
Game changer for enterprise management!
23.2 How Crypto-Policies Work
Architecture
┌──────────────────────────────────────────────────┐
│ update-crypto-policies --set DEFAULT │
│ (Administrator command) │
└───────────────────────┬──────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ /etc/crypto-policies/back-ends/ │
│ (Generated config files for each library) │
│ ├─ opensslcnf.config │
│ ├─ gnutls.config │
│ ├─ nss.config │
│ ├─ bind.config │
│ └─ ... more ... │
└───────────────────────┬──────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
OpenSSL GnuTLS NSS
↓ ↓ ↓
Apache NGINX Firefox
Postfix vsftpd Thunderbird
OpenSSH wget Java apps
Key Insight: Applications read from back-end files, not directly from policy!
23.3 The Four Main Policies
Policy Comparison
| Policy | TLS Versions | Min RSA | SHA-1 | 3DES | Min DH | Use Case |
|---|---|---|---|---|---|---|
| DEFAULT | 1.2, 1.3 | 2048 | ❌ | ❌ | 2048 | ✅ Recommended |
| LEGACY | 1.0+ | 1024 | ⚠️ | ⚠️ | 1024 | Compatibility only |
| FUTURE | 1.2, 1.3 | 3072 | ❌ | ❌ | 3072 | High security |
| FIPS | 1.2, 1.3 | 2048 | ❌ | ❌ | 2048 | Federal compliance |
DEFAULT Policy Details
# Balanced security and compatibility
Protocols:
- TLS 1.2
- TLS 1.3
Minimum Key Sizes:
- RSA: 2048 bits
- DH: 2048 bits
- ECC: secp256r1 (P-256)
Allowed Ciphers:
- AES-128-GCM
- AES-256-GCM
- ChaCha20-Poly1305
- AES-128-CBC
- AES-256-CBC
Signature Algorithms:
- SHA-256
- SHA-384
- SHA-512
Blocked:
- TLS 1.0, 1.1
- MD5
- SHA-1 signatures
- 3DES, RC4, DES
- RSA < 2048 bits
- Export ciphers
23.4 Viewing and Changing Policies
Basic Commands
#============================================#
# CRYPTO-POLICY BASIC OPERATIONS
#============================================#
# View current policy
update-crypto-policies --show
# Output: DEFAULT
# List available policies
ls /usr/share/crypto-policies/policies/
# DEFAULT.pol FUTURE.pol LEGACY.pol FIPS.pol
# Set policy
sudo update-crypto-policies --set FUTURE
# You MUST restart services for changes to take effect!
sudo systemctl restart httpd nginx postfix slapd
# Or reboot (ensures everything picks up changes)
sudo reboot
What Happens When You Change Policy
#============================================#
# BEHIND THE SCENES
#============================================#
# Before
update-crypto-policies --show
# DEFAULT
# Change
sudo update-crypto-policies --set FUTURE
# Updates generated:
ls -l /etc/crypto-policies/back-ends/
# -rw-r--r--. opensslcnf.config ← Updated!
# -rw-r--r--. gnutls.config ← Updated!
# -rw-r--r--. nss.config ← Updated!
# -rw-r--r--. bind.config ← Updated!
# ... all back-ends updated ...
# View OpenSSL config
cat /etc/crypto-policies/back-ends/opensslcnf.config
# Shows actual OpenSSL configuration applied
23.5 Subpolicies (RHEL 9+)
Policy Modifiers
RHEL 9 introduced subpolicies - fine-tune existing policies!
#============================================#
# CRYPTO-POLICY SUBPOLICIES (RHEL 9+)
#============================================#
# Base policy with modifier
sudo update-crypto-policies --set DEFAULT:NO-SHA1
# Multiple modifiers
sudo update-crypto-policies --set DEFAULT:NO-SHA1:GOST
# Available subpolicy modules
ls /usr/share/crypto-policies/policies/modules/
# AD-SUPPORT.pmod
# GOST.pmod
# NO-CAMELLIA.pmod
# NO-SHA1.pmod
# NO-ENFORCE-EMS.pmod
# ...and more
# View module details
cat /usr/share/crypto-policies/policies/modules/NO-SHA1.pmod
Common Subpolicies:
| Subpolicy | Effect | Use Case |
|---|---|---|
NO-SHA1 | Completely disable SHA-1 | Extra security |
AD-SUPPORT | Enable AD compatibility | Mixed Windows/Linux |
GOST | Enable GOST algorithms | Russian requirements |
NO-CAMELLIA | Disable Camellia cipher | Specific compliance |
NO-ENFORCE-EMS | Disable Extended Master Secret | Compatibility |
23.6 Creating Custom Policy Modules
Custom Module Example
#============================================#
# CREATE CUSTOM POLICY MODULE
#============================================#
# Create custom module
sudo vi /etc/crypto-policies/policies/modules/CUSTOM-SECURITY.pmod
# Example content:
min_rsa_size = 4096
min_dh_size = 3072
min_dsa_size = 3072
sha1_in_certs = 0
arbitrary_dh_groups = 0
ssh_certs = 0
# Apply
sudo update-crypto-policies --set DEFAULT:CUSTOM-SECURITY
# Restart services
sudo systemctl restart httpd nginx postfix
# Verify
openssl ciphers -v | grep -E "RSA|DH"
23.7 Per-Application Overrides
When to Override
Sometimes ONE application needs different settings than system policy:
Example: Legacy application needs TLS 1.1, but system uses DEFAULT
Apache Override
#============================================#
# APACHE CRYPTO-POLICY OVERRIDE
#============================================#
# /etc/httpd/conf.d/ssl.conf
# Option 1: Include crypto-policy, then override
Include /etc/crypto-policies/back-ends/httpd.config
# Then add overrides:
SSLProtocol all -SSLv3 # Re-enable TLS 1.0/1.1
# Option 2: Completely opt-out
# Don't include crypto-policy file
# Manually configure everything:
SSLProtocol TLSv1.1 TLSv1.2 TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5
# ⚠️ Warning: You now manage Apache TLS manually
# System crypto-policy changes won't affect Apache
Better Approach: Create custom policy module instead of per-app overrides!
23.8 Testing Policy Impact
Before Changing Policy
#============================================#
# TEST POLICY CHANGE IMPACT
#============================================#
# 1. Document current state
update-crypto-policies --show > /tmp/current-policy.txt
systemctl list-units --type=service --state=running > /tmp/running-services.txt
# 2. Test applications
curl https://localhost/
psql -h localhost # etc.
# 3. Change policy on test system first
sudo update-crypto-policies --set FUTURE
# 4. Restart services
sudo systemctl restart httpd nginx postfix
# 5. Test thoroughly
./test-all-services.sh
# 6. If problems: Revert
sudo update-crypto-policies --set DEFAULT
# 7. If successful: Document and deploy to production
23.9 Policy Impact on Certificates
What Policies Control
Crypto-policies affect:
- ✅ TLS protocol versions allowed
- ✅ Cipher suites available
- ✅ Minimum key sizes accepted
- ✅ Signature algorithms allowed
- ✅ Diffie-Hellman parameters
- ✅ Certificate validation strictness
Crypto-policies DON’T affect:
- ❌ Which certificates to use (still configured per service)
- ❌ Certificate file locations
- ❌ CA trust store (that’s update-ca-trust)
- ❌ Certificate issuance
Certificate Compatibility Matrix
| Certificate Type | DEFAULT | LEGACY | FUTURE | FIPS |
|---|---|---|---|---|
| RSA 1024 bit | ❌ | ⚠️ | ❌ | ❌ |
| RSA 2048 bit | ✅ | ✅ | ❌ | ✅ |
| RSA 3072 bit | ✅ | ✅ | ✅ | ✅ |
| RSA 4096 bit | ✅ | ✅ | ✅ | ✅ |
| EC P-256 | ✅ | ✅ | ❌ | ✅ |
| EC P-384 | ✅ | ✅ | ✅ | ✅ |
| SHA-1 signature | ❌ | ⚠️ | ❌ | ❌ |
| SHA-256 signature | ✅ | ✅ | ✅ | ✅ |
23.10 Troubleshooting Crypto-Policies
Common Issues
Issue 1: Application Fails After Policy Change
# Symptom
sudo update-crypto-policies --set FUTURE
sudo systemctl restart httpd
# httpd fails to start
# Diagnosis
sudo journalctl -xe -u httpd | grep -i cipher
# Common cause: Application has hard-coded weak ciphers
# Solution 1: Revert policy
sudo update-crypto-policies --set DEFAULT
# Solution 2: Update application config
# Remove hard-coded cipher specifications
# Solution 3: Create custom policy module
Issue 2: “No Shared Cipher”
# Symptom: Clients can't connect
# Test
openssl s_client -connect server:443
# If shows "no shared cipher":
# Check policy
update-crypto-policies --show
# Test client capabilities
openssl s_client -connect server:443 -cipher 'ALL' -tls1_2
# Temporary fix (not recommended long-term):
sudo update-crypto-policies --set LEGACY
# Proper fix: Update client to support TLS 1.2+ and modern ciphers
Issue 3: Policy Doesn’t Seem to Apply
# Check if application is overriding policy
# Apache
grep -r "SSLProtocol\|SSLCipherSuite" /etc/httpd/
# If found: App is overriding policy
# NGINX
grep -r "ssl_protocols\|ssl_ciphers" /etc/nginx/
# If found: App is overriding policy
# Solution: Remove overrides, let crypto-policy handle it
# Or: Document why override is necessary
23.11 Best Practices
Recommendations
✅ **Use DEFAULT policy** for most environments
✅ **Test before deploying** new policies
✅ **Document policy choices** and reasons
✅ **Restart services** after policy changes
✅ **Avoid per-app overrides** when possible
✅ **Use subpolicies** (RHEL 9+) for fine-tuning
✅ **Monitor for compatibility** issues
✅ **Keep LEGACY temporary** if used
✅ **Plan migrations** when changing policies
✅ **Update clients** rather than weaken policy
When to Use Each Policy
DEFAULT:
- ✅ Most production environments
- ✅ Balanced security/compatibility
- ✅ Recommended starting point
- ✅ Tested and maintained by Red Hat
LEGACY:
- ⚠️ Temporary during migrations only!
- ⚠️ Supporting very old clients
- ⚠️ Testing compatibility issues
- ❌ Never long-term!
FUTURE:
- ✅ High-security environments
- ✅ All clients are modern
- ✅ Want strongest settings
- ✅ Planning for future standards
FIPS:
- ✅ Federal compliance required
- ✅ Government contracts
- ✅ Regulated industries
- ✅ Certification requirements
23.12 FIPS Policy Deep Dive
Enabling FIPS Mode
#============================================#
# ENABLE FIPS MODE
#============================================#
# Check current status
fips-mode-setup --check
# FIPS mode is disabled.
# Enable FIPS mode
sudo fips-mode-setup --enable
# MUST reboot
sudo reboot
# Verify after reboot
fips-mode-setup --check
# FIPS mode is enabled.
# Crypto-policy automatically set to FIPS
update-crypto-policies --show
# FIPS
FIPS Mode Requirements:
- Must be enabled at install OR with fips-mode-setup
- Requires reboot
- Affects entire system
- Only FIPS-approved algorithms available
- Performance impact (~10-20% slower)
FIPS Policy Specifics
# What FIPS policy allows:
✅ TLS 1.2, 1.3
✅ RSA 2048+ bits
✅ AES-128, AES-256 (GCM mode)
✅ SHA-256, SHA-384, SHA-512
✅ ECDHE key exchange
# What FIPS blocks:
❌ TLS 1.0, 1.1
❌ RSA < 2048 bits
❌ 3DES, RC4, DES
❌ MD5, SHA-1
❌ Non-approved algorithms
❌ CBC mode ciphers (in some cases)
23.13 Monitoring and Auditing
Check Policy Compliance
#============================================#
# VERIFY CRYPTO-POLICY COMPLIANCE
#============================================#
# Current policy
update-crypto-policies --show
# Which applications use crypto-policies?
ls -l /etc/crypto-policies/back-ends/
# Verify OpenSSL follows policy
openssl ciphers -v | head -20
# Check specific application config
# Apache
cat /etc/crypto-policies/back-ends/httpd.config
# Test actual connection
openssl s_client -connect localhost:443 -tls1_3
# Verify no overrides
grep -r "SSLProtocol\|SSLCipherSuite" /etc/httpd/ | grep -v crypto-policies
# Should be empty or commented out
23.14 Troubleshooting Workflow
Systematic Approach
Application fails after policy change?
│
├─ Step 1: Identify error
│ └─ Check logs: journalctl -xe
│
├─ Step 2: Verify policy active
│ └─ update-crypto-policies --show
│
├─ Step 3: Test with LEGACY
│ └─ sudo update-crypto-policies --set LEGACY
│ └─ If works → cipher/protocol issue
│
├─ Step 4: Identify incompatibility
│ └─ openssl s_client -cipher 'ALL' -tls1
│ └─ Find what client/server needs
│
├─ Step 5: Choose solution
│ ├─ A) Update client (best)
│ ├─ B) Create custom module (good)
│ └─ C) Per-app override (last resort)
│
└─ Step 6: Document and deploy
└─ Why override needed, plan to remove
23.15 Key Takeaways
- Crypto-policies are RHEL 8+ only (not in RHEL 7)
- DEFAULT policy is recommended for most cases
- Changes require service restarts to take effect
- Affects ALL crypto applications system-wide
- Subpolicies provide fine-tuning (RHEL 9+)
- Avoid per-app overrides when possible
- Test before deploying new policies
- LEGACY is temporary only!
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ CRYPTO-POLICIES QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Available: RHEL 8, 9, 10 only (not RHEL 7) │
│ │
│ View: update-crypto-policies --show │
│ Set: sudo update-crypto-policies --set <POLICY> │
│ Policies: DEFAULT, LEGACY, FUTURE, FIPS │
│ │
│ Subpolicy: update-crypto-policies --set DEFAULT:NO-SHA1 │
│ (RHEL 9+ only) │
│ │
│ Back-ends: /etc/crypto-policies/back-ends/ │
│ Modules: /usr/share/crypto-policies/policies/modules/ │
│ │
│ After change: systemctl restart <services> │
│ OR reboot │
│ │
│ DEFAULT: TLS 1.2+, RSA 2048+, No SHA-1 │
│ LEGACY: TLS 1.0+, allows weak (temporary only!) │
│ FUTURE: TLS 1.2+, RSA 3072+, strictest │
│ FIPS: Federal compliance (requires FIPS mode) │
└──────────────────────────────────────────────────────────────┘
⚠️ RHEL 7 doesn't have crypto-policies (manual config only)
✅ DEFAULT works for 95% of environments
🧪 Hands-On Lab
Lab 12: Crypto-Policies
Understand and configure system-wide crypto-policies
- 📁 Location:
labs/en_US/12-crypto-policies/ - ⏱️ Time: 25-30 minutes
- 🎯 Level: Intermediate
Chapter Navigation
Chapter 24: Let’s Encrypt & certbot
Free Public Certificates: Let’s Encrypt provides free, automated certificates for public-facing websites. Learn how to use it on RHEL with certbot.
24.1 What is Let’s Encrypt?
Let’s Encrypt is a free, automated, and open Certificate Authority.
Key Features:
- ✅ Free certificates (no cost)
- ✅ Automated issuance (via ACME protocol)
- ✅ Auto-renewal (every 60-90 days)
- ✅ Widely trusted (in all major browsers)
- ✅ Domain validation (DV certificates)
Limitations:
- ❌ Public domains only (must be internet-accessible for validation)
- ❌ 90-day validity (short-lived, requires automation)
- ❌ Domain Validation only (no Organization or Extended Validation)
- ❌ No wildcard with HTTP-01 (requires DNS-01 challenge)
24.2 Let’s Encrypt on RHEL: Public ACME and Native Alternatives
Method 1: certbot (Traditional)
⚠️ CRITICAL: EPEL Required
certbot is NOT available in official RHEL repositories.
It requires EPEL (Extra Packages for Enterprise Linux), a community-maintained repository that is NOT officially supported by Red Hat.
For Enterprise Production Environments:
- Consider FreeIPA with certmonger (Chapter 19)
- Or commercial CA with certmonger (Chapter 22)
- Or manual certificate management
EPEL is suitable for:
- Development/testing environments
- Small deployments where EPEL risk is acceptable
- Situations where free certificates outweigh support concerns
certbot Tool:
- Full automation
- Apache/NGINX plugins
- Automatic configuration
- Renewal timers
- ⚠️ Requires EPEL
Method 2: certmonger for Internal/Private CAs
Native RHEL Solution:
- ✅ No EPEL needed
- ✅ Red Hat supported
- ✅ Best fit for FreeIPA / IdM and local CA workflows
- ⏸️ Manual web server config (no Apache/NGINX plugins)
- ❌ Not a direct replacement for certbot with public Let’s Encrypt
We’ll use certbot for public Let’s Encrypt and certmonger for native internal workflows.
24.3 certbot Installation
RHEL 7
#============================================#
# INSTALL CERTBOT ON RHEL 7 (REQUIRES EPEL!)
#============================================#
# ⚠️ WARNING: Enabling third-party repository
# Step 1: Enable EPEL
sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y
# Verify EPEL enabled
yum repolist | grep epel
# Step 2: Install certbot
sudo yum install certbot python2-certbot-apache python2-certbot-nginx -y
# Verify
certbot --version
RHEL 8
#============================================#
# INSTALL CERTBOT ON RHEL 8 (REQUIRES EPEL!)
#============================================#
# ⚠️ WARNING: Enabling third-party repository
# Step 1: Enable EPEL
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y
# Or if you have subscription:
sudo dnf install epel-release -y
# Step 2: Install certbot
sudo dnf install certbot python3-certbot-apache python3-certbot-nginx -y
# Verify
certbot --version
RHEL 9/10
#============================================#
# INSTALL CERTBOT ON RHEL 9/10 (REQUIRES EPEL!)
#============================================#
# ⚠️ WARNING: Enabling third-party repository
# Step 1: Enable EPEL
sudo dnf install epel-release -y
# Step 2: Install certbot
sudo dnf install certbot python3-certbot-apache python3-certbot-nginx -y
# Verify
certbot --version
Remember: EPEL is community-supported. For enterprise production, consider FreeIPA + certmonger (native RHEL solution).
24.4 certbot Usage - Apache
Automatic Apache Configuration
#============================================#
# CERTBOT WITH APACHE (AUTOMATED!)
#============================================#
# Prerequisites:
# - Apache installed and running
# - Port 80 accessible from internet
# - Domain resolves to this server
# - EPEL repository enabled
# Obtain certificate and auto-configure Apache
sudo certbot --apache -d www.example.com -d example.com
# certbot will:
# 1. Generate certificate from Let's Encrypt
# 2. Automatically configure Apache SSL
# 3. Set up HTTP→HTTPS redirect
# 4. Configure auto-renewal
# Interactive prompts:
# - Email address (for renewal notices)
# - Agree to ToS
# - Redirect HTTP to HTTPS? (choose yes)
# Non-interactive (automation):
sudo certbot --apache \
-d www.example.com \
-d example.com \
--non-interactive \
--agree-tos \
--email admin@example.com \
--redirect
# Certificate location:
# /etc/letsencrypt/live/www.example.com/fullchain.pem
# /etc/letsencrypt/live/www.example.com/privkey.pem
24.5 certbot Usage - NGINX
Automatic NGINX Configuration
#============================================#
# CERTBOT WITH NGINX (AUTOMATED!)
#============================================#
# Prerequisites:
# - NGINX installed and running
# - Port 80 accessible
# - Domain resolves to server
# Obtain and configure
sudo certbot --nginx -d api.example.com
# Non-interactive
sudo certbot --nginx \
-d api.example.com \
--non-interactive \
--agree-tos \
--email admin@example.com \
--redirect
# certbot updates NGINX config automatically!
# No manual SSL configuration needed
24.6 certbot Manual Mode (Standalone)
Without Web Server Plugin
#============================================#
# CERTBOT STANDALONE (NO PLUGIN)
#============================================#
# Use when:
# - Web server not Apache/NGINX
# - Want manual control over config
# - Using custom web server
# Obtain certificate only (doesn't configure server)
sudo certbot certonly --standalone \
-d app.example.com \
--non-interactive \
--agree-tos \
--email admin@example.com
# Certificate saved to:
# /etc/letsencrypt/live/app.example.com/fullchain.pem
# /etc/letsencrypt/live/app.example.com/privkey.pem
# Manually configure your service to use it
# Apache example:
# SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
# SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem
24.7 Renewal
Automatic Renewal
#============================================#
# CERTBOT AUTO-RENEWAL
#============================================#
# certbot automatically sets up renewal timer
systemctl list-timers | grep certbot
# Should show: certbot-renew.timer
# View timer details
systemctl status certbot-renew.timer
# Test renewal (dry run - doesn't actually renew)
sudo certbot renew --dry-run
# Force actual renewal (if needed)
sudo certbot renew --force-renewal
# Check certificate expiration
sudo certbot certificates
# Renewal runs twice daily
# Renews certificates expiring within 30 days
Renewal Hooks
#============================================#
# RENEWAL HOOKS (DEPLOY COMMANDS)
#============================================#
# Add hook to reload service after renewal
sudo certbot renew --deploy-hook "systemctl reload nginx"
# Or create hook script
sudo vi /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
systemctl reload httpd
systemctl reload nginx
systemctl reload postfix
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
# Hooks in /etc/letsencrypt/renewal-hooks/:
# - pre/: Run before renewal
# - post/: Run after renewal (even if failed)
# - deploy/: Run after successful renewal only
24.8 Wildcard Certificates
DNS-01 Challenge Required
#============================================#
# WILDCARD CERTIFICATE (DNS CHALLENGE)
#============================================#
# Wildcard requires DNS-01 challenge
# (can't use HTTP-01 for *.example.com)
# Manual DNS challenge
sudo certbot certonly --manual \
--preferred-challenges dns \
-d "*.example.com" \
-d "example.com"
# certbot will prompt you to:
# 1. Create TXT record in DNS
# 2. Wait for propagation
# 3. Press Enter to continue
# DNS automation with plugins (if available)
# sudo certbot certonly --dns-route53 -d "*.example.com"
# (requires DNS provider plugin)
24.9 Where certmonger Fits
Use certmonger for Internal or Private CA Workflows
#============================================#
# CERTMONGER FOR IPA / INTERNAL CA WORKFLOWS
#============================================#
# Install certmonger
# RHEL 8/9/10
sudo dnf install certmonger -y
# RHEL 7
# sudo yum install certmonger -y
sudo systemctl enable --now certmonger
# Request an internal certificate from FreeIPA / IdM
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.example.com.crt \
-k /etc/pki/tls/private/internal.example.com.key \
-K HTTP/internal.example.com@REALM \
-D internal.example.com \
-C "systemctl reload httpd"
# Check status
sudo getcert list
Keep these workflows separate:
| Use Case | Recommended Tool |
|---|---|
| Public internet certificate from Let’s Encrypt | certbot |
| Internal service certificate from FreeIPA / IdM | certmonger with ipa-getcert |
| ACME against your own IdM ACME endpoint | certbot or another ACME client |
Important: IdM ACME, when enabled, is your own FreeIPA / IdM CA exposing an ACME endpoint. It is not Let’s Encrypt.
24.10 Troubleshooting certbot
Common Issues
Issue 1: Challenge Validation Failed
# Symptom
sudo certbot --apache -d example.com
# Error: Challenge validation failed
# Common causes:
# 1. Port 80 not accessible
curl http://example.com/.well-known/acme-challenge/test
# Should be accessible from internet
# 2. Firewall blocking
sudo firewall-cmd --list-services | grep http
# 3. DNS not resolving
nslookup example.com
# 4. Another service on port 80
ss -tlnp | grep :80
Issue 2: Renewal Failed
# Check renewal logs
sudo cat /var/log/letsencrypt/letsencrypt.log
# Common causes:
# - Port 80 blocked
# - DNS changed
# - Rate limit hit
# Test renewal manually
sudo certbot renew --dry-run
Issue 3: Permission Errors
# Fix certbot permissions
sudo chmod 0755 /etc/letsencrypt/{live,archive}
sudo chmod 0644 /etc/letsencrypt/live/*/fullchain.pem
sudo chmod 0600 /etc/letsencrypt/live/*/privkey.pem
24.11 Best Practices
certbot Best Practices
✅ **Use certbot for public-facing sites only**
✅ **Ensure port 80 accessible** (HTTP-01 challenge)
✅ **Test renewal regularly** (certbot renew --dry-run)
✅ **Monitor renewal timer** (systemctl status certbot-renew.timer)
✅ **Set up email notifications** for failures
✅ **Use renewal hooks** to reload services
✅ **Backup /etc/letsencrypt/** directory
✅ **Document EPEL dependency** in runbooks
✅ **Have fallback plan** if EPEL unavailable
✅ **For internal services, use certmonger + FreeIPA/local CA**
When to Use certbot
✅ Good Use Cases:
- Public-facing websites
- Development/staging environments
- Small deployments
- Cost-sensitive projects
- Quick HTTPS setup
❌ Consider Alternatives:
- Enterprise production (use FreeIPA)
- Internal-only services (use FreeIPA)
- Strict vendor support required (no EPEL)
- Air-gapped environments (no internet)
- Compliance requiring commercial CA
24.12 Alternative: FreeIPA for Internal
Comparison
For INTERNAL services:
# Instead of Let's Encrypt (public CA)
# Use FreeIPA (internal CA)
# FreeIPA advantages for internal:
✅ No internet dependency
✅ Red Hat supported
✅ Works offline
✅ No EPEL needed
✅ Integrated with RHEL
✅ Certificate profiles
✅ Centralized management
# Setup:
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.crt \
-k /etc/pki/tls/private/internal.key \
-K HTTP/$(hostname -f)@REALM \
-C "systemctl reload httpd"
# See Chapter 19 for FreeIPA details
24.13 Migration from certbot to certmonger
When Moving Internal Services to FreeIPA / IdM
If you are replacing public Let’s Encrypt certificates with internal PKI for non-public services, move the service to FreeIPA / IdM rather than trying to make certmonger talk directly to Let’s Encrypt.
#============================================#
# MIGRATE CERTBOT → CERTMONGER FOR INTERNAL PKI
#============================================#
# Step 1: Inventory existing certbot-managed certificates
sudo certbot certificates
# Step 2: Request the replacement internal certificate from IPA
sudo ipa-getcert request \
-f /etc/pki/tls/certs/internal.example.com.crt \
-k /etc/pki/tls/private/internal.example.com.key \
-K HTTP/internal.example.com@REALM \
-D internal.example.com \
-C "systemctl reload httpd"
# Step 3: Update Apache/NGINX config
# Change from /etc/letsencrypt/live/... to /etc/pki/tls/...
# Step 4: Reload and verify
sudo systemctl reload httpd
sudo getcert list
# Step 5: Disable certbot renewals only after all public certs are gone
sudo systemctl disable --now certbot-renew.timer
24.14 Rate Limits
Let’s Encrypt Limits
Be aware of rate limits:
| Limit Type | Value | Period |
|---|---|---|
| Certificates per domain | 50 | per week |
| Duplicate certificates | 5 | per week |
| Failed validations | 5 | per hour |
| New accounts | 10 | per IP per 3 hours |
Avoid hitting limits:
- ✅ Use –dry-run for testing
- ✅ Use staging environment first
- ✅ Don’t request same cert repeatedly
- ✅ Plan deployments carefully
Staging Environment:
# Test against staging (doesn't count against limits)
sudo certbot --apache \
-d test.example.com \
--test-cert # Uses staging environment
# When ready, get production cert:
sudo certbot --apache -d test.example.com
24.15 Complete Examples
Example 1: Apache with certbot
#!/bin/bash
# setup-apache-letsencrypt.sh
DOMAIN="www.example.com"
EMAIL="admin@example.com"
echo "=== Apache + Let's Encrypt Setup ==="
echo "⚠️ Requires EPEL repository"
# 1. Install Apache
sudo dnf install -y httpd
# 2. Enable EPEL
sudo dnf install -y epel-release
# 3. Install certbot
sudo dnf install -y certbot python3-certbot-apache
# 4. Ensure port 80 open
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
# 5. Start Apache
sudo systemctl enable --now httpd
# 6. Obtain certificate
sudo certbot --apache \
-d "$DOMAIN" \
--non-interactive \
--agree-tos \
--email "$EMAIL" \
--redirect
# 7. Verify
sudo certbot certificates
# 8. Test
curl -I https://$DOMAIN/
echo "✅ Apache + Let's Encrypt configured!"
echo "⚠️ Remember: certbot requires EPEL (community-supported)"
Example 2: NGINX with certbot
#!/bin/bash
# setup-nginx-letsencrypt.sh
DOMAIN="api.example.com"
EMAIL="admin@example.com"
echo "=== NGINX + Let's Encrypt Setup ==="
# 1. Install NGINX
sudo dnf install -y nginx
# 2. Install certbot
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-nginx
# 3. Create basic NGINX config
sudo tee /etc/nginx/conf.d/$DOMAIN.conf << EOF
server {
listen 80;
server_name $DOMAIN;
root /usr/share/nginx/html;
}
EOF
# 4. Start NGINX
sudo systemctl enable --now nginx
# 5. Obtain certificate
sudo certbot --nginx \
-d "$DOMAIN" \
--non-interactive \
--agree-tos \
--email "$EMAIL"
# 6. certbot automatically updates NGINX config!
# 7. Verify
curl -I https://$DOMAIN/
echo "✅ NGINX + Let's Encrypt configured!"
24.16 Backup and Restore
Backup Let’s Encrypt Certificates
#============================================#
# BACKUP CERTBOT/LETSENCRYPT
#============================================#
# Backup entire letsencrypt directory
sudo tar czf letsencrypt-backup-$(date +%Y%m%d).tar.gz \
/etc/letsencrypt/
# Store backup securely (contains private keys!)
# Restore
sudo tar xzf letsencrypt-backup-YYYYMMDD.tar.gz -C /
# Verify
sudo certbot certificates
24.17 Key Takeaways
- Let’s Encrypt provides free certificates for public domains
- certbot requires EPEL on ALL RHEL versions (not officially supported)
- certbot automates Apache/NGINX configuration
- 90-day validity requires automatic renewal
- certmonger remains the native choice for FreeIPA/internal CA workflows
- For internal services: Use FreeIPA instead
- Test with –dry-run to avoid rate limits
- Monitor renewal timer - check it’s running
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ LET'S ENCRYPT & CERTBOT QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ ⚠️ REQUIRES EPEL (community repository, not Red Hat!) │
│ │
│ Install: dnf install epel-release │
│ dnf install certbot python3-certbot-apache │
│ │
│ Apache: certbot --apache -d example.com │
│ NGINX: certbot --nginx -d example.com │
│ Standalone: certbot certonly --standalone -d example.com │
│ │
│ Test: certbot renew --dry-run │
│ Renew: certbot renew (automatic via timer) │
│ List: certbot certificates │
│ │
│ Certs: /etc/letsencrypt/live/<domain>/ │
│ fullchain.pem, privkey.pem │
│ │
│ Timer: systemctl status certbot-renew.timer │
│ Logs: /var/log/letsencrypt/letsencrypt.log │
│ │
│ Alternative: certmonger + FreeIPA / internal CA │
│ ipa-getcert request ... │
└──────────────────────────────────────────────────────────────┘
⚠️ certbot NOT available in official RHEL repos
⚠️ EPEL is community-supported, not Red Hat supported
✅ For enterprise: Consider FreeIPA + certmonger (Chapter 19)
✅ Use certmonger for FreeIPA / internal CA renewals
🧪 Hands-On Lab
Lab 13: Let’s Encrypt & Certbot
Obtain and auto-renew Let’s Encrypt certificates
- 📁 Location:
labs/en_US/13-letsencrypt-certbot/ - ⏱️ Time: 30-40 minutes
- 🎯 Level: Intermediate
Chapter Navigation
| ← Previous: Chapter 23 - Crypto-Policies Deep Dive | Next: Chapter 25 - Ansible Automation for Certificates → |
|---|
Chapter 25: Ansible Automation for Certificates
Scale Up: Manage certificates across hundreds of RHEL systems with Ansible. Automate deployment, renewal, and monitoring at enterprise scale.
25.1 Why Ansible for Certificates?
Manual Management:
❌ SSH to 100 servers individually
❌ Copy certificates one by one
❌ Configure services manually
❌ Track renewals per server
❌ Hope you didn't miss any
Ansible Automation:
✅ Deploy certificates to 100 servers in minutes
✅ Consistent configuration
✅ Idempotent (safe to run repeatedly)
✅ Version controlled (Git)
✅ Auditable (who changed what when)
✅ Rollback capable
25.2 Prerequisites
Install Ansible on RHEL
#============================================#
# INSTALL ANSIBLE
#============================================#
# RHEL 8/9/10
sudo dnf install ansible-core -y
# Or from Ansible repository for latest
sudo dnf install epel-release # If using EPEL
sudo dnf install ansible -y
# Verify
ansible --version
# Install community.crypto collection (ESSENTIAL!)
ansible-galaxy collection install community.crypto
25.3 Ansible Inventory for Certificate Management
Example Inventory
#============================================#
# inventory/hosts.ini
#============================================#
[webservers]
web01.example.com
web02.example.com
web03.example.com
[mailservers]
mail01.example.com
[databases]
db01.example.com
db02.example.com
[all:vars]
ansible_user=ansible
ansible_become=yes
ansible_python_interpreter=/usr/bin/python3
25.4 Generate Certificates with Ansible
Playbook: Generate Private Keys
#============================================#
# playbooks/generate-keys.yml
#============================================#
---
- name: Generate Private Keys for RHEL Servers
hosts: webservers
become: yes
tasks:
- name: Install OpenSSL
dnf:
name: openssl
state: present
- name: Generate private key
community.crypto.openssl_privatekey:
path: "/etc/pki/tls/private/{{ inventory_hostname }}.key"
size: 2048
type: RSA
mode: '0600'
owner: root
group: root
- name: Verify key generated
stat:
path: "/etc/pki/tls/private/{{ inventory_hostname }}.key"
register: key_file
- name: Show result
debug:
msg: "Key generated: {{ key_file.stat.exists }}"
Playbook: Generate CSRs
#============================================#
# playbooks/generate-csrs.yml
#============================================#
---
- name: Generate Certificate Signing Requests
hosts: webservers
become: yes
tasks:
- name: Generate CSR
community.crypto.openssl_csr:
path: "/tmp/{{ inventory_hostname }}.csr"
privatekey_path: "/etc/pki/tls/private/{{ inventory_hostname }}.key"
common_name: "{{ inventory_hostname }}"
subject_alt_name:
- "DNS:{{ inventory_hostname }}"
- "DNS:{{ inventory_hostname_short }}"
key_usage:
- digitalSignature
- keyEncipherment
extended_key_usage:
- serverAuth
organization_name: "Example Company"
country_name: "US"
- name: Fetch CSR to control node
fetch:
src: "/tmp/{{ inventory_hostname }}.csr"
dest: "csrs/{{ inventory_hostname }}.csr"
flat: yes
25.5 Deploy Certificates with Ansible
Playbook: Deploy Certificates to Apache
#============================================#
# playbooks/deploy-apache-certs.yml
#============================================#
---
- name: Deploy Certificates to Apache Servers
hosts: webservers
become: yes
vars:
cert_source_dir: "/path/to/certificates"
tasks:
- name: Install Apache and mod_ssl
dnf:
name:
- httpd
- mod_ssl
state: present
- name: Deploy certificate
copy:
src: "{{ cert_source_dir }}/{{ inventory_hostname }}.crt"
dest: "/etc/pki/tls/certs/{{ inventory_hostname }}.crt"
mode: '0644'
owner: root
group: root
notify: reload apache
- name: Deploy private key
copy:
src: "{{ cert_source_dir }}/{{ inventory_hostname }}.key"
dest: "/etc/pki/tls/private/{{ inventory_hostname }}.key"
mode: '0600'
owner: root
group: root
no_log: yes # Don't log private key
notify: reload apache
- name: Configure Apache SSL
template:
src: templates/ssl.conf.j2
dest: /etc/httpd/conf.d/ssl.conf
mode: '0644'
notify: reload apache
- name: Ensure Apache is running
service:
name: httpd
state: started
enabled: yes
handlers:
- name: reload apache
service:
name: httpd
state: reloaded
25.6 Certificate Validation with Ansible
Playbook: Validate Certificates
#============================================#
# playbooks/validate-certificates.yml
#============================================#
---
- name: Validate Certificates on RHEL Servers
hosts: all
become: yes
tasks:
- name: Find all certificates
find:
paths: /etc/pki/tls/certs/
patterns: '*.crt'
register: certificates
- name: Check certificate expiration
community.crypto.x509_certificate_info:
path: "{{ item.path }}"
register: cert_info
loop: "{{ certificates.files }}"
loop_control:
label: "{{ item.path }}"
- name: Identify expiring certificates (30 days)
set_fact:
expiring_certs: "{{ expiring_certs | default([]) + [item.item.path] }}"
when:
- (item.not_after | to_datetime('%Y%m%d%H%M%SZ')) - (ansible_date_time.iso8601 | to_datetime) < '30 days'
loop: "{{ cert_info.results }}"
loop_control:
label: "{{ item.item.path }}"
- name: Report expiring certificates
debug:
msg: "⚠️ Certificate expiring soon: {{ item }}"
loop: "{{ expiring_certs | default([]) }}"
when: expiring_certs is defined
25.7 certmonger Management with Ansible
Playbook: Setup certmonger Tracking
#============================================#
# playbooks/setup-certmonger.yml
#============================================#
---
- name: Configure certmonger Tracking
hosts: webservers
become: yes
tasks:
- name: Install certmonger
dnf:
name: certmonger
state: present
- name: Ensure certmonger is running
service:
name: certmonger
state: started
enabled: yes
- name: Request certificate from FreeIPA
command: >
ipa-getcert request
-f /etc/pki/tls/certs/{{ inventory_hostname }}.crt
-k /etc/pki/tls/private/{{ inventory_hostname }}.key
-K HTTP/{{ inventory_hostname }}@EXAMPLE.COM
-D {{ inventory_hostname }}
-C "systemctl reload httpd"
args:
creates: /etc/pki/tls/certs/{{ inventory_hostname }}.crt
- name: Check certmonger status
command: getcert list
register: cert_status
changed_when: false
- name: Display status
debug:
var: cert_status.stdout_lines
25.8 Certificate Monitoring with Ansible
Playbook: Certificate Expiration Report
#============================================#
# playbooks/cert-expiration-report.yml
#============================================#
---
- name: Generate Certificate Expiration Report
hosts: all
become: yes
gather_facts: yes
tasks:
- name: Get certificate expiration for Apache
shell: |
if [ -f /etc/pki/tls/certs/{{ inventory_hostname }}.crt ]; then
openssl x509 -in /etc/pki/tls/certs/{{ inventory_hostname }}.crt -noout -enddate | cut -d= -f2
else
echo "No certificate found"
fi
register: cert_expiry
changed_when: false
- name: Calculate days until expiration
set_fact:
days_left: "{{ ((cert_expiry.stdout | to_datetime('%b %d %H:%M:%S %Y %Z')) - (ansible_date_time.iso8601 | to_datetime)).days }}"
when: cert_expiry.stdout != "No certificate found"
- name: Add to report
set_fact:
cert_report: |
{{ inventory_hostname }},{{ cert_expiry.stdout }},{{ days_left | default('N/A') }}
delegate_to: localhost
delegate_facts: yes
- name: Save report
copy:
content: "{{ hostvars | dict2items | map(attribute='value.cert_report') | join('\n') }}"
dest: "/tmp/cert-expiration-report.csv"
delegate_to: localhost
run_once: yes
25.9 Complete Certificate Deployment Workflow
End-to-End Automation
#============================================#
# playbooks/full-cert-deployment.yml
#============================================#
---
- name: Complete Certificate Deployment
hosts: webservers
become: yes
vars:
cert_domain: "example.com"
cert_base_path: "/etc/pki/tls"
pre_tasks:
- name: Gather facts
setup:
tasks:
# 1. Ensure directories exist
- name: Ensure certificate directories exist
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ cert_base_path }}/certs"
- "{{ cert_base_path }}/private"
# 2. Generate private key (if not exists)
- name: Generate private key
community.crypto.openssl_privatekey:
path: "{{ cert_base_path }}/private/{{ inventory_hostname }}.key"
size: 2048
mode: '0600'
# 3. Deploy certificate (from controller)
- name: Deploy certificate
copy:
src: "files/certs/{{ inventory_hostname }}.crt"
dest: "{{ cert_base_path }}/certs/{{ inventory_hostname }}.crt"
mode: '0644'
notify: reload httpd
# 4. Deploy CA bundle
- name: Deploy CA bundle
copy:
src: "files/ca-bundle.crt"
dest: "{{ cert_base_path }}/certs/ca-bundle.crt"
mode: '0644'
# 5. Configure Apache
- name: Deploy Apache SSL configuration
template:
src: templates/apache-ssl.conf.j2
dest: /etc/httpd/conf.d/ssl.conf
mode: '0644'
notify: reload httpd
# 6. Validate configuration
- name: Test Apache configuration
command: apachectl configtest
changed_when: false
# 7. Ensure firewall open
- name: Open HTTPS in firewall
firewalld:
service: https
permanent: yes
state: enabled
immediate: yes
# 8. Ensure Apache running
- name: Ensure Apache is running
service:
name: httpd
state: started
enabled: yes
handlers:
- name: reload httpd
service:
name: httpd
state: reloaded
25.10 Ansible Roles for Certificates
Create Reusable Role
#============================================#
# CREATE ANSIBLE ROLE FOR CERTIFICATES
#============================================#
# Create role structure
ansible-galaxy role init certificates
# Directory structure:
certificates/
├── defaults/
│ └── main.yml # Default variables
├── files/
│ └── ca-bundle.crt # CA certificates
├── handlers/
│ └── main.yml # Service reload handlers
├── tasks/
│ └── main.yml # Main tasks
├── templates/
│ └── ssl.conf.j2 # Config templates
└── vars/
└── main.yml # Variables
Example Role Tasks
#============================================#
# roles/certificates/tasks/main.yml
#============================================#
---
- name: Install certificate management tools
dnf:
name:
- openssl
- certmonger
state: present
- name: Ensure certificate directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /etc/pki/tls/certs
- /etc/pki/tls/private
- name: Deploy certificates
include_tasks: deploy-cert.yml
loop: "{{ certificates }}"
loop_control:
loop_var: cert
- name: Setup certmonger tracking
include_tasks: setup-certmonger.yml
when: use_certmonger | default(false)
Use the Role
#============================================#
# playbook.yml
#============================================#
---
- name: Deploy Certificates
hosts: webservers
become: yes
roles:
- role: certificates
vars:
certificates:
- name: "{{ inventory_hostname }}"
service: httpd
principal: "HTTP/{{ inventory_hostname }}@REALM"
25.11 Best Practices
Ansible Certificate Management Best Practices
✅ **Use community.crypto collection** for certificate tasks
✅ **Vault-encrypt private keys** (ansible-vault)
✅ **Use no_log for sensitive data** (keys, passwords)
✅ **Test on staging first** before production
✅ **Use handlers** for service reloads (avoid unnecessary restarts)
✅ **Make playbooks idempotent** (safe to run multiple times)
✅ **Version control** playbooks in Git
✅ **Document variables** and requirements
✅ **Tag tasks** for selective execution
✅ **Use roles** for reusability
Security Considerations
# Encrypt private keys with ansible-vault
ansible-vault encrypt files/private-keys/*.key
# Use no_log for sensitive tasks
- name: Deploy private key
copy:
src: "{{ key_file }}"
dest: "/etc/pki/tls/private/server.key"
no_log: yes
# Use vault for passwords
# group_vars/all/vault.yml (encrypted)
admin_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
...
25.12 Complete Examples
Example 1: Deploy CA to All Servers
---
- name: Deploy Corporate CA to All RHEL Servers
hosts: all
become: yes
tasks:
- name: Copy CA certificate
copy:
src: files/corporate-ca.crt
dest: /etc/pki/ca-trust/source/anchors/corporate-ca.crt
mode: '0644'
- name: Update CA trust
command: update-ca-trust extract
changed_when: true
- name: Verify CA installed
command: trust list
register: trust_list
changed_when: false
- name: Confirm CA present
assert:
that:
- "'Corporate CA' in trust_list.stdout"
fail_msg: "Corporate CA not in trust store!"
Example 2: Mass Certificate Renewal Check
---
- name: Check Certificate Expiration Across Fleet
hosts: all
become: yes
tasks:
- name: Check certmonger status
command: getcert list
register: certmonger_status
changed_when: false
failed_when: false
- name: Check for CA_UNREACHABLE
set_fact:
has_issue: true
when: "'CA_UNREACHABLE' in certmonger_status.stdout"
- name: Report problems
debug:
msg: "⚠️ {{ inventory_hostname }} has CA_UNREACHABLE certificates!"
when: has_issue | default(false)
- name: Create problem report
lineinfile:
path: "/tmp/cert-problems.txt"
line: "{{ inventory_hostname }}: CA_UNREACHABLE"
create: yes
delegate_to: localhost
when: has_issue | default(false)
25.13 Key Takeaways
- Ansible enables mass certificate deployment
- community.crypto collection essential for certificate tasks
- Use ansible-vault for private keys
- Idempotent playbooks are critical
- Test on staging before production
- Combine with certmonger for best results
- Version control everything in Git
Quick Reference Card
┌────────────────────────────────────────────────────────────────────┐
│ ANSIBLE CERTIFICATE AUTOMATION QUICK REFERENCE │
├────────────────────────────────────────────────────────────────────┤
│ Install: dnf install ansible-core │
│ Collection: ansible-galaxy collection install community.crypto │
│ │
│ Generate key: community.crypto.openssl_privatekey │
│ Generate CSR: community.crypto.openssl_csr │
│ Get cert info: community.crypto.x509_certificate_info │
│ │
│ Deploy cert: copy module (with mode: '0644') │
│ Deploy key: copy module (with mode: '0600', no_log: yes) │
│ │
│ Vault: ansible-vault encrypt <file> │
│ ansible-vault decrypt <file> │
│ │
│ Run: ansible-playbook playbook.yml │
│ Dry run: ansible-playbook playbook.yml --check │
│ Specific: ansible-playbook playbook.yml --tags certs │
└────────────────────────────────────────────────────────────────────┘
✅ Use community.crypto collection
✅ Encrypt private keys with ansible-vault
✅ Use no_log for sensitive data
🧪 Hands-On Lab
Lab 14: Ansible Automation
Automate certificate deployment with Ansible
- 📁 Location:
labs/en_US/14-ansible-automation/ - ⏱️ Time: 40-50 minutes
- 🎯 Level: Advanced
Chapter Navigation
Chapter 26: Monitoring & Alerting on RHEL
Prevent Outages: Proactive monitoring prevents certificate expiration surprises. Learn how to monitor certificates and set up alerts on RHEL.
26.1 Why Monitor Certificates?
Without Monitoring:
❌ Certificate expires
❌ Website goes down at 2 AM
❌ Emergency incident
❌ Revenue loss
❌ Reputation damage
❌ Stressful night for ops team
With Monitoring:
✅ Alert 30 days before expiry
✅ Planned renewal during business hours
✅ No outages
✅ No emergencies
✅ Happy customers
✅ Well-rested ops team
26.2 What to Monitor
Certificate Metrics
## Critical Metrics:
✅ **Expiration date** (days until expiry)
✅ **Certificate validity** (not yet valid, expired)
✅ **Certificate/key pair match**
✅ **Trust chain validity**
✅ **certmonger status** (if used)
✅ **Service health** (using the certificate)
✅ **Renewal success/failure**
## Warning Thresholds:
🟡 60 days: First warning
🟠 30 days: Second warning
🔴 15 days: Critical alert
🚨 7 days: Emergency escalation
26.3 Simple Monitoring Scripts
Basic Expiration Check
#!/bin/bash
# check-cert-expiration.sh
# Simple certificate expiration checker
WARN_DAYS=30
CRIT_DAYS=7
check_cert() {
local cert=$1
local name=$(basename "$cert")
if [ ! -f "$cert" ]; then
echo "❌ $name: File not found"
return 2
fi
# Get expiration date
expiry=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$expiry" ]; then
echo "❌ $name: Invalid certificate"
return 2
fi
# Calculate days remaining
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null)
now_epoch=$(date +%s)
days_left=$(( ($expiry_epoch - $now_epoch) / 86400 ))
# Check thresholds
if [ $days_left -lt 0 ]; then
echo "🚨 $name: EXPIRED $((- days_left)) days ago!"
return 2
elif [ $days_left -lt $CRIT_DAYS ]; then
echo "🔴 $name: CRITICAL - $days_left days left"
return 2
elif [ $days_left -lt $WARN_DAYS ]; then
echo "🟡 $name: WARNING - $days_left days left"
return 1
else
echo "✅ $name: OK - $days_left days left"
return 0
fi
}
# Check all certificates
for cert in /etc/pki/tls/certs/*.crt; do
check_cert "$cert"
done
certmonger Status Monitor
#!/bin/bash
# monitor-certmonger.sh
# Monitor certmonger tracking status
echo "=== certmonger Status Monitor ==="
# Check if certmonger is running
if ! systemctl is-active --quiet certmonger; then
echo "🚨 CRITICAL: certmonger is not running!"
systemctl status certmonger
exit 2
fi
# Get certmonger status
STATUS_OUTPUT=$(sudo getcert list 2>&1)
# Count certificates by status
TOTAL=$(echo "$STATUS_OUTPUT" | grep -c "Request ID")
MONITORING=$(echo "$STATUS_OUTPUT" | grep -c "status: MONITORING")
UNREACHABLE=$(echo "$STATUS_OUTPUT" | grep -c "status: CA_UNREACHABLE")
REJECTED=$(echo "$STATUS_OUTPUT" | grep -c "status: CA_REJECTED")
echo "Total certificates: $TOTAL"
echo " MONITORING: $MONITORING ✅"
echo " CA_UNREACHABLE: $UNREACHABLE $([ $UNREACHABLE -gt 0 ] && echo '⚠️')"
echo " CA_REJECTED: $REJECTED $([ $REJECTED -gt 0 ] && echo '❌')"
# Alert if problems
if [ $UNREACHABLE -gt 0 ] || [ $REJECTED -gt 0 ]; then
echo ""
echo "🚨 ATTENTION REQUIRED:"
sudo getcert list | grep -B5 "status: CA_" | grep -E "(Request ID|status:)"
exit 1
fi
echo "✅ All certificates OK"
26.4 Systemd Timer for Monitoring
Create Monitoring Timer
#============================================#
# CREATE SYSTEMD TIMER FOR MONITORING
#============================================#
# Create service file
sudo tee /etc/systemd/system/cert-monitor.service << 'EOF'
[Unit]
Description=Certificate Expiration Monitor
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/check-cert-expiration.sh
StandardOutput=journal
StandardError=journal
EOF
# Create timer file (run daily)
sudo tee /etc/systemd/system/cert-monitor.timer << 'EOF'
[Unit]
Description=Daily Certificate Monitoring
Requires=cert-monitor.service
[Timer]
OnCalendar=daily
OnBootSec=15min
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Install monitoring script
sudo cp check-cert-expiration.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/check-cert-expiration.sh
# Enable timer
sudo systemctl daemon-reload
sudo systemctl enable cert-monitor.timer
sudo systemctl start cert-monitor.timer
# Verify
systemctl list-timers | grep cert-monitor
# Test manually
sudo systemctl start cert-monitor.service
sudo journalctl -u cert-monitor.service
26.5 Email Alerts
Simple Email Alerting
#!/bin/bash
# cert-monitor-with-email.sh
# Certificate monitor with email alerts
EMAIL="admin@example.com"
WARN_DAYS=30
ALERTS=""
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
if ! openssl x509 -in "$cert" -noout -checkend $((86400*WARN_DAYS)) 2>/dev/null; then
expiry=$(openssl x509 -in "$cert" -noout -enddate | cut -d= -f2)
ALERTS="$ALERTS\nCertificate: $cert\nExpires: $expiry\n"
fi
done
if [ -n "$ALERTS" ]; then
echo -e "Certificate Expiration Warnings:\n$ALERTS" | \
mail -s "⚠️ Certificate Expiration Alert - $(hostname)" "$EMAIL"
fi
26.6 Prometheus Monitoring (Advanced)
Certificate Exporter
#============================================#
# PROMETHEUS CERTIFICATE MONITORING
#============================================#
# Install x509-certificate-exporter (example)
# https://github.com/enix/x509-certificate-exporter
# Or use Node Exporter textfile collector
# Create metrics collector script
cat > /usr/local/bin/cert-metrics.sh << 'EOF'
#!/bin/bash
# Generate Prometheus metrics for certificates
METRICS_FILE="/var/lib/node_exporter/textfile_collector/certificates.prom"
mkdir -p $(dirname "$METRICS_FILE")
cat > "$METRICS_FILE" << PROM
# HELP certificate_expiry_days Days until certificate expiration
# TYPE certificate_expiry_days gauge
PROM
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
expiry=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null)
now_epoch=$(date +%s)
days_left=$(( ($expiry_epoch - $now_epoch) / 86400 ))
echo "certificate_expiry_days{cert=\"$cert\",hostname=\"$(hostname)\"} $days_left" >> "$METRICS_FILE"
done
EOF
chmod +x /usr/local/bin/cert-metrics.sh
# Run via cron
echo "*/5 * * * * /usr/local/bin/cert-metrics.sh" | sudo crontab -
Prometheus Alert Rules
#============================================#
# prometheus-alerts.yml
#============================================#
groups:
- name: certificates
rules:
- alert: CertificateExpiringSoon
expr: certificate_expiry_days < 30
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate expiring in {{ $value }} days"
description: "Certificate {{ $labels.cert }} on {{ $labels.hostname }} expires in {{ $value }} days"
- alert: CertificateExpiryCritical
expr: certificate_expiry_days < 7
for: 5m
labels:
severity: critical
annotations:
summary: "Certificate expiring in {{ $value }} days!"
description: "URGENT: Certificate {{ $labels.cert }} on {{ $labels.hostname }} expires in {{ $value }} days!"
- alert: CertificateExpired
expr: certificate_expiry_days < 0
for: 1m
labels:
severity: critical
annotations:
summary: "Certificate EXPIRED!"
description: "Certificate {{ $labels.cert }} on {{ $labels.hostname }} has expired!"
26.7 Centralized Logging
Send Certificate Events to Syslog
#============================================#
# LOG CERTIFICATE EVENTS
#============================================#
# certmonger logs to journal
sudo journalctl -u certmonger -f
# Forward to central syslog
# /etc/rsyslog.conf
*.* @@syslog-server.example.com:514
# Or configure specific certmonger logging
# Monitor certmonger renewals
sudo journalctl -u certmonger --since today | grep -i "renewed\|failed"
26.8 Monitoring Tools Comparison
Options for RHEL
| Tool | Complexity | Cost | Integration | Alerting |
|---|---|---|---|---|
| Simple scripts | Low | Free | Easy | Email/syslog |
| Nagios/Icinga | Medium | Free | Good | Multiple |
| Prometheus + Grafana | Medium-High | Free | Excellent | Powerful |
| Zabbix | Medium | Free | Good | Multiple |
| Commercial (Datadog, etc.) | Low | $$$ | Excellent | Advanced |
| Red Hat Insights | Low | Subscription | Native | Dashboard |
Recommendation for RHEL:
- Small: Simple scripts + email
- Medium: Prometheus + Grafana
- Enterprise: Commercial or Red Hat Insights
26.9 Complete Monitoring Solution
Comprehensive Monitoring Script
#!/bin/bash
# comprehensive-cert-monitor.sh
# Complete certificate monitoring for RHEL
WARN_DAYS=30
CRIT_DAYS=7
EMAIL="admin@example.com"
LOG_FILE="/var/log/cert-monitor.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
send_alert() {
local subject=$1
local body=$2
echo "$body" | mail -s "$subject" "$EMAIL"
logger -t cert-monitor "$subject"
}
log "=== Starting Certificate Monitor ==="
# Check 1: certmonger running?
if ! systemctl is-active --quiet certmonger; then
send_alert "🚨 certmonger NOT running on $(hostname)" \
"certmonger service is not running. Certificate renewals may fail!"
fi
# Check 2: certmonger status
UNREACHABLE=$(sudo getcert list | grep -c "CA_UNREACHABLE")
if [ $UNREACHABLE -gt 0 ]; then
send_alert "⚠️ CA unreachable for $UNREACHABLE certificates on $(hostname)" \
"$(sudo getcert list | grep -B5 'CA_UNREACHABLE')"
fi
# Check 3: Certificate expiration
CRITICAL=0
WARNING=0
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
expiry=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
[ -z "$expiry" ] && continue
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null)
now_epoch=$(date +%s)
days_left=$(( ($expiry_epoch - $now_epoch) / 86400 ))
if [ $days_left -lt 0 ]; then
log "🚨 EXPIRED: $cert"
((CRITICAL++))
elif [ $days_left -lt $CRIT_DAYS ]; then
log "🔴 CRITICAL ($days_left days): $cert"
((CRITICAL++))
elif [ $days_left -lt $WARN_DAYS ]; then
log "🟡 WARNING ($days_left days): $cert"
((WARNING++))
else
log "✅ OK ($days_left days): $cert"
fi
done
# Send summary alert if issues
if [ $CRITICAL -gt 0 ] || [ $WARNING -gt 0 ]; then
SUMMARY="Critical: $CRITICAL, Warning: $WARNING\n\n$(tail -20 $LOG_FILE)"
send_alert "Certificate Alert: $(hostname)" "$SUMMARY"
fi
log "=== Monitor Complete: Critical=$CRITICAL, Warning=$WARNING ==="
26.10 Monitoring with Grafana
Dashboard Example
{
"dashboard": {
"title": "RHEL Certificate Monitoring",
"panels": [
{
"title": "Certificates Expiring Soon",
"targets": [
{
"expr": "certificate_expiry_days < 30"
}
]
},
{
"title": "certmonger Status",
"targets": [
{
"expr": "certmonger_status != 'MONITORING'"
}
]
}
]
}
}
26.11 Key Takeaways
- Monitor proactively - Don’t wait for expiration
- 30-day warning minimum recommended
- certmonger status critical if using automation
- Multiple alert channels (email, Slack, PagerDuty)
- Test monitoring - Ensure alerts actually reach you
- Log everything for audit trail
- Automate remediation where possible
Quick Reference Card
┌─────────────────────────────────────────────────────────────────┐
│ CERTIFICATE MONITORING QUICK REFERENCE │
├─────────────────────────────────────────────────────────────────┤
│ Check expiry: openssl x509 -in cert.crt -noout -checkend 86400 │
│ Days left: openssl x509 -in cert.crt -noout -enddate │
│ │
│ certmonger: getcert list │
│ Status: getcert list | grep "status:" │
│ Logs: journalctl -u certmonger │
│ │
│ Alert levels: 60 days (info) │
│ 30 days (warning) │
│ 7 days (critical) │
│ 0 days (emergency!) │
│ │
│ Tools: Simple scripts, Prometheus, Nagios, Zabbix │
│ Native: certmonger built-in tracking │
└─────────────────────────────────────────────────────────────────┘
✅ Monitor == No Surprises!
✅ Automate monitoring with systemd timers or cron
Chapter Navigation
| ← Previous: Chapter 25 - Ansible Automation for Certificates | Next: Chapter 27 - RHEL Certificate Troubleshooting Methodology → |
|---|
Chapter 27: RHEL Certificate Troubleshooting Methodology
Critical Skill: This chapter teaches you a systematic approach to troubleshoot ANY certificate issue on RHEL systems. Master this methodology and you’ll solve problems in minutes instead of hours.
27.1 The Problem
Certificate issues are frustrating:
- Error messages are cryptic
- Root causes are hidden
- Multiple layers involved (OpenSSL, service config, SELinux, crypto-policies)
- Varies by RHEL version
Random troubleshooting doesn’t work. You need a system.
27.2 The Systematic Approach
Follow this 7-step methodology for EVERY certificate issue:
Step 1: Identify RHEL Version & Environment
Step 2: Verify Basic Certificate Properties
Step 3: Check Trust Chain & CA Validation
Step 4: Verify Service Configuration
Step 5: Check System-Level Settings
Step 6: Test Certificate Functionality
Step 7: Review Logs & Error Details
Rule: Never skip steps. Each provides critical diagnostic information.
27.3 Step 1: Identify RHEL Version & Environment
Why It Matters
Certificate behavior differs significantly across RHEL versions.
Quick Checks
#============================================#
# RHEL VERSION CHECK
#============================================#
# 1. Check RHEL version
cat /etc/redhat-release
# Output example: Red Hat Enterprise Linux release 9.8 (Plow)
# 2. Check OpenSSL version
openssl version
# RHEL 7: OpenSSL 1.0.2k
# RHEL 8: OpenSSL 1.1.1k
# RHEL 9/10: OpenSSL 3.5.5
# 3. Check crypto-policy (RHEL 8+)
update-crypto-policies --show 2>/dev/null
# DEFAULT, LEGACY, FUTURE, or FIPS
# 4. Check FIPS mode
fips-mode-setup --check 2>/dev/null
# FIPS mode is enabled/disabled
# 5. Check SELinux status
getenforce
# Enforcing, Permissive, or Disabled
What to Document
Create a troubleshooting note with:
RHEL Version: _______
OpenSSL Version: _______
Crypto-Policy: _______ (if RHEL 8+)
FIPS Mode: _______
SELinux: _______
Service: _______ (Apache, NGINX, Postfix, etc.)
27.4 Step 2: Verify Basic Certificate Properties
The Five Essential Checks
#============================================#
# CHECK 1: Certificate Expiration
#============================================#
# View certificate dates
openssl x509 -in /path/to/cert.crt -noout -dates
# Output:
# notBefore=Jan 1 00:00:00 2024 GMT
# notAfter=Jan 1 23:59:59 2025 GMT ← Must be in the future!
# Quick check if expired
openssl x509 -in /path/to/cert.crt -noout -checkend 0
# Exit 0 = valid, Exit 1 = expired
# Check expiration in X days
openssl x509 -in /path/to/cert.crt -noout -checkend $((86400*30))
# Check if expires in next 30 days
#============================================#
# CHECK 2: Certificate Subject/Hostname
#============================================#
# View subject (who certificate is for)
openssl x509 -in /path/to/cert.crt -noout -subject
# subject=CN=server.example.com
# View Subject Alternative Names (SANs) - CRITICAL!
openssl x509 -in /path/to/cert.crt -noout -ext subjectAltName
# X509v3 Subject Alternative Name:
# DNS:server.example.com, DNS:www.example.com
# ⚠️ Modern browsers REQUIRE SANs (CN alone is insufficient)
#============================================#
# CHECK 3: Certificate/Key Pair Match
#============================================#
# Get certificate modulus
openssl x509 -noout -modulus -in /path/to/cert.crt | openssl md5
# Get key modulus
openssl rsa -noout -modulus -in /path/to/cert.key | openssl md5
# ✅ If MD5 hashes match → cert and key are paired
# ❌ If different → WRONG KEY!
#============================================#
# CHECK 4: Certificate Issuer
#============================================#
# View who signed this certificate
openssl x509 -in /path/to/cert.crt -noout -issuer
# issuer=C=US, O=Let's Encrypt, CN=R3
# Self-signed? (subject == issuer)
openssl x509 -in /path/to/cert.crt -noout -subject -issuer | sort | uniq -d
# If output is not empty → self-signed
#============================================#
# CHECK 5: Certificate Algorithm & Key Size
#============================================#
# View signature algorithm
openssl x509 -in /path/to/cert.crt -noout -text | grep "Signature Algorithm"
# Signature Algorithm: sha256WithRSAEncryption ← Good
# Signature Algorithm: sha1WithRSAEncryption ← Bad (deprecated)
# View public key size
openssl x509 -in /path/to/cert.crt -noout -text | grep "Public-Key"
# Public-Key: (2048 bit) ← Minimum
# Public-Key: (4096 bit) ← Better
# ⚠️ RHEL 8+ rejects < 2048 bit keys by default
# ⚠️ RHEL 9+ rejects SHA-1 signatures by default
Quick Validation Script
#!/bin/bash
# quick-cert-check.sh
CERT=$1
echo "=== Certificate Quick Check ==="
echo ""
echo "File: $CERT"
echo ""
echo "1. Expiration:"
openssl x509 -in "$CERT" -noout -dates
echo ""
echo "2. Subject:"
openssl x509 -in "$CERT" -noout -subject
echo ""
echo "3. SANs:"
openssl x509 -in "$CERT" -noout -ext subjectAltName 2>/dev/null || echo "No SANs found"
echo ""
echo "4. Issuer:"
openssl x509 -in "$CERT" -noout -issuer
echo ""
echo "5. Algorithm & Key:"
openssl x509 -in "$CERT" -noout -text | grep -E "(Signature Algorithm|Public-Key)"
echo ""
echo "6. Still valid?"
if openssl x509 -in "$CERT" -noout -checkend 0 >/dev/null 2>&1; then
echo "✅ Certificate is valid"
else
echo "❌ Certificate has expired!"
fi
Usage:
bash quick-cert-check.sh /etc/pki/tls/certs/server.crt
27.5 Step 3: Check Trust Chain & CA Validation
Understanding the Chain
Root CA (must be trusted by system)
└─ Intermediate CA(s)
└─ Server Certificate (your certificate)
Verify Trust Chain
#============================================#
# CHECK COMPLETE CERTIFICATE CHAIN
#============================================#
# Method 1: Verify against system CA bundle
openssl verify /path/to/cert.crt
# /path/to/cert.crt: OK ← Good!
# error 20: unable to get local issuer certificate ← Missing CA!
# Method 2: Verify with specific CA file
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt /path/to/cert.crt
# Method 3: Show full chain
openssl s_client -connect server.example.com:443 -showcerts
#============================================#
# CHECK IF CA IS TRUSTED BY RHEL
#============================================#
# List all trusted CAs
trust list | grep -i "certificate-authority"
# Search for specific CA
trust list | grep -i "Let's Encrypt"
# Check if specific CA file is trusted
trust list --filter=ca-anchors | grep -A5 "pkcs11"
#============================================#
# VIEW CERTIFICATE CHAIN
#============================================#
# Extract and view full chain from server
openssl s_client -connect server.example.com:443 -showcerts 2>/dev/null | \
awk '/BEGIN CERT/,/END CERT/ {print}'
# View chain from file (if bundled)
openssl crl2pkcs7 -nocrl -certfile /path/to/chain.crt | \
openssl pkcs7 -print_certs -text -noout
#============================================#
# CHECK INTERMEDIATE CERTIFICATES
#============================================#
# Common issue: Missing intermediate certificate!
# Server should send: [Server Cert] → [Intermediate] → [Root]
# But only sends: [Server Cert]
# Result: Client can't validate chain!
# Test from another machine
echo | openssl s_client -connect server.example.com:443 -servername server.example.com 2>&1 | \
grep -E "(verify return code|Verify return code)"
# Verify return code: 0 (ok) ← Good
# Verify return code: 21 (unable to verify the first certificate) ← Missing intermediate!
Common Trust Issues
| Error Code | Meaning | Solution |
|---|---|---|
| 0 | OK | ✅ No issues |
| 19 | Self-signed certificate in chain | Add CA to trust store |
| 20 | Unable to get local issuer cert | Missing CA or intermediate |
| 21 | Unable to verify first certificate | Missing intermediate cert |
| 27 | Certificate not trusted | CA not in system trust store |
27.6 Step 4: Verify Service Configuration
Service-Specific Checks
#============================================#
# APACHE (httpd)
#============================================#
# Check SSL configuration
sudo apachectl -t -D DUMP_VHOSTS | grep -A5 ":443"
# View SSL cert paths
sudo grep -r "SSLCertificateFile\|SSLCertificateKeyFile" /etc/httpd/
# Test configuration syntax
sudo apachectl configtest
# Check SSL modules loaded
sudo httpd -M | grep ssl
#============================================#
# NGINX
#============================================#
# Test configuration
sudo nginx -t
# View SSL paths
sudo grep -r "ssl_certificate\|ssl_certificate_key" /etc/nginx/
# Check certificate files referenced
sudo nginx -T | grep "ssl_certificate"
#============================================#
# POSTFIX (Mail)
#============================================#
# View TLS settings
sudo postconf | grep -i tls
# Check cert/key paths
sudo postconf smtpd_tls_cert_file smtpd_tls_key_file
# Test TLS
openssl s_client -connect localhost:25 -starttls smtp
#============================================#
# OPENLDAP
#============================================#
# Check TLS settings
sudo grep -i "TLSCert\|TLSKey" /etc/openldap/slapd.conf /etc/openldap/slapd.d/* 2>/dev/null
# Test LDAPS
openssl s_client -connect localhost:636
#============================================#
# POSTGRESQL
#============================================#
# Check SSL settings
sudo -u postgres psql -c "SHOW ssl_cert_file; SHOW ssl_key_file;"
# Test SSL connection
psql "host=localhost sslmode=require"
File Permissions Check
#============================================#
# VERIFY PERMISSIONS (CRITICAL!)
#============================================#
# Certificate files (public) should be readable
ls -l /etc/pki/tls/certs/*.crt
# -rw-r--r-- (644) ← Good
# Key files (private) should be protected
ls -l /etc/pki/tls/private/*.key
# -rw------- (600) or -rw-r----- (640) ← Good
# -rw-r--r-- (644) ← BAD! Too permissive!
# Check ownership
ls -l /etc/pki/tls/private/*.key
# Should be owned by service user or root
# Fix permissions if needed
sudo chmod 600 /etc/pki/tls/private/server.key
sudo chown root:root /etc/pki/tls/private/server.key
27.7 Step 5: Check System-Level Settings
RHEL Version-Specific
#============================================#
# RHEL 8/9/10: CHECK CRYPTO-POLICIES
#============================================#
# Current policy
update-crypto-policies --show
# DEFAULT, LEGACY, FUTURE, or FIPS
# If service fails with "no shared cipher" or similar:
# Temporarily test with LEGACY policy
sudo update-crypto-policies --set LEGACY
sudo systemctl restart <service>
# Test if it works now
# If YES → cipher/TLS version incompatibility
# If NO → different issue
# Revert to DEFAULT
sudo update-crypto-policies --set DEFAULT
#============================================#
# CHECK FIPS MODE (All Versions)
#============================================#
fips-mode-setup --check
# FIPS mode is enabled.
# In FIPS mode, additional restrictions apply:
# - Only approved algorithms
# - Stricter key requirements
# - Some ciphers disabled
#============================================#
# CHECK SELINUX (Critical!)
#============================================#
# SELinux status
getenforce
# Enforcing, Permissive, or Disabled
# Check for certificate-related denials
sudo ausearch -m avc -ts recent | grep -i cert
# Check SELinux context of cert files
ls -Z /etc/pki/tls/certs/server.crt
# system_u:object_r:cert_t:s0 ← Correct
# If wrong, relabel
sudo restorecon -v /etc/pki/tls/certs/server.crt
sudo restorecon -v /etc/pki/tls/private/server.key
#============================================#
# CHECK FIREWALL
#============================================#
# Verify service port is open
sudo firewall-cmd --list-all | grep -E "(https|443|ldaps|636)"
# If not open
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
27.8 Step 6: Test Certificate Functionality
Live Connection Tests
#============================================#
# TEST HTTPS/TLS CONNECTION
#============================================#
# Method 1: openssl s_client (most detailed)
openssl s_client -connect server.example.com:443 -servername server.example.com
# Look for:
# - "Verify return code: 0 (ok)" ← Good
# - Certificate chain display
# - Cipher negotiated
# - Protocol version (TLS 1.2/1.3)
# Method 2: curl (quick test)
curl -v https://server.example.com/
# Look for:
# * SSL connection using TLSv1.3
# * Server certificate:
# * subject: CN=server.example.com
# * issuer: CN=Let's Encrypt Authority
# Method 3: Test with specific TLS version
openssl s_client -connect server.example.com:443 -tls1_2
openssl s_client -connect server.example.com:443 -tls1_3
#============================================#
# TEST FROM CLIENT PERSPECTIVE
#============================================#
# Test DNS resolution
nslookup server.example.com
# Must resolve to correct IP
# Test network connectivity
telnet server.example.com 443
nc -zv server.example.com 443
# Test with different clients
curl --insecure https://server.example.com/ # Bypass cert check
wget --no-check-certificate https://server.example.com/
Certificate Validation Tests
#============================================#
# VALIDATE SPECIFIC ASPECTS
#============================================#
# Test hostname matching
openssl s_client -connect server.example.com:443 -servername server.example.com 2>&1 | \
grep "verify return"
# Test with wrong hostname (should fail)
openssl s_client -connect server.example.com:443 -servername wrong.example.com 2>&1 | \
grep "verify return"
# Test expiration
openssl s_client -connect server.example.com:443 2>&1 | openssl x509 -noout -dates
# Test cipher strength
openssl s_client -connect server.example.com:443 -cipher 'HIGH:!aNULL:!MD5'
27.9 Step 7: Review Logs & Error Details
Where to Look
#============================================#
# SERVICE LOGS
#============================================#
# Apache
sudo tail -f /var/log/httpd/error_log
sudo tail -f /var/log/httpd/ssl_error_log
# NGINX
sudo tail -f /var/log/nginx/error.log
# Postfix
sudo tail -f /var/log/maillog
# System journal (all services)
sudo journalctl -u httpd.service -f
sudo journalctl -u nginx.service -f
sudo journalctl -xe | grep -i cert
#============================================#
# CERTMONGER LOGS (Auto-Renewal)
#============================================#
# certmonger status
sudo getcert list
# certmonger logs
sudo journalctl -u certmonger.service -f
# Detailed status for specific cert
sudo getcert list -i <request-id>
#============================================#
# SELINUX DENIALS
#============================================#
# Recent AVC denials
sudo ausearch -m avc -ts recent
# Certificate-related denials
sudo ausearch -m avc -ts today | grep -i cert
#============================================#
# OPENSSL/TLS ERRORS
#============================================#
# Common in logs:
# - "SSL_CTX_use_certificate:ca md too weak" → Weak signature algorithm
# - "unable to get local issuer certificate" → Missing CA
# - "certificate has expired" → Expired cert
# - "certificate verify failed" → Chain validation failure
# - "no shared cipher" → Cipher mismatch
# - "wrong version number" → Protocol mismatch
27.10 Decision Trees
Quick Diagnostic Flowchart
Certificate Issue
│
├─ Service won't start?
│ ├─ Check file paths in config
│ ├─ Check file permissions (600 for keys)
│ ├─ Check cert/key pair match
│ └─ Check SELinux context
│
├─ Connection fails with "certificate verify failed"?
│ ├─ Check CA trust (Step 3)
│ ├─ Check intermediate certificates
│ └─ Check crypto-policy (RHEL 8+)
│
├─ "Certificate has expired"?
│ ├─ Verify with: openssl x509 -noout -dates
│ ├─ Check certmonger status
│ └─ Renew certificate
│
├─ "Hostname does not match"?
│ ├─ Check SANs: openssl x509 -noout -ext subjectAltName
│ ├─ Verify DNS resolution
│ └─ Check server_name/ServerName directive
│
└─ "No shared cipher" / "wrong version number"?
├─ Check crypto-policy (RHEL 8+)
├─ Check TLS version compatibility
└─ Test with: openssl s_client -tls1_2
27.11 Troubleshooting Toolkit
Essential Commands
# Quick reference card for troubleshooting
# 1. IDENTIFY
cat /etc/redhat-release
openssl version
update-crypto-policies --show
# 2. VERIFY CERTIFICATE
openssl x509 -in cert.crt -noout -text
openssl x509 -in cert.crt -noout -dates
openssl x509 -in cert.crt -noout -subject
# 3. CHECK TRUST
openssl verify cert.crt
trust list | grep -i "authority"
# 4. TEST CONNECTION
openssl s_client -connect host:443 -servername host
curl -v https://host/
# 5. CHECK PERMISSIONS
ls -lZ /etc/pki/tls/certs/cert.crt
ls -lZ /etc/pki/tls/private/key.key
# 6. CHECK LOGS
sudo journalctl -xe | grep -i cert
sudo tail -f /var/log/httpd/ssl_error_log
Create Your Troubleshooting Checklist
## Certificate Issue Troubleshooting Checklist
### Environment
- [ ] RHEL Version: _______
- [ ] OpenSSL Version: _______
- [ ] Crypto-Policy (RHEL 8+): _______
- [ ] FIPS Mode: _______
- [ ] SELinux: _______
- [ ] Service: _______
### Certificate Checks
- [ ] Certificate expiration date
- [ ] Subject/hostname matches
- [ ] SANs present and correct
- [ ] Certificate/key pair match
- [ ] Signature algorithm (SHA-256 or stronger; no SHA-1 or MD5)
- [ ] Key size (>= 2048 bits)
### Trust Chain
- [ ] Certificate validates with system CA bundle
- [ ] Intermediate certificates present
- [ ] Root CA trusted by system
### Service Configuration
- [ ] Correct file paths in config
- [ ] File permissions correct (600 for keys)
- [ ] SELinux contexts correct
- [ ] Service config syntax valid
### System Settings
- [ ] Crypto-policy compatible (RHEL 8+)
- [ ] FIPS requirements met (if applicable)
- [ ] Firewall allows connections
- [ ] No SELinux denials
### Testing
- [ ] Connection test with openssl s_client
- [ ] Connection test with curl
- [ ] Hostname verification passes
### Logs
- [ ] Service logs reviewed
- [ ] System journal checked
- [ ] SELinux audit log checked
27.12 Key Takeaways
- Always follow the 7-step methodology - don’t skip steps
- Check RHEL version first - behavior varies significantly
- Verify basic certificate properties before complex troubleshooting
- Trust chain issues are the most common problem
- File permissions cause many “mysterious” failures
- Crypto-policies (RHEL 8+) affect everything
- SELinux can block certificate access
- Logs tell the story - always check them
27.13 Practice Scenarios
See upcoming chapters for detailed troubleshooting of:
- Chapter 28: Common RHEL Certificate Errors
- Chapter 29: Service-Specific Troubleshooting
- Chapter 30: certmonger Troubleshooting
- Chapter 31: Crypto-Policy Troubleshooting
- Chapter 32: SOS Report Analysis
- Chapter 33: Emergency Procedures
Quick Reference
┌────────────────────────────────────────────────────────────┐
│ 7-STEP TROUBLESHOOTING METHOD │
├────────────────────────────────────────────────────────────┤
│ 1. Identify: RHEL version, OpenSSL, crypto-policy │
│ 2. Verify: Expiry, hostname, key match, algorithm │
│ 3. Trust: CA validation, chain, intermediates │
│ 4. Config: Service files, paths, permissions │
│ 5. System: Crypto-policy, FIPS, SELinux, firewall │
│ 6. Test: Live connections, curl, openssl s_client │
│ 7. Logs: Service logs, journal, SELinux audit │
└────────────────────────────────────────────────────────────┘
🧪 Hands-On Lab
Lab 15: Troubleshooting Scenarios
Practice diagnosing and fixing an expired certificate problem (one implemented scenario)
- 📁 Location:
labs/en_US/15-troubleshooting-scenarios/ - ⏱️ Time: 15-20 minutes
- 🎯 Level: Advanced
Chapter Navigation
| ← Previous: Chapter 26 - Monitoring & Alerting on RHEL | Next: Chapter 28 - Common RHEL Certificate Errors → |
|---|
Chapter 28: Common RHEL Certificate Errors
Learn from Others’ Pain: This chapter catalogs the most common certificate errors on RHEL, organized by type and version. When you encounter an error, look here first!
28.1 Using This Chapter
How to use this troubleshooting guide:
- See an error? Search this chapter for the error message
- Service won’t start? Check Section 28.3 (Configuration Errors)
- Connection fails? Check Section 28.4 (Validation Errors)
- After RHEL upgrade? Check Section 28.7 (Version-Specific)
- Not sure? Use the methodology from Chapter 27
28.2 Most Common Errors (Top 10)
Quick Reference Table
| # | Error | Common Cause | Quick Fix |
|---|---|---|---|
| 1 | Certificate expired | Forgot to renew | Renew certificate |
| 2 | unable to get local issuer | Missing CA in trust store | Add CA to /etc/pki/ca-trust/source/anchors/ |
| 3 | certificate verify failed | Chain incomplete | Install intermediate certs |
| 4 | Permission denied | Wrong file permissions | chmod 600 on key file |
| 5 | hostname does not match | CN/SAN mismatch | Reissue with correct SANs |
| 6 | no shared cipher | Cipher incompatibility | Check crypto-policy (RHEL 8+) |
| 7 | ca md too weak | SHA-1 signature (RHEL 9+) | Reissue with SHA-256+ |
| 8 | wrong version number | TLS version mismatch | Check client TLS support |
| 9 | CA_UNREACHABLE | certmonger can’t reach IPA | Check IPA connectivity |
| 10 | SELinux denying access | Wrong SELinux context | restorecon on cert files |
28.3 Configuration Errors
Error: “SSLCertificateFile: file does not exist or is empty”
Services: Apache
Symptom:
AH00526: Syntax error on line 100 of /etc/httpd/conf.d/ssl.conf:
SSLCertificateFile: file '/etc/pki/tls/certs/server.crt' does not exist or is empty
Diagnosis:
ls -l /etc/pki/tls/certs/server.crt
# ls: cannot access '/etc/pki/tls/certs/server.crt': No such file or directory
Solutions:
# Solution 1: Fix path in config
sudo vi /etc/httpd/conf.d/ssl.conf
# Correct the SSLCertificateFile path
# Solution 2: Install certificate to expected location
sudo cp server.crt /etc/pki/tls/certs/
# Solution 3: Restore from backup
sudo cp /var/backups/certificates/latest/server.crt /etc/pki/tls/certs/
Error: “Private key does not match this certificate”
Services: Apache, NGINX, Postfix
Symptom:
SSL Library Error: error:0B080074:x509 certificate routines:
X509_check_private_key:key values mismatch
Diagnosis:
# Check if cert and key match
CERT_MOD=$(openssl x509 -noout -modulus -in /etc/pki/tls/certs/server.crt | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in /etc/pki/tls/private/server.key | openssl md5)
echo "Cert: $CERT_MOD"
echo "Key: $KEY_MOD"
# If different → mismatch!
Cause: Certificate was issued for a different private key
Solution:
# Regenerate CSR with the CORRECT key
openssl req -new -key /etc/pki/tls/private/server.key -out server.csr \
-subj "/CN=server.example.com"
# Submit CSR to CA, get new certificate
# Install new certificate
Error: “Permission denied” on Private Key
Services: All
Symptom:
Permission denied: Can't open PEM file '/etc/pki/tls/private/server.key'
Diagnosis:
ls -l /etc/pki/tls/private/server.key
# -rw-r--r--. 1 root root ← Too permissive!
# Check if service user can read it
sudo -u apache cat /etc/pki/tls/private/server.key >/dev/null
# Permission denied
Solution:
# Set correct permissions
sudo chmod 600 /etc/pki/tls/private/server.key
sudo chown apache:apache /etc/pki/tls/private/server.key
# For services that need specific ownership:
# OpenLDAP:
sudo chown ldap:ldap /etc/openldap/certs/ldap.key
# PostgreSQL:
sudo chown postgres:postgres /var/lib/pgsql/data/server.key
# MySQL:
sudo chown mysql:mysql /etc/mysql/certs/server.key
28.4 Validation Errors
Error: “certificate verify failed”
Services: All
Full Error:
SSL_connect: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
Common Causes:
Cause 1: Self-signed certificate not trusted
# Diagnosis
openssl verify /etc/pki/tls/certs/server.crt
# error 18: self signed certificate
# Solution: Add to trust store
sudo cp server.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
Cause 2: Missing CA certificate
# Diagnosis
openssl verify /etc/pki/tls/certs/server.crt
# error 20: unable to get local issuer certificate
# Solution: Add CA certificate
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
Cause 3: Missing intermediate certificate
# Diagnosis
openssl s_client -connect server.example.com:443 -showcerts
# Verify return code: 21 (unable to verify the first certificate)
# Solution: Include intermediate in cert file
cat server.crt intermediate.crt > /etc/pki/tls/certs/server-chain.crt
# Update service config to use server-chain.crt
Error: “certificate has expired”
Services: All
Symptom:
SSL_connect: error:14090086:SSL routines:ssl3_get_server_certificate:certificate has expired
Diagnosis:
# Check expiration
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -dates
# notAfter=Jan 15 23:59:59 2024 GMT ← In the past!
Solutions:
# Solution 1: If certmonger tracking
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/server.crt
# Solution 2: Manual renewal
# Generate new CSR, submit to CA, install new cert
# Solution 3: Emergency - temporary self-signed
sudo /usr/local/bin/emergency-self-signed-cert.sh $(hostname -f)
# See Chapter 33 for emergency procedures
Error: “hostname (or IP address) does not match certificate”
Services: All (especially browsers)
Full Error:
SSL: certificate subject name 'server.example.com' does not match target host name 'www.example.com'
Diagnosis:
# Check certificate CN and SANs
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -subject -ext subjectAltName
# Output:
# subject=CN=server.example.com
# X509v3 Subject Alternative Name:
# DNS:server.example.com
#
# Problem: Accessing www.example.com but cert only has server.example.com
Solution:
# Reissue certificate with correct SANs
openssl req -new -key server.key -out server.csr \
-subj "/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:server.example.com,DNS:example.com"
# Or use wildcard: *.example.com
28.5 Trust Chain Errors
Error: “unable to get local issuer certificate”
Error Code: 20
Symptom:
openssl verify /etc/pki/tls/certs/server.crt
# error 20 at 0 depth lookup: unable to get local issuer certificate
Cause: CA that signed the certificate is not in system trust store
Solution:
# Get CA certificate (from CA or extract from chain)
# Add to trust store
sudo cp issuing-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Verify
openssl verify /etc/pki/tls/certs/server.crt
# /etc/pki/tls/certs/server.crt: OK
Error: “unable to verify the first certificate”
Error Code: 21
Symptom:
openssl s_client -connect server.example.com:443
# Verify return code: 21 (unable to verify the first certificate)
Cause: Server sending certificate without intermediate(s)
Diagnosis:
# Count certificates in chain
openssl s_client -connect server.example.com:443 -showcerts 2>&1 | \
grep -c "BEGIN CERTIFICATE"
# If shows 1: Only server cert (missing intermediate!)
# Should show 2+: Server + intermediate(s)
Solution:
# Create certificate bundle with intermediate
cat server.crt intermediate.crt > /etc/pki/tls/certs/server-bundle.crt
# Update service config
# Apache:
SSLCertificateFile /etc/pki/tls/certs/server-bundle.crt
# Or use SSLCertificateChainFile (Apache):
SSLCertificateChainFile /etc/pki/tls/certs/intermediate.crt
# NGINX:
ssl_certificate /etc/pki/tls/certs/server-bundle.crt;
# Reload service
28.6 Crypto-Policy Errors (RHEL 8/9/10)
Error: “no shared cipher”
Services: All (RHEL 8/9/10)
Symptom:
SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
Diagnosis:
# Check current policy
update-crypto-policies --show
# DEFAULT
# Test connection showing ciphers
openssl s_client -connect server:443 -cipher 'ALL'
# Check what ciphers are available under current policy
openssl ciphers -v | head -20
Common Causes & Solutions:
Cause 1: Client too old (needs TLS 1.0)
# Temporary test with LEGACY
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd
# If works → client compatibility issue
# Proper fix: Update client or create custom policy module
Cause 2: Server crypto-policy too strict
# If using FUTURE policy with old clients
update-crypto-policies --show
# FUTURE
# Temporary: Use DEFAULT
sudo update-crypto-policies --set DEFAULT
sudo systemctl restart services
Error: “SSL routines:tls_post_process_client_hello:no shared cipher”
Services: All (RHEL 9+)
Symptom: Client can’t negotiate cipher with server
Solution:
# RHEL 9: Check if client using very old ciphers
# May need LEGACY policy temporarily
# Check server configuration for overrides
grep -r "SSLCipherSuite\|ssl_ciphers" /etc/httpd/ /etc/nginx/
# If hard-coded ciphers found, remove them
# Let crypto-policy handle it
28.7 RHEL Version-Specific Errors
RHEL 7 Specific Errors
Error: “dh key too small”
SSL routines:ssl3_check_cert_and_algorithm:dh key too small
Cause: Default DH parameters too small for modern clients
Solution:
# Generate stronger DH parameters
openssl dhparam -out /etc/pki/tls/dhparams.pem 2048
# Apache: Add to ssl.conf
SSLOpenSSLConfCmd DHParameters "/etc/pki/tls/dhparams.pem"
# NGINX: Add to config
ssl_dhparam /etc/pki/tls/dhparams.pem;
RHEL 8/9/10 Specific Errors
Error: “ca md too weak” (RHEL 9+)
error 3 at 0 depth lookup: CA md too weak
Cause: Certificate has SHA-1 signature (blocked on RHEL 9+)
Diagnosis:
openssl x509 -in server.crt -noout -text | grep "Signature Algorithm"
# Signature Algorithm: sha1WithRSAEncryption ← Problem!
Solution:
# Reissue certificate with SHA-256 or better
# No workaround - SHA-1 is blocked for security
# Request new certificate
openssl req -new -key server.key -out server.csr -sha256 \
-subj "/CN=server.example.com"
Error: “Provider ‘legacy’ could not be loaded” (RHEL 9+)
openssl: error while loading shared libraries: Provider 'legacy' could not be loaded
Cause: Trying to use legacy algorithm without provider
Solution:
# Use legacy provider explicitly
openssl md5 -provider legacy file.txt
# Or update to use modern algorithm
openssl sha256 file.txt
28.8 SELinux Errors
Error: SELinux Preventing Access to Certificate
Symptom:
audit: type=1400 audit(timestamp): avc: denied { read } for pid=1234 comm="httpd"
name="server.key" dev="sda1" ino=12345 scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0
Diagnosis:
# Check for AVC denials
sudo ausearch -m avc -ts recent | grep cert
# Check SELinux context
ls -Z /etc/pki/tls/certs/server.crt
ls -Z /etc/pki/tls/private/server.key
Solution:
# Fix SELinux context
sudo restorecon -Rv /etc/pki/tls/
# Verify
ls -Z /etc/pki/tls/certs/server.crt
# system_u:object_r:cert_t:s0 ← Correct
# If still issues, check if SELinux is blocking
sudo ausearch -m avc -ts recent
# Generate policy if needed
sudo ausearch -m avc -ts recent | audit2allow -M mycert
sudo semodule -i mycert.pp
28.9 certmonger Errors
Error: CA_UNREACHABLE
Symptom:
sudo getcert list
# status: CA_UNREACHABLE
Diagnosis:
# Check IPA connectivity
ipa ping
# Check Kerberos ticket
klist
# Check IPA services
ssh ipa-server "sudo ipactl status"
Solutions:
# Solution 1: Renew Kerberos ticket
kinit -k host/$(hostname -f)@REALM
# Solution 2: Check network connectivity
ping ipa.example.com
# Solution 3: Restart certmonger
sudo systemctl restart certmonger
# Solution 4: Resubmit request
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/server.crt
Error: CA_REJECTED
Symptom:
sudo getcert list
# status: CA_REJECTED
# ca-error: Server unwilling to issue certificate
Common Causes:
Cause 1: Service principal doesn’t exist
# Check if principal exists
ipa service-show HTTP/$(hostname -f)
# If not found, add it
ipa service-add HTTP/$(hostname -f)
# Resubmit
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/server.crt
Cause 2: Host not enrolled to IPA
# Check enrollment
ipa host-show $(hostname -f)
# If not enrolled
sudo ipa-client-install
28.10 GPG/PGP Legacy Key Errors
Error: “skipped PGP-2 keys” on Import
Tool: GnuPG (gpg)
Symptom:
$ gpg --allow-old-cipher-algos --import keyfile.asc
gpg: Total number processed: 2
gpg: skipped PGP-2 keys: 2
The key is silently rejected — even with --allow-old-cipher-algos.
Cause: The key file contains OpenPGP version 3 keys (PGP 2.x era). Modern GnuPG (2.2+) refuses to import v3 key packets entirely. These keys typically use RSA with MD5 signatures, both of which are cryptographically obsolete.
Diagnosis — Inspect the key packets:
gpg --list-packets keyfile.asc
Example output:
# off=0 ctb=95 tag=5 hlen=3 plen=930
:key packet: [obsolete version 3]
# off=933 ctb=b4 tag=13 hlen=2 plen=27
:user ID packet: "User Name <user@example.org>"
# off=962 ctb=89 tag=2 hlen=3 plen=277
:signature packet: algo 1, keyid A1B2C3D4E5F60789
version 3, created 1034280585, md5len 5, sigclass 0x10
digest algo 1, begin of digest a1 26
data: [2047 bits]
The critical indicators are:
:key packet: [obsolete version 3]— v3 format, rejected by modern GnuPGalgo 1— RSA (encrypt or sign)digest algo 1— MD5 (cryptographically broken)version 3on the signature — old signature formatsigclass 0x10— generic certification of a User ID
Solution: There is no way to “upgrade” a v3 key. You must generate a new modern key and retire the old one:
# 1. Generate a modern key (Ed25519 + Curve25519)
gpg --full-generate-key
# Choose: (9) ECC (sign and encrypt)
# Curve: ed25519 (signing), cv25519 (encryption)
# 2. Verify the new key
gpg --list-keys --keyid-format long
# 3. If you still control the old key, cross-sign for trust continuity
gpg --default-key OLDKEYID --sign-key NEWKEYID
# 4. Generate a revocation certificate for the old key
gpg --output revoke-old.asc --gen-revoke OLDKEYID
# 5. Publish the new key
gpg --send-keys NEWKEYID
# 6. Revoke the old key
gpg --import revoke-old.asc
gpg --send-keys OLDKEYID
After migration, update all systems that reference the old key (CI/CD, package signing, email encryption).
Understanding gpg --list-packets Output
When debugging GPG key issues, gpg --list-packets shows the raw OpenPGP packet structure. Here is a complete reference for interpreting the output.
Packet Header Fields
Each packet line starts with:
# off=0 ctb=95 tag=5 hlen=3 plen=930
| Field | Meaning |
|---|---|
off | Byte offset in the file (start position of this packet) |
ctb | Cipher Type Byte (raw header byte in hex, encodes format + tag) |
tag | Packet type (decoded — see table below) |
hlen | Header length in bytes |
plen | Payload length in bytes |
Packet Tags (tag=)
| Tag | Packet Type |
|---|---|
| 1 | Public-Key Encrypted Session Key |
| 2 | Signature |
| 3 | Symmetric-Key Encrypted Session Key |
| 4 | One-Pass Signature |
| 5 | Public Key |
| 6 | Secret Key |
| 7 | Secret Subkey |
| 8 | Compressed Data |
| 9 | Symmetrically Encrypted Data |
| 10 | Marker |
| 11 | Literal Data |
| 12 | Trust |
| 13 | User ID |
| 14 | Public Subkey |
| 17 | User Attribute |
| 18 | Encrypted + Integrity Protected Data |
| 19 | Modification Detection Code |
Public-Key Algorithm IDs (algo)
| ID | Algorithm |
|---|---|
| 1 | RSA (encrypt or sign) |
| 2 | RSA (encrypt-only) |
| 3 | RSA (sign-only) |
| 16 | Elgamal (encrypt-only) |
| 17 | DSA |
| 18 | ECDH |
| 19 | ECDSA |
| 21 | Diffie-Hellman |
| 22 | EdDSA (Ed25519, etc.) |
Digest (Hash) Algorithm IDs (digest algo)
| ID | Algorithm | Status |
|---|---|---|
| 1 | MD5 | Broken — do not use |
| 2 | SHA-1 | Deprecated — blocked on RHEL 9+ |
| 3 | RIPEMD-160 | Legacy |
| 8 | SHA-256 | Recommended |
| 9 | SHA-384 | Strong |
| 10 | SHA-512 | Strong |
| 11 | SHA-224 | Acceptable |
Signature Classes (sigclass)
| Code | Meaning |
|---|---|
| 0x00 | Binary document signature |
| 0x01 | Canonical text signature |
| 0x02 | Standalone signature |
| 0x10 | Generic key certification |
| 0x11 | Persona certification |
| 0x12 | Casual certification |
| 0x13 | Positive certification |
| 0x18 | Subkey binding |
| 0x19 | Primary key binding |
| 0x1F | Direct key signature |
| 0x20 | Key revocation |
| 0x28 | Subkey revocation |
| 0x30 | Certification revocation |
| 0x40 | Timestamp |
| 0x50 | Third-party confirmation |
Key Packet Versions
| Version | Era | Status |
|---|---|---|
| 3 | PGP 2.x (1990s) | Obsolete — uses MD5 internally, rejected by modern GnuPG |
| 4 | OpenPGP RFC 4880 (2007) | Current standard |
| 5 | Draft (crypto-refresh) | Emerging |
Signature Packet Fields
For a signature like:
:signature packet: algo 1, keyid A1B2C3D4E5F60789
version 3, created 1034280585, md5len 5, sigclass 0x10
digest algo 1, begin of digest a1 26
data: [2047 bits]
| Field | Meaning |
|---|---|
algo 1 | Public-key algorithm used (RSA) |
keyid | Short identifier of the signing key |
version 3 | Signature format version (v3 = legacy) |
created | Unix timestamp of signature creation |
md5len | Length of MD5 prefix (v3 legacy artifact) |
sigclass 0x10 | Signature type (generic key certification) |
digest algo 1 | Hash algorithm (MD5) |
begin of digest | First 2 bytes of hash (for quick verification checks) |
data: [2047 bits] | RSA signature data (~2048-bit key) |
28.11 RPM Signature Errors After RHEL Upgrade
Error: “Certificate invalid: policy violation — SHA1 is not considered secure”
Tools: RPM, DNF
Symptom:
After upgrading to RHEL 9+ or RHEL 10, RPM commands produce signature verification errors for third-party packages:
$ rpm -qa
error: Verifying a signature using certificate
D4E7A923F10B82C6459831AE5F6C0D9BA47E31D2
(Third-Party Vendor (Release signing) <security@vendor.example.com>):
1. Certificate 5F6C0D9BA47E31D2 invalid: policy violation
because: No binding signature at time 2024-08-12T10:44:42Z
because: Policy rejected non-revocation signature
(PositiveCertification) requiring second pre-image resistance
because: SHA1 is not considered secure
2. Certificate 5F6C0D9BA47E31D2 invalid: policy violation
because: No binding signature at time 2026-04-16T19:46:38Z
because: Policy rejected non-revocation signature
(PositiveCertification) requiring second pre-image resistance
because: SHA1 is not considered secure
RPM commands still function but produce excessive error output for every package signed with the affected key.
Cause: Third-party GPG signing keys that use SHA-1 for their binding signatures (self-certifications) are rejected by RHEL 9+ crypto-policies (SHA-1 blocked by default) and RHEL 10 (SHA-1 support removed entirely). Packages installed before the upgrade retain their old SHA-1 signatures in the RPM database.
Diagnosis:
# List all imported GPG keys in the RPM database
rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n'
Solution:
# 1. Remove the old third-party signing key
# The certificate ID 5F6C0D9BA47E31D2 maps to version a47e31d2 in RPM
rpm -e --allmatches gpg-pubkey-a47e31d2-6142699d
# 2. Import the vendor's updated key
rpm --import https://vendor.example.com/keys/signing.asc
# 3. Verify errors are gone
rpm -qa > /dev/null
Key ID mapping: The certificate ID in the error (e.g.,
5F6C0D9BA47E31D2) corresponds to the RPMgpg-pubkeyversion in lowercase hex (a47e31d2). Ifrpm -ereports “not installed”, list all keys withrpm -q gpg-pubkey --qf '...'to find the exact version-release string.
28.12 RPM Database Corruption After RHEL Upgrade
Error: “Malformed MPI” / “non-conformant OpenPGP implementation”
Tools: RPM
Symptom:
After upgrading to RHEL 9+ or RHEL 10, RPM reports a corrupt signature on every database query:
error: rpmdbNextIterator: skipping h# 9
Header RSA signature: BAD (header tag 268: invalid OpenPGP signature:
Parsing an OpenPGP packet:
Failed to parse Signature Packet
because: Signature appears to be created by a non-conformant
OpenPGP implementation, see
<https://github.com/rpm-software-management/rpm/issues/2351>.
because: Malformed MPI: leading bit is not set: expected bit 8 to
be set in 110010 (32))
Header SHA256 digest: OK
Header SHA1 digest: OK
Cause: Some third-party packages were signed using non-standard OpenPGP implementations that produce malformed MPI (Multi-Precision Integer) values in RSA signatures. The older RPM in RHEL 7/8 tolerated these, but the Sequoia-PGP based parser in RHEL 9+/10 rejects them.
Diagnosis:
# Identify the broken package using the header number from the error (h# 9)
rpm -q --nosignature --querybynumber 9
Solution:
# 1. Backup the RPM database
tar zcvf /var/preserve/rpmdb-$(date +"%d%m%Y").tar.gz /usr/lib/sysimage/rpm/
# Note: on RHEL 8/9, the database is in /var/lib/rpm/
# 2. Identify the damaged package
rpm -q --nosignature --querybynumber <NUMBER_FROM_ERROR>
# 3. Remove the package with the broken signature
rpm -e --nosignature --nodigest <package-name>
# If removal fails, remove just the database entry:
rpm -e --justdb --nodeps <package-name>
# 4. Rebuild the RPM database
rpm --rebuilddb
# 5. Verify errors are resolved
rpm -qa > /dev/null
# 6. Reinstall from an updated repository
dnf install <package-name>
Note: On RHEL 10, the RPM database is in
/usr/lib/sysimage/rpm/. On RHEL 8/9, it’s in/var/lib/rpm/.
Reference: RPM issue #2351 documents the stricter MPI parsing introduced with the Sequoia-PGP backend.
Combined Scenario: Both Errors After Upgrade
When upgrading from RHEL 7/8 to RHEL 9+/10, both errors often appear simultaneously. Resolve them in this order:
- Remove old SHA-1 signing keys and import updated vendor keys
- Rebuild the RPM database with
rpm --rebuilddb - If malformed MPI errors persist, identify affected packages by header number (
rpm -q --nosignature --querybynumber <N>), remove them, and reinstall from updated repositories
28.13 Browser/Client Errors
Error: “NET::ERR_CERT_COMMON_NAME_INVALID”
Symptom: Browser shows “Your connection is not private”
Cause: Hostname doesn’t match certificate CN or SANs
Diagnosis:
# Check what you're accessing
echo "Accessing: www.example.com"
# Check certificate SANs
openssl s_client -connect www.example.com:443 2>&1 | \
openssl x509 -noout -ext subjectAltName
# X509v3 Subject Alternative Name:
# DNS:server.example.com ← Doesn't include www.example.com!
Solution:
# Reissue certificate with correct SANs
openssl req -new -key server.key -out server.csr \
-subj "/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:server.example.com,DNS:example.com"
Error: “NET::ERR_CERT_AUTHORITY_INVALID”
Symptom: Browser doesn’t trust certificate
Cause: CA not in browser’s trust store (self-signed or internal CA)
For Internal CA:
# Distribute CA certificate to clients
# Users need to install CA in their browser
# Or add to system trust (Linux clients)
sudo cp corporate-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
For Self-Signed (Testing Only):
# Don't use self-signed in production!
# Get proper certificate from CA
28.14 Firewall/Network Errors
Error: Connection Timeout
Symptom: Can’t connect to HTTPS port
Diagnosis:
# Check if service listening
ss -tlnp | grep :443
# Check firewall
sudo firewall-cmd --list-services | grep https
# Test locally
curl -vk https://localhost/
# Test remotely
telnet server.example.com 443
Solution:
# Open firewall
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
28.15 Error Message Dictionary
Quick Lookup Table
| Error Message | Error Code | Cause | Chapter |
|---|---|---|---|
| “certificate has expired” | - | Expired cert | 28.4 |
| “unable to get local issuer” | 20 | Missing CA | 28.4 |
| “unable to verify first cert” | 21 | Missing intermediate | 28.4 |
| “self signed certificate” | 18 | Self-signed not trusted | 28.4 |
| “certificate verify failed” | - | General validation | 28.4 |
| “ca md too weak” | 3 | SHA-1 signature | 28.7 |
| “no shared cipher” | - | Cipher mismatch | 28.6 |
| “wrong version number” | - | TLS version mismatch | 28.6 |
| “Permission denied” | - | File permissions | 28.3 |
| “key values mismatch” | - | Cert/key don’t pair | 28.3 |
| “hostname does not match” | - | CN/SAN mismatch | 28.4 |
| “CA_UNREACHABLE” | - | certmonger can’t reach IPA | 28.9 |
| “CA_REJECTED” | - | IPA rejected request | 28.9 |
| “skipped PGP-2 keys” | - | GPG v3 key import rejected | 28.10 |
| “SHA1 is not considered secure” | - | Third-party GPG key uses SHA-1 | 28.11 |
| “Malformed MPI” | - | Non-conformant OpenPGP signature | 28.12 |
28.16 Quick Diagnostic Commands
Universal Troubleshooting Commands
#============================================#
# RUN THESE FOR ANY CERTIFICATE ERROR
#============================================#
# 1. Check RHEL version
cat /etc/redhat-release
# 2. Check certificate file
openssl x509 -in /path/to/cert.crt -noout -text
# 3. Check expiration
openssl x509 -in /path/to/cert.crt -noout -dates
# 4. Check trust
openssl verify /path/to/cert.crt
# 5. Check permissions
ls -lZ /path/to/cert.crt
ls -lZ /path/to/key.key
# 6. Check cert/key match
openssl x509 -noout -modulus -in cert.crt | openssl md5
openssl rsa -noout -modulus -in key.key | openssl md5
# 7. Test connection
openssl s_client -connect server:443
# 8. Check logs
sudo journalctl -xe | grep -i cert
sudo tail -f /var/log/httpd/ssl_error_log
# 9. Check SELinux
sudo ausearch -m avc -ts recent | grep cert
# 10. Check crypto-policy (RHEL 8+)
update-crypto-policies --show
28.17 Error Resolution Flowchart
Certificate Error Occurred
│
├─ Service won't start?
│ ├─ Check config syntax
│ ├─ Check file paths
│ ├─ Check permissions (600 for keys)
│ └─ Check SELinux context
│
├─ Connection fails?
│ ├─ Check firewall
│ ├─ Check service listening
│ ├─ Test with openssl s_client
│ └─ Check network routing
│
├─ Certificate validation error?
│ ├─ Check expiration
│ ├─ Check trust chain
│ ├─ Check hostname match
│ └─ Check intermediate certs
│
├─ Cipher/protocol error?
│ ├─ Check crypto-policy (RHEL 8+)
│ ├─ Check TLS versions
│ └─ Test with different TLS version
│
└─ certmonger error?
├─ CA_UNREACHABLE → Check IPA connectivity
├─ CA_REJECTED → Check principal exists
└─ See Chapter 30
28.18 Key Takeaways
- Most errors are predictable - Common patterns
- Always check expiration first - #1 cause of issues
- Permissions matter - 600 for keys, 644 for certs
- Trust chain critical - Missing CA or intermediate
- RHEL version matters - Different errors per version
- crypto-policies affect everything (RHEL 8+)
- SELinux can block - Check contexts
- Reference Chapter 27 for systematic approach
Quick Reference Card
┌───────────────────────────────────────────────────────────────────┐
│ COMMON CERTIFICATE ERRORS QUICK REFERENCE │
├───────────────────────────────────────────────────────────────────┤
│ Expired: Check: openssl x509 -noout -dates │
│ Fix: Renew certificate │
│ │
│ Trust: Check: openssl verify cert.crt │
│ Fix: Add CA to /etc/pki/ca-trust/... │
│ │
│ Hostname: Check: openssl x509 -noout -ext subjectAltName │
│ Fix: Reissue with correct SANs │
│ │
│ Permissions: Check: ls -lZ cert.crt key.key │
│ Fix: chmod 600 key.key │
│ │
│ Cert/Key match: Check: Compare MD5 of modulus │
│ Fix: Regenerate CSR with correct key │
│ │
│ No shared cipher: Check: update-crypto-policies --show │
│ Fix: Update policy or client │
│ │
│ SELinux: Check: ausearch -m avc | grep cert │
│ Fix: restorecon -Rv /etc/pki/tls/ │
└───────────────────────────────────────────────────────────────────┘
Always start with: Chapter 27 (7-step methodology)
Chapter Navigation
| ← Previous: Chapter 27 - RHEL Certificate Troubleshooting Methodology | Next: Chapter 29 - Service-Specific Troubleshooting → |
|---|
Chapter 29: Service-Specific Troubleshooting
Service by Service: Each RHEL service has unique certificate requirements and failure modes. This chapter provides targeted troubleshooting for each major service.
29.1 Apache httpd Troubleshooting
Apache Won’t Start
Diagnostic Steps:
#============================================#
# APACHE CERTIFICATE TROUBLESHOOTING
#============================================#
# Step 1: Check Apache status
systemctl status httpd
sudo journalctl -xe -u httpd
# Step 2: Test configuration
sudo apachectl configtest
# Look for SSL-related errors
# Step 3: Check if mod_ssl loaded
sudo httpd -M | grep ssl
# Should show: ssl_module (shared)
# Step 4: Verify certificate files
ls -l /etc/pki/tls/certs/*.crt
ls -l /etc/pki/tls/private/*.key
# Step 5: Check permissions
ls -l /etc/pki/tls/private/server.key
# Should be: -rw------- (600)
# Step 6: Verify cert/key match
CERT_MOD=$(openssl x509 -noout -modulus -in /etc/pki/tls/certs/server.crt | openssl md5)
KEY_MOD=$(openssl rsa -noout -modulus -in /etc/pki/tls/private/server.key | openssl md5)
[ "$CERT_MOD" = "$KEY_MOD" ] && echo "✅ Match" || echo "❌ Mismatch!"
# Step 7: Check SELinux
sudo ausearch -m avc -ts recent | grep httpd | grep cert
Common Apache SSL Errors
| Error | Cause | Solution |
|---|---|---|
| “SSLCertificateFile: file does not exist” | Wrong path | Fix path in ssl.conf |
| “key values mismatch” | Cert/key don’t pair | Regenerate with correct key |
| “unable to load certificate” | File format issue | Ensure PEM format |
| “Syntax error” in ssl.conf | Config typo | Run apachectl configtest |
| “unable to verify certificate” | Chain issue | Add intermediate cert |
29.2 NGINX Troubleshooting
NGINX SSL/TLS Issues
Diagnostic Steps:
#============================================#
# NGINX CERTIFICATE TROUBLESHOOTING
#============================================#
# Step 1: Test configuration
sudo nginx -t
# Step 2: Show full config
sudo nginx -T | grep ssl_certificate
# Step 3: Check certificate files
ls -l /etc/pki/tls/certs/nginx.crt
ls -l /etc/pki/tls/private/nginx.key
# Step 4: Verify cert/key pair
openssl x509 -noout -modulus -in /etc/pki/tls/certs/nginx.crt | openssl md5
openssl rsa -noout -modulus -in /etc/pki/tls/private/nginx.key | openssl md5
# Step 5: Check NGINX error log
sudo tail -50 /var/log/nginx/error.log | grep -i ssl
# Step 6: Check if NGINX running
systemctl status nginx
ss -tlnp | grep nginx
Common NGINX SSL Errors
| Error | Cause | Solution |
|---|---|---|
| “SSL: error:0200100D” | Permission denied on key | chmod 600 on key |
| “no "ssl" is defined” | Missing ssl in listen | Add listen 443 ssl; |
| “cannot load certificate” | File not found | Check path |
| “PEM_read_bio:no start line” | Wrong format | Ensure PEM format |
| “nginx: [emerg] bind() failed” | Port in use | Check what’s on port 443 |
29.3 Postfix Troubleshooting
Postfix TLS Issues
Diagnostic Steps:
#============================================#
# POSTFIX TLS TROUBLESHOOTING
#============================================#
# Step 1: Check Postfix TLS config
sudo postconf | grep -i tls
# Step 2: View specific settings
sudo postconf smtpd_tls_cert_file smtpd_tls_key_file
# Step 3: Test configuration
sudo postfix check
# Step 4: Check certificate files
ls -l $(sudo postconf -h smtpd_tls_cert_file)
ls -l $(sudo postconf -h smtpd_tls_key_file)
# Step 5: Test SMTP TLS
openssl s_client -starttls smtp -connect localhost:25
# Step 6: Check mail logs
sudo tail -f /var/log/maillog | grep -i tls
# Step 7: Check if STARTTLS offered
telnet localhost 25
# Type: EHLO test
# Should show: 250-STARTTLS
Common Postfix TLS Errors
| Error | Cause | Solution |
|---|---|---|
| “SSL_accept error” | Cert/key issue | Check cert/key pair |
| “TLS is required but not available” | TLS not enabled | Set security_level = may |
| “no shared cipher” | Cipher mismatch | Check crypto-policy |
| “certificate verify failed” | Chain issue | Install intermediate |
| “Permission denied” | Key permissions | chmod 600 on key |
29.4 OpenLDAP Troubleshooting
LDAPS Issues
Diagnostic Steps:
#============================================#
# OPENLDAP TLS TROUBLESHOOTING
#============================================#
# Step 1: Check if slapd listening on 636
ss -tlnp | grep 636
# Step 2: Check TLS configuration
sudo slapcat -b "cn=config" | grep -i tls
# Step 3: Verify certificate files
ls -l /etc/openldap/certs/ldap.{crt,key}
# Step 4: Check ownership
# CRITICAL: Must be owned by ldap user!
ls -l /etc/openldap/certs/
# Should show: ldap:ldap
# Step 5: Test LDAPS connection
openssl s_client -connect localhost:636
# Step 6: Test with ldapsearch
ldapsearch -H ldaps://localhost:636 -x -b "" -s base
# Step 7: Check slapd logs
sudo journalctl -u slapd | grep -i tls
Common OpenLDAP TLS Errors
| Error | Cause | Solution |
|---|---|---|
| “TLS: can’t accept” | Key not readable | chown ldap:ldap on key |
| “TLS: hostname does not match” | CN/SAN mismatch | Reissue with correct hostname |
| “certificate verify failed” | CA not trusted | Add CA to trust store |
| “Permission denied” | Wrong ownership | chown ldap:ldap |
| “TLS engine not initialized” | TLS not configured | Add TLS directives |
29.5 PostgreSQL Troubleshooting
PostgreSQL SSL Issues
Diagnostic Steps:
#============================================#
# POSTGRESQL SSL TROUBLESHOOTING
#============================================#
# Step 1: Check if SSL enabled
sudo -u postgres psql -c "SHOW ssl;"
# Step 2: View SSL settings
sudo -u postgres psql -c "SHOW ssl_cert_file; SHOW ssl_key_file;"
# Step 3: Check certificate files
ls -l /var/lib/pgsql/data/server.{crt,key}
# Step 4: Check ownership
# Must be owned by postgres user
ls -l /var/lib/pgsql/data/server.key
# -rw------- postgres postgres
# Step 5: Test SSL connection
psql "host=localhost sslmode=require"
# Step 6: Check PostgreSQL logs
sudo tail -f /var/lib/pgsql/data/log/postgresql-*.log | grep -i ssl
# Step 7: Check permissions
sudo -u postgres stat /var/lib/pgsql/data/server.key
Common PostgreSQL SSL Errors
| Error | Cause | Solution |
|---|---|---|
| “could not load server certificate” | Permission denied | chown postgres:postgres, chmod 600 |
| “private key file has wrong permissions” | Too permissive | chmod 600 on key |
| “SSL connection has been closed unexpectedly” | Trust issue | Check client CA trust |
| “SSL is not enabled” | SSL off in config | Set ssl = on |
29.6 MySQL/MariaDB Troubleshooting
Database SSL Issues
Diagnostic Steps:
#============================================#
# MYSQL/MARIADB SSL TROUBLESHOOTING
#============================================#
# Step 1: Check if SSL available
mysql -u root -p -e "SHOW VARIABLES LIKE 'have_ssl';"
# Should show: YES
# Step 2: View SSL variables
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# Step 3: Check certificate files
ls -l /etc/mysql/certs/{ca,server}.{crt,key}
# Step 4: Check ownership
# Must be readable by mysql user
ls -l /etc/mysql/certs/
# mysql:mysql
# Step 5: Test SSL connection
mysql --ssl-mode=REQUIRED -h localhost -u root -p
# Step 6: Check connection status
mysql -u root -p -e "STATUS" | grep SSL
# Step 7: Check error log
sudo tail -f /var/log/mariadb/mariadb.log | grep -i ssl
29.7 Cross-Service Issues
Certificate Works in One Service, Fails in Another
Scenario: Same certificate works in Apache but fails in Postfix
Diagnosis:
# Apache works
curl -v https://localhost/
# ✅ OK
# Postfix fails
openssl s_client -starttls smtp -connect localhost:25
# ❌ Error
# Why? Different requirements!
Common Causes:
Cause 1: File ownership
- Apache: Runs as root (can read root-owned keys)
- Postfix: Runs as postfix (needs readable key)
- OpenLDAP: Runs as ldap (needs ldap-owned key)
Cause 2: File locations
- Apache: /etc/pki/tls/
- PostgreSQL: /var/lib/pgsql/data/
- OpenLDAP: /etc/openldap/certs/
Cause 3: Format requirements
- Most services: Separate cert and key files
- HAProxy: Combined PEM file
- Cockpit: Combined cert+key
29.8 Troubleshooting Toolkit
Service-Specific Test Commands
#============================================#
# TEST EACH SERVICE
#============================================#
# Apache HTTPS
curl -v https://localhost/
openssl s_client -connect localhost:443
# NGINX HTTPS
curl -v https://localhost:8443/ # If custom port
openssl s_client -connect localhost:443
# Postfix SMTP
openssl s_client -starttls smtp -connect localhost:25
openssl s_client -connect localhost:465 # SMTPS
# Dovecot IMAP
openssl s_client -connect localhost:993 # IMAPS
openssl s_client -connect localhost:995 # POP3S
# OpenLDAP
openssl s_client -connect localhost:636 # LDAPS
ldapsearch -H ldaps://localhost:636 -x -b ""
# PostgreSQL
psql "host=localhost sslmode=require"
# MySQL/MariaDB
mysql --ssl-mode=REQUIRED -h localhost -u root -p
# Cockpit
openssl s_client -connect localhost:9090
29.9 Key Takeaways
- Each service has unique requirements - Ownership, location, format
- Always check service-specific logs first
- Test with service-specific commands (not just openssl)
- Permissions critical - Different users for different services
- File locations matter - Service-dependent paths
- Configuration syntax varies by service
- Reference service chapters (Ch 14-21) for detailed config
Quick Reference Card
┌─────────────────────────────────────────────────────────────┐
│ SERVICE-SPECIFIC TROUBLESHOOTING │
├─────────────────────────────────────────────────────────────┤
│ Apache: apachectl configtest │
│ tail -f /var/log/httpd/ssl_error_log │
│ │
│ NGINX: nginx -t │
│ tail -f /var/log/nginx/error.log │
│ │
│ Postfix: postfix check │
│ tail -f /var/log/maillog | grep TLS │
│ │
│ OpenLDAP: slapcat -b "cn=config" | grep TLS │
│ journalctl -u slapd | grep TLS │
│ chown ldap:ldap (CRITICAL!) │
│ │
│ PostgreSQL: psql -c "SHOW ssl;" │
│ chown postgres:postgres (CRITICAL!) │
│ │
│ MySQL: mysql -e "SHOW VARIABLES LIKE '%ssl%';" │
│ chown mysql:mysql (CRITICAL!) │
└─────────────────────────────────────────────────────────────┘
⚠️ File ownership is service-specific!
✅ Always check logs for each service
Chapter Navigation
| ← Previous: Chapter 28 - Common RHEL Certificate Errors | Next: Chapter 30 - certmonger Troubleshooting → |
|---|
Chapter 30: certmonger Troubleshooting
Automation Issues: certmonger is RHEL’s certificate automation tool. When it fails, certificates don’t renew. This chapter teaches you to diagnose and fix certmonger problems quickly.
30.1 certmonger Status Values
Understanding Status Messages
| Status | Meaning | Action Required |
|---|---|---|
MONITORING | ✅ All good - cert issued, tracking expiry | None |
SUBMITTING | 🔄 Requesting cert from CA | Wait (usually seconds) |
CA_UNREACHABLE | ❌ Can’t contact CA server | Fix connectivity |
CA_REJECTED | ❌ CA refused request | Fix principal/permissions |
NEED_KEY_GEN_PIN | ⏸️ Waiting for PIN (HSM) | Provide PIN |
NEED_GUIDANCE | ⚠️ Needs manual intervention | Check request details |
PRE_SAVE_COMMAND | 🔄 Running pre-save script | Wait |
POST_SAVE_COMMAND | 🔄 Running post-save script | Wait |
NEWLY_ADDED | 🆕 Just added, not yet processed | Wait |
30.2 CA_UNREACHABLE Troubleshooting
Most Common certmonger Issue!
Symptom:
sudo getcert list
# status: CA_UNREACHABLE
Diagnosis Steps
#============================================#
# DIAGNOSE CA_UNREACHABLE
#============================================#
# Step 1: Which CA are we trying to reach?
sudo getcert list -v | grep "CA:"
# CA: IPA
# Step 2: Can we reach IPA?
ipa ping
# Pong! ← Good
# ipa: ERROR: cannot connect to 'https://ipa.example.com/ipa/xml' ← Bad!
# Step 3: Check Kerberos ticket
klist
# Ticket cache: FILE:/tmp/krb5cc_0
# Valid starting Expires Service principal
# ...
# Step 4: Check if ticket expired
klist | grep "host/"
# If no host ticket or expired → Problem!
# Step 5: Check IPA server status
ssh ipa.example.com "sudo ipactl status"
# Step 6: Check network
ping ipa.example.com
curl -k https://ipa.example.com/ipa/config/ca.crt
# Step 7: Check DNS
nslookup ipa.example.com
Solutions for CA_UNREACHABLE
Solution 1: Renew Kerberos Ticket
# Get new host ticket
sudo kinit -k host/$(hostname -f)@REALM
# Verify
klist
# Retry cert request
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
Solution 2: Check IPA Server
# On IPA server
sudo ipactl status
# If services down
sudo ipactl restart
# Check specific service
sudo systemctl status pki-tomcatd@pki-tomcat # CA service
Solution 3: Network/Firewall
# Test IPA connectivity
curl -vk https://ipa.example.com/ipa/xml
# Check firewall on IPA server
ssh ipa.example.com "sudo firewall-cmd --list-services | grep https"
# Check routes
traceroute ipa.example.com
Solution 4: Restart certmonger
sudo systemctl restart certmonger
# Wait a moment
sleep 10
# Check status
sudo getcert list
30.3 CA_REJECTED Troubleshooting
When CA Refuses the Request
Symptom:
sudo getcert list -v
# status: CA_REJECTED
# ca-error: Server at https://ipa.example.com/ipa/xml unwilling to issue certificate
Diagnosis Steps
#============================================#
# DIAGNOSE CA_REJECTED
#============================================#
# Step 1: Check error details
sudo getcert list -v -f /etc/pki/tls/certs/web.crt
# Look at 'ca-error' field
# Step 2: Does service principal exist?
ipa service-show HTTP/$(hostname -f)
# If error: Service not found
# Step 3: Is host enrolled?
ipa host-show $(hostname -f)
# Step 4: Check certificate profile exists
sudo getcert list -v | grep "profile:"
ipa certprofile-show caIPAserviceCert
# Step 5: Check request details
sudo getcert list -v | grep -A30 "Request ID"
Solutions for CA_REJECTED
Solution 1: Create Service Principal
# Add missing service principal
ipa service-add HTTP/$(hostname -f)
# Add SAN (if needed)
ipa service-mod HTTP/$(hostname -f) --addattr=cn=web.example.com
# Retry
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
Solution 2: Fix Host Entry
# Re-enroll to IPA if needed
sudo ipa-client-install --force-join
# Verify
ipa host-show $(hostname -f)
Solution 3: Check Permissions
# Check if you have permission to request certs
ipa permission-find --name="Request Certificate"
# Check ACLs
ipa aci-find --name="*cert*"
# May need IPA admin to grant permissions
30.4 Renewal Failures
Certificate Not Renewing
Symptom: Certificate approaching expiry but not renewing
Diagnosis:
#============================================#
# DIAGNOSE RENEWAL FAILURE
#============================================#
# Step 1: Check current status
sudo getcert list -f /etc/pki/tls/certs/web.crt
# Step 2: When should it renew?
# certmonger renews at 2/3 of cert lifetime
# 365-day cert → renews at day 243 (122 days before expiry)
# Step 3: Check certmonger logs
sudo journalctl -u certmonger --since "7 days ago" | grep -i renew
# Step 4: Force renewal attempt
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
# Step 5: Watch logs in real-time
sudo journalctl -u certmonger -f
Common Renewal Issues
Issue 1: Post-save command fails
# Check post-save command
sudo getcert list -f /etc/pki/tls/certs/web.crt | grep "post-save"
# post-save command: systemctl reload httpd
# Test command manually
sudo systemctl reload httpd
# If fails → fix the command
# Update command (re-create tracking entry; do not use getcert rekey)
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt
sudo getcert start-tracking \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-C "systemctl reload httpd"
Issue 2: IPA server down during renewal window
# certmonger will retry
# Check retry schedule in logs
sudo journalctl -u certmonger | grep "will try again"
# Manual retry
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
30.5 Tracking Issues
Certificate Not Being Tracked
Symptom: Certificate expires because certmonger wasn’t tracking it
Solution:
#============================================#
# START TRACKING EXISTING CERTIFICATE
#============================================#
sudo getcert start-tracking \
-f /etc/pki/tls/certs/existing.crt \
-k /etc/pki/tls/private/existing.key \
-c IPA \
-K HTTP/$(hostname -f)@REALM
Duplicate Tracking
Symptom: Same certificate tracked multiple times
Diagnosis:
# List all tracked certs
sudo getcert list | grep -E "(Request ID|certificate:)" | \
awk -F"'" '/certificate:/{cert=$2} /Request ID/{print cert, $2}'
# Look for duplicates
Solution:
# Remove duplicate tracking
sudo getcert stop-tracking -i <duplicate-request-id>
# Keep only one tracking entry per certificate
30.6 Configuration Issues
Wrong CA Configured
Symptom: certmonger trying to reach wrong CA
Diagnosis:
# Check configured CA
sudo getcert list -v | grep "CA:"
# List available CAs
sudo getcert list-cas
Solution:
# Stop tracking with wrong CA
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt
# Re-request with correct CA
sudo ipa-getcert request \
-c IPA \ # Specify correct CA
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-K HTTP/$(hostname -f)@REALM
30.7 certmonger Database Corruption
Rare but Serious Issue
Symptom: certmonger completely broken, all certs show errors
Diagnosis:
# Check database
ls -l /var/lib/certmonger/
# Check for corruption
sudo journalctl -u certmonger | grep -i corrupt
Solution (Nuclear Option):
# CAUTION: This removes all tracking!
# Step 1: Backup current state
sudo tar czf certmonger-backup-$(date +%Y%m%d).tar.gz \
/var/lib/certmonger/ \
/etc/pki/tls/
# Step 2: Document current tracking
sudo getcert list > /tmp/certmonger-list-backup.txt
# Step 3: Stop certmonger
sudo systemctl stop certmonger
# Step 4: Remove database
sudo rm -rf /var/lib/certmonger/cas/*
sudo rm -rf /var/lib/certmonger/requests/*
# Step 5: Start certmonger
sudo systemctl start certmonger
# Step 6: Re-add certificates (from backup documentation)
# Manually re-request each certificate
30.8 Debugging certmonger
Enable Debug Logging
#============================================#
# CERTMONGER DEBUG MODE
#============================================#
# Edit service file
sudo systemctl edit certmonger
# Add:
[Service]
Environment="G_MESSAGES_DEBUG=all"
# Reload and restart
sudo systemctl daemon-reload
sudo systemctl restart certmonger
# Watch detailed logs
sudo journalctl -u certmonger -f
# Disable debug after troubleshooting
sudo systemctl revert certmonger
sudo systemctl restart certmonger
Manual Cert Request Test
#============================================#
# TEST CERTIFICATE REQUEST MANUALLY
#============================================#
# Submit request and watch
sudo ipa-getcert request \
-f /tmp/test.crt \
-k /tmp/test.key \
-K HTTP/$(hostname -f)@REALM \
-v # Verbose
# Watch in another terminal
sudo journalctl -u certmonger -f
# If successful, remove test
sudo getcert stop-tracking -f /tmp/test.crt -r
rm -f /tmp/test.{crt,key}
30.9 Common Scenarios
Scenario 1: All Certificates Show CA_UNREACHABLE
Likely Cause: IPA server down or network issue
Quick Fix:
# Check IPA
ipa ping
# If down, fix IPA first
ssh ipa-server "sudo ipactl start"
# If network issue, fix network
# Restart certmonger
sudo systemctl restart certmonger
Scenario 2: One Certificate Stuck
Diagnosis:
# Check specific certificate
sudo getcert list -f /etc/pki/tls/certs/problem.crt
# Try resubmitting
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/problem.crt
# If still stuck, recreate request
sudo getcert stop-tracking -f /etc/pki/tls/certs/problem.crt
sudo ipa-getcert request \
-f /etc/pki/tls/certs/problem.crt \
-k /etc/pki/tls/private/problem.key \
-K HTTP/$(hostname -f)@REALM \
-D $(hostname -f)
Scenario 3: Certificate Renewed but Service Not Reloaded
Symptom: New cert exists but service still uses old one
Cause: Post-save command failed or not configured
Solution:
# Check post-save command
sudo getcert list -f /etc/pki/tls/certs/web.crt | grep "post-save"
# If missing, add it (re-create tracking entry; do not use getcert rekey)
sudo getcert stop-tracking -f /etc/pki/tls/certs/web.crt
sudo getcert start-tracking \
-f /etc/pki/tls/certs/web.crt \
-k /etc/pki/tls/private/web.key \
-C "systemctl reload httpd"
# Test post-save command works
sudo systemctl reload httpd
# Force renewal to test
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/web.crt
30.10 Key Takeaways
- CA_UNREACHABLE is most common issue - Check IPA connectivity
- CA_REJECTED means principal problem - Create service principal
- MONITORING status means all is well
- Post-save commands critical - Test them independently
- certmonger logs in journal - Use
journalctl -u certmonger - Retry with resubmit - Often fixes transient issues
- Check Kerberos tickets - Expired tickets cause issues
Quick Reference Card
┌─────────────────────────────────────────────────────────────┐
│ CERTMONGER TROUBLESHOOTING QUICK REFERENCE │
├─────────────────────────────────────────────────────────────┤
│ Status: getcert list │
│ Verbose: getcert list -v │
│ Specific: getcert list -f /path/to/cert.crt │
│ Logs: journalctl -u certmonger -f │
│ │
│ Resubmit: ipa-getcert resubmit -f /path/to/cert.crt │
│ Stop track: getcert stop-tracking -f /path/to/cert.crt │
│ Start track: getcert start-tracking -f cert -k key │
│ │
│ CA_UNREACHABLE: Check: ipa ping, klist │
│ Fix: kinit -k host/$(hostname -f)@REALM │
│ │
│ CA_REJECTED: Check: ipa service-show SERVICE/host │
│ Fix: ipa service-add SERVICE/host │
│ │
│ Debug: systemctl edit certmonger │
│ Environment="G_MESSAGES_DEBUG=all" │
└─────────────────────────────────────────────────────────────┘
✅ MONITORING = All good!
❌ CA_UNREACHABLE = Check IPA connectivity
❌ CA_REJECTED = Check service principal
Chapter Navigation
| ← Previous: Chapter 29 - Service-Specific Troubleshooting | Next: Chapter 31 - Crypto-Policy Troubleshooting → |
|---|
Chapter 31: Crypto-Policy Troubleshooting
RHEL 8/9/10 Only: Crypto-policies are powerful but can cause compatibility issues. Learn how to diagnose and fix crypto-policy problems.
31.1 Crypto-Policy Overview
Available: RHEL 8, 9, 10 only (NOT RHEL 7)
Quick Check:
# Check if crypto-policies available
which update-crypto-policies
# If found: RHEL 8/9/10
# If not found: RHEL 7 (no crypto-policies)
# Current policy
update-crypto-policies --show
31.2 Common Crypto-Policy Issues
Issue 1: Application Fails After Policy Change
Symptom: Service worked, then you changed crypto-policy, now it fails
Scenario:
# Before
update-crypto-policies --show
# DEFAULT
# You changed it
sudo update-crypto-policies --set FUTURE
sudo systemctl restart httpd
# Now httpd won't start or clients can't connect
Diagnosis:
#============================================#
# DIAGNOSE POLICY CHANGE IMPACT
#============================================#
# Step 1: Check what changed
cat /etc/crypto-policies/back-ends/opensslcnf.config
# Step 2: Check logs
sudo journalctl -xe -u httpd | grep -i cipher
# Step 3: Test connection
openssl s_client -connect localhost:443
# Step 4: Check if app overriding policy
grep -r "SSLProtocol\|SSLCipherSuite" /etc/httpd/
Solution:
# Solution 1: Revert policy
sudo update-crypto-policies --set DEFAULT
sudo systemctl restart httpd
# Solution 2: Fix application config
# Remove hard-coded cipher specifications
# Let crypto-policy handle it
# Solution 3: Create custom policy module (RHEL 9+)
# See Chapter 23 for details
Issue 2: “no shared cipher”
Symptom: Clients can’t connect after policy change
Full Error:
SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
no shared cipher
Diagnosis:
#============================================#
# DIAGNOSE CIPHER MISMATCH
#============================================#
# Step 1: Check current policy
update-crypto-policies --show
# FUTURE ← Very strict!
# Step 2: What ciphers are available?
openssl ciphers -v | head -20
# Step 3: Test client capabilities
openssl s_client -connect server:443 -cipher 'ALL'
# Step 4: Is client too old?
# Old client might only support weak ciphers blocked by FUTURE policy
Solutions:
# Solution 1: Use less strict policy (temporary!)
sudo update-crypto-policies --set DEFAULT
sudo systemctl restart services
# Solution 2: Update client to support modern ciphers
# Solution 3: Create custom policy module
# Allow specific cipher for compatibility
Issue 3: TLS 1.0/1.1 Client Can’t Connect
Symptom: Old clients fail to connect to RHEL 8+ server
Error:
SSL routines:ssl3_read_bytes:tlsv1 alert protocol version
wrong version number
Diagnosis:
# Check policy
update-crypto-policies --show
# DEFAULT ← Blocks TLS 1.0/1.1
# Test if TLS 1.0 works
openssl s_client -connect server:443 -tls1
# Should fail with DEFAULT policy
# Test if TLS 1.2 works
openssl s_client -connect server:443 -tls1_2
# Should work
Solutions:
# Solution 1: Temporary LEGACY policy (NOT recommended!)
sudo update-crypto-policies --set LEGACY
sudo systemctl restart services
# Now TLS 1.0/1.1 allowed
# Solution 2: Update client to support TLS 1.2+
# This is the PROPER fix
# Solution 3: Per-application override (last resort)
# Apache example:
# SSLProtocol all -SSLv3 # Re-enables TLS 1.0/1.1
Issue 4: Service Overriding Crypto-Policy
Symptom: Policy changes don’t affect service
Diagnosis:
#============================================#
# CHECK FOR POLICY OVERRIDES
#============================================#
# Apache
grep -r "SSLProtocol\|SSLCipherSuite" /etc/httpd/
# NGINX
grep -r "ssl_protocols\|ssl_ciphers" /etc/nginx/
# Postfix
sudo postconf | grep -E "smtp.*_tls_protocols|smtp.*_tls_ciphers"
# If found → Service is overriding policy!
Solution:
# Remove overrides from config files
# Let crypto-policy handle TLS settings
# Apache: Remove or comment out
# #SSLProtocol all -SSLv3
# #SSLCipherSuite ...
# NGINX: Remove
# #ssl_protocols ...
# #ssl_ciphers ...
# Restart service
sudo systemctl restart httpd
31.3 Crypto-Policy Not Applied
Policy Set But Not Taking Effect
Symptoms:
- Changed policy but services still use old settings
- Weak ciphers still accepted
Diagnosis:
#============================================#
# VERIFY POLICY IS ACTIVE
#============================================#
# Step 1: Confirm policy set
update-crypto-policies --show
# Step 2: Check when policy was last updated
ls -l /etc/crypto-policies/back-ends/
# Step 3: Check if services restarted
systemctl status httpd nginx postfix | grep "Active:"
# Services MUST be restarted after policy change!
# Step 4: Test actual ciphers in use
openssl s_client -connect localhost:443 | grep "Cipher"
Solution:
# Restart ALL services
sudo systemctl restart httpd nginx postfix slapd
# Or reboot (ensures everything picks up changes)
sudo reboot
# Verify after restart
openssl s_client -connect localhost:443
31.4 FIPS Policy Issues
FIPS Policy Failures
Symptom: Services fail in FIPS mode
Diagnosis:
#============================================#
# DIAGNOSE FIPS ISSUES
#============================================#
# Step 1: Verify FIPS mode enabled
fips-mode-setup --check
# Step 2: Check crypto-policy
update-crypto-policies --show
# Should show: FIPS
# Step 3: Check for non-FIPS algorithms
# Common culprits: MD5, SHA-1, weak ciphers
# Step 4: Test with FIPS provider
openssl list -providers | grep fips
Common FIPS Issues:
# Issue: Application uses MD5 (not FIPS-approved)
# Error: "digital envelope routines:EVP_DigestInit_ex:disabled for fips"
# Solution: Update application to use SHA-256
# Issue: Certificate has SHA-1 signature
# Error: "ca md too weak"
# Solution: Reissue certificate with SHA-256 or better
31.5 Policy Compatibility Testing
Before Changing Policy
#!/bin/bash
# test-crypto-policy-change.sh
# Test crypto-policy change before production
NEW_POLICY=$1 # DEFAULT, LEGACY, FUTURE, or FIPS
if [ -z "$NEW_POLICY" ]; then
echo "Usage: $0 <policy>"
exit 1
fi
echo "=== Testing Crypto-Policy Change to $NEW_POLICY ==="
# Save current policy
CURRENT=$(update-crypto-policies --show)
echo "Current policy: $CURRENT"
# Change policy
echo "Changing to $NEW_POLICY..."
sudo update-crypto-policies --set "$NEW_POLICY"
# Restart services
echo "Restarting services..."
sudo systemctl restart httpd nginx postfix 2>/dev/null
# Wait for services to start
sleep 3
# Test each service
echo ""
echo "Testing services:"
# Apache
if systemctl is-active --quiet httpd; then
curl -ks https://localhost/ >/dev/null && \
echo "✅ Apache: OK" || echo "❌ Apache: FAILED"
else
echo "❌ Apache: Not running"
fi
# NGINX
if systemctl is-active --quiet nginx; then
curl -ks https://localhost:8443/ >/dev/null && \
echo "✅ NGINX: OK" || echo "❌ NGINX: FAILED"
else
echo "⚠️ NGINX: Not installed"
fi
# Postfix
if systemctl is-active --quiet postfix; then
timeout 3 openssl s_client -starttls smtp -connect localhost:25 </dev/null &>/dev/null && \
echo "✅ Postfix: OK" || echo "❌ Postfix: FAILED"
else
echo "⚠️ Postfix: Not installed"
fi
# Ask to keep or revert
echo ""
read -p "Keep $NEW_POLICY policy? (y/n): " KEEP
if [ "$KEEP" != "y" ]; then
echo "Reverting to $CURRENT..."
sudo update-crypto-policies --set "$CURRENT"
sudo systemctl restart httpd nginx postfix 2>/dev/null
echo "✅ Reverted"
else
echo "✅ Keeping $NEW_POLICY policy"
fi
31.6 Troubleshooting Workflow
Systematic Approach
Crypto-Policy Issue?
│
├─ Step 1: Identify current policy
│ └─ update-crypto-policies --show
│
├─ Step 2: Check if policy changed recently
│ └─ Check /var/log/messages for "crypto-policies"
│
├─ Step 3: Test with different policy
│ └─ sudo update-crypto-policies --set LEGACY
│ └─ If works → policy was too strict
│
├─ Step 4: Identify incompatibility
│ └─ openssl s_client -cipher 'ALL' -tls1
│ └─ Find what client/server needs
│
├─ Step 5: Choose fix
│ ├─ A) Update client (best)
│ ├─ B) Create custom module (good)
│ ├─ C) Use less strict policy (acceptable)
│ └─ D) Per-app override (last resort)
│
└─ Step 6: Test and document
└─ Verify fix works
└─ Document why change needed
31.7 Debugging Crypto-Policy Application
Verify Policy Is Applied
#============================================#
# VERIFY CRYPTO-POLICY APPLICATION
#============================================#
# Step 1: Check policy
update-crypto-policies --show
# Step 2: Check back-end files were updated
ls -l /etc/crypto-policies/back-ends/
# Files should be recently modified
# Step 3: View OpenSSL configuration
cat /etc/crypto-policies/back-ends/opensslcnf.config
# Step 4: Test actual cipher availability
openssl ciphers -v | grep -E "TLS|SSL"
# Step 5: Test connection
openssl s_client -connect localhost:443
# Look for: Protocol version, Cipher
# Step 6: Check if service restarted since policy change
systemctl status httpd | grep "Active:"
# Should show recent activation time
31.8 Common Scenarios
Scenario 1: Legacy Application After RHEL 8 Upgrade
Problem: App worked on RHEL 7, fails on RHEL 8
Root Cause: RHEL 7 had no crypto-policies, RHEL 8 DEFAULT blocks TLS 1.0/1.1
Solution:
# Quick fix (temporary!):
sudo update-crypto-policies --set LEGACY
# Proper fix:
# Update application to support TLS 1.2+
# Document exception
echo "Application X requires LEGACY policy due to TLS 1.0 requirement" > \
/etc/crypto-policies/POLICY-EXCEPTION.txt
Scenario 2: Can’t Connect to Windows Server 2008
Problem: RHEL 9 can’t connect to old Windows server
Cause: Windows Server 2008 only supports TLS 1.0
Solutions:
# Option 1: Upgrade Windows (best)
# Option 2: LEGACY policy (temporary)
sudo update-crypto-policies --set LEGACY
# Option 3: Custom policy module for this specific case
# See Chapter 23
31.9 Key Takeaways
- Crypto-policies are RHEL 8+ only (not RHEL 7)
- Services MUST restart after policy change
- Policy changes are system-wide - Affect everything
- DEFAULT is recommended for most environments
- LEGACY should be temporary only
- Test before deploying new policies
- Update clients rather than weaken policy
Quick Reference Card
┌───────────────────────────────────────────────────────────────┐
│ CRYPTO-POLICY TROUBLESHOOTING │
├───────────────────────────────────────────────────────────────┤
│ Check: update-crypto-policies --show │
│ Set: sudo update-crypto-policies --set <POLICY> │
│ Revert: sudo update-crypto-policies --set DEFAULT │
│ │
│ Back-ends: /etc/crypto-policies/back-ends/ │
│ OpenSSL: cat .../back-ends/opensslcnf.config │
│ │
│ Test: openssl ciphers -v │
│ openssl s_client -connect :443 │
│ │
│ After change: sudo systemctl restart <all-services> │
│ OR: sudo reboot │
│ │
│ Debug: grep -r "SSLProtocol\|ssl_protocols" /etc/ │
│ (look for overrides) │
└───────────────────────────────────────────────────────────────┘
⚠️ RHEL 7 doesn't have crypto-policies
✅ Always restart services after policy change
✅ DEFAULT works for 95% of cases
Chapter Navigation
Chapter 32: SOS Report Analysis
Support Essential: SOS reports are RHEL’s system diagnostic tool. Learn how to extract certificate information from SOS reports for troubleshooting.
32.1 What is an SOS Report?
sosreport is Red Hat’s diagnostic data collection tool.
Contains:
- ✅ System configuration files
- ✅ Log files
- ✅ Command outputs
- ✅ Package lists
- ✅ Certificate information
- ✅ Security settings
- ❌ Private keys (excluded for security!)
Use Cases:
- Opening Red Hat support cases
- Post-incident analysis
- Pre-migration audits
- Security compliance checks
32.2 Generating an SOS Report
Basic SOS Report Generation
#============================================#
# GENERATE SOS REPORT
#============================================#
# Install sos (usually pre-installed)
sudo dnf install sos -y
# Generate report
sudo sos report
# Interactive prompts:
# - Case ID (optional)
# - Description
# - Confirm
# Output:
# /var/tmp/sosreport-hostname-YYYYMMDDHHMMSS.tar.xz
# Extract
tar xf /var/tmp/sosreport-*.tar.xz
cd sosreport-*/
Certificate-Focused SOS Report
#============================================#
# SOS REPORT WITH CERTIFICATE FOCUS
#============================================#
# Generate with specific plugins
sudo sos report \
--batch \
--enable-plugins crypto,openssl,certmonger,freeipa \
--case-id "CASE12345"
# Or specify what to include
sudo sos report \
--batch \
-o crypto \
-o openssl \
-o certmonger \
-o pki
32.3 Finding Certificate Information in SOS
Key Locations in SOS Report
#============================================#
# CERTIFICATE-RELATED FILES IN SOS REPORT
#============================================#
# After extracting sosreport-*.tar.xz:
cd sosreport-*/
# Certificate files (public only, no private keys!)
ls -la etc/pki/tls/certs/
ls -la etc/pki/ca-trust/source/anchors/
# certmonger tracking
cat sos_commands/certmonger/getcert_list
# OpenSSL version
cat sos_commands/crypto/openssl_version
# Crypto-policy (RHEL 8+)
cat sos_commands/crypto/update-crypto-policies_--show
# Trust store
ls -la etc/pki/ca-trust/extracted/
# Service configurations
cat etc/httpd/conf.d/ssl.conf
cat etc/nginx/nginx.conf
cat etc/postfix/main.cf | grep tls
# Certificate expiration check
cat sos_commands/crypto/openssl_x509_-in_*
# System info
cat etc/redhat-release
cat sos_commands/kernel/uname_-a
32.4 Analyzing Certificate Issues from SOS
Certificate Expiration Analysis
#============================================#
# CHECK CERTIFICATE EXPIRATION IN SOS
#============================================#
# Navigate to SOS report directory
cd sosreport-hostname-*/
# Find all certificate inspection outputs
find sos_commands/crypto/ -name "*x509*" -type f
# Check each certificate
for cert_output in sos_commands/crypto/openssl_x509_*.txt; do
echo "=== $cert_output ==="
grep -E "(Subject:|Not After)" "$cert_output"
echo ""
done
# Or extract expiration dates
grep -r "Not After" sos_commands/crypto/ | sort
certmonger Status Analysis
#============================================#
# ANALYZE CERTMONGER FROM SOS
#============================================#
# certmonger list output
cat sos_commands/certmonger/getcert_list
# Look for:
# - status: CA_UNREACHABLE ← Problem!
# - status: CA_REJECTED ← Problem!
# - expires: <date> ← Check if soon
# Count certificates by status
grep "status:" sos_commands/certmonger/getcert_list | sort | uniq -c
# Find problematic certificates
grep -B10 "CA_UNREACHABLE\|CA_REJECTED" sos_commands/certmonger/getcert_list
Crypto-Policy Analysis (RHEL 8+)
#============================================#
# CHECK CRYPTO-POLICY IN SOS
#============================================#
# Current policy
cat sos_commands/crypto/update-crypto-policies_--show
# Check for overrides
grep -r "SSLProtocol\|SSLCipherSuite" etc/httpd/
grep -r "ssl_protocols\|ssl_ciphers" etc/nginx/
grep -r "tls_protocols" etc/postfix/main.cf
# If overrides found: Document that service opts out of crypto-policy
32.5 Common SOS Report Findings
Finding 1: Expired Certificates
In SOS Report:
# Check certificate expirations
grep "Not After" sos_commands/crypto/* | \
while read line; do
# Parse and check if expired
echo "$line"
done
Red Flags:
- Certificates expired before SOS report generation
- Certificates expiring within 30 days
- Multiple expired certificates
Finding 2: certmonger Issues
In SOS Report:
# Check certmonger status
cat sos_commands/certmonger/getcert_list | grep -A15 "Request ID"
# Common issues:
# - Multiple CA_UNREACHABLE (IPA connectivity issue)
# - CA_REJECTED (permissions/principal issue)
# - Old expiration dates with no renewal (certmonger not working)
Finding 3: Missing Intermediate Certificates
In SOS Report:
# Check certificate chain
# If service config points to cert without intermediate:
grep "SSLCertificateFile" etc/httpd/conf.d/ssl.conf
# /etc/pki/tls/certs/server.crt ← Check if this includes chain
# Check actual certificate
openssl x509 -in etc/pki/tls/certs/server.crt -noout -text
# Look for: Issuer (if not well-known, need intermediate)
32.6 SOS Report Checklist for Certificates
Systematic Analysis
## SOS Report Certificate Analysis Checklist
### System Information
- [ ] RHEL version (`cat etc/redhat-release`)
- [ ] OpenSSL version (`cat sos_commands/crypto/openssl_version`)
- [ ] Crypto-policy (`cat sos_commands/crypto/update-crypto-policies*`)
- [ ] FIPS mode (`grep FIPS sos_commands/crypto/*`)
### Certificate Files
- [ ] List certificates (`ls etc/pki/tls/certs/`)
- [ ] Check permissions (`ls -la etc/pki/tls/private/`)
- [ ] Verify ownership
- [ ] Check SELinux contexts (`ls -Z etc/pki/tls/`)
### Certificate Validity
- [ ] Check expirations (`grep "Not After" sos_commands/crypto/*`)
- [ ] Identify expired certificates
- [ ] Identify certificates expiring soon (< 30 days)
- [ ] Check signature algorithms (SHA-1 = problem on RHEL 9+)
### certmonger Status (if used)
- [ ] certmonger running? (`cat sos_commands/systemd/systemctl_list-units`)
- [ ] Tracked certificates (`cat sos_commands/certmonger/getcert_list`)
- [ ] Any CA_UNREACHABLE or CA_REJECTED?
- [ ] Renewal schedule appropriate?
### Service Configurations
- [ ] Apache SSL config (`cat etc/httpd/conf.d/ssl.conf`)
- [ ] NGINX SSL config (`cat etc/nginx/nginx.conf`)
- [ ] Postfix TLS config (`grep tls etc/postfix/main.cf`)
- [ ] OpenLDAP TLS config
- [ ] Certificate paths correct?
### Trust Store
- [ ] Custom CAs (`ls etc/pki/ca-trust/source/anchors/`)
- [ ] Trust bundle updated
- [ ] Blacklisted certs (RHEL 8+)
### Logs
- [ ] Recent certificate errors (`grep -i cert var/log/messages`)
- [ ] SSL/TLS errors in service logs
- [ ] SELinux denials (`grep AVC var/log/audit/audit.log | grep cert`)
### Recommendations
- [ ] List certificate issues found
- [ ] Prioritize by severity
- [ ] Suggest remediation steps
32.7 Automated SOS Analysis Script
Certificate Issue Finder
#!/bin/bash
# analyze-sos-certificates.sh
# Automated certificate issue detection in SOS reports
SOS_DIR=$1
if [ -z "$SOS_DIR" ] || [ ! -d "$SOS_DIR" ]; then
echo "Usage: $0 /path/to/sosreport-directory"
exit 1
fi
cd "$SOS_DIR"
echo "=== SOS Report Certificate Analysis ==="
echo "Report: $(basename $SOS_DIR)"
echo ""
# System info
echo "System Information:"
echo " RHEL Version: $(cat etc/redhat-release 2>/dev/null)"
echo " OpenSSL: $(cat sos_commands/crypto/openssl_version 2>/dev/null | head -2)"
if [ -f sos_commands/crypto/update-crypto-policies_--show ]; then
echo " Crypto-Policy: $(cat sos_commands/crypto/update-crypto-policies_--show)"
fi
echo ""
# Certificate expiration
echo "Certificate Expiration:"
if [ -d sos_commands/crypto ]; then
grep -h "Not After" sos_commands/crypto/openssl_x509_* 2>/dev/null | \
while read line; do
echo " $line"
done
else
echo " No certificate data found"
fi
echo ""
# certmonger status
echo "certmonger Status:"
if [ -f sos_commands/certmonger/getcert_list ]; then
STATUS_COUNT=$(grep "status:" sos_commands/certmonger/getcert_list | sort | uniq -c)
echo "$STATUS_COUNT"
# Highlight problems
if grep -q "CA_UNREACHABLE\|CA_REJECTED" sos_commands/certmonger/getcert_list; then
echo " ⚠️ Issues found:"
grep -B5 "CA_UNREACHABLE\|CA_REJECTED" sos_commands/certmonger/getcert_list | \
grep -E "(Request ID|status:)" | head -20
fi
else
echo " certmonger not installed or no data"
fi
echo ""
# Check for common issues
echo "Potential Issues:"
ISSUES=0
# Expired certs (basic check)
if grep -q "Not After.*202[0-3]" sos_commands/crypto/* 2>/dev/null; then
echo " ⚠️ Potentially expired certificates found"
((ISSUES++))
fi
# certmonger problems
if grep -q "CA_UNREACHABLE" sos_commands/certmonger/getcert_list 2>/dev/null; then
echo " ⚠️ certmonger CA_UNREACHABLE status found"
((ISSUES++))
fi
# SELinux denials
if grep -q "avc.*denied.*cert" var/log/audit/audit.log 2>/dev/null; then
echo " ⚠️ SELinux denials related to certificates"
((ISSUES++))
fi
if [ $ISSUES -eq 0 ]; then
echo " ✅ No obvious issues detected"
fi
echo ""
echo "=== Analysis Complete ==="
32.8 Key Files to Check in SOS
Critical Certificate Files
sosreport-hostname-YYYYMMDDHHMMSS/
├── etc/
│ ├── pki/
│ │ ├── tls/certs/ ← Certificates (public)
│ │ ├── ca-trust/ ← Trust store
│ │ └── nssdb/ ← NSS databases
│ ├── httpd/conf.d/ssl.conf ← Apache config
│ ├── nginx/nginx.conf ← NGINX config
│ └── postfix/main.cf ← Postfix config
│
├── sos_commands/
│ ├── crypto/
│ │ ├── openssl_version ← OpenSSL version
│ │ ├── openssl_x509_* ← Certificate inspections
│ │ └── update-crypto-policies_--show ← Policy
│ │
│ ├── certmonger/
│ │ └── getcert_list ← certmonger status
│ │
│ ├── systemd/
│ │ └── systemctl_list-units ← Service status
│ │
│ └── networking/
│ └── ss_-tulpn ← Listening ports
│
└── var/log/
├── messages ← System log
├── httpd/ssl_error_log ← Apache SSL errors
└── audit/audit.log ← SELinux denials
32.9 Common SOS Report Scenarios
Scenario 1: Website Down - Certificate Issue?
Analysis Steps:
# 1. Check if httpd was running
grep "httpd.service" sos_commands/systemd/systemctl_list-units
# active (running) ← Service was up
# 2. Check SSL error log
tail var/log/httpd/ssl_error_log
# Look for certificate-related errors
# 3. Check certificate expiration
cat sos_commands/crypto/openssl_x509_*server.crt* | grep "Not After"
# 4. Check Apache config
cat etc/httpd/conf.d/ssl.conf | grep -E "SSLCertificate"
# 5. Check if files existed
ls -l etc/pki/tls/certs/ | grep server
Scenario 2: certmonger Renewal Failures
Analysis Steps:
# 1. Check certmonger status
cat sos_commands/certmonger/getcert_list
# 2. Look for CA_UNREACHABLE
grep "CA_UNREACHABLE" sos_commands/certmonger/getcert_list
# 3. Check IPA connectivity (if using FreeIPA)
grep "ipa" var/log/messages | tail -50
# 4. Check Kerberos tickets
cat sos_commands/kerberos/klist* 2>/dev/null
# 5. Identify when renewal should have happened
# Look for expiration dates, calculate 2/3 of lifetime
32.10 Key Takeaways
- SOS reports are invaluable for remote troubleshooting
- No private keys included (security!)
- Certificate information IS included (public certs, config, logs)
- certmonger status preserved in getcert_list output
- Crypto-policy recorded (RHEL 8+)
- Use for post-incident analysis and audits
- Automate analysis with scripts
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ SOS REPORT CERTIFICATE ANALYSIS │
├──────────────────────────────────────────────────────────────┤
│ Generate: sudo sos report │
│ Extract: tar xf sosreport-*.tar.xz │
│ │
│ Key Files: etc/pki/tls/certs/ │
│ etc/httpd/conf.d/ssl.conf │
│ sos_commands/certmonger/getcert_list │
│ sos_commands/crypto/openssl_version │
│ sos_commands/crypto/update-crypto-policies* │
│ var/log/httpd/ssl_error_log │
│ │
│ Common checks: │
│ - Certificate expiration dates │
│ - certmonger status │
│ - Service configurations │
│ - crypto-policy setting │
│ - SELinux denials │
└──────────────────────────────────────────────────────────────┘
⚠️ Private keys NOT included (security)
✅ Perfect for remote troubleshooting
Chapter Navigation
Chapter 33: Emergency Procedures
Production Down: When certificates break and services are offline, you need quick, reliable procedures. This chapter is your emergency playbook.
33.1 Emergency Response Philosophy
When production is down:
- ⏰ Speed matters - Every minute counts
- 🎯 Fix first, investigate later - Get services running
- 📝 Document everything - For post-mortem
- 🔄 Temporary is OK - Proper fix comes after recovery
This chapter provides:
- Quick diagnostic procedures
- Emergency workarounds
- Temporary certificates
- Roll back procedures
- Communication templates
33.2 Quick Diagnostic (First 60 Seconds)
Triage Questions
#============================================#
# EMERGENCY TRIAGE - 60 SECONDS
#============================================#
# Q1: What's broken?
systemctl status httpd nginx postfix
# Q2: When did it break?
journalctl -xe --since "10 minutes ago" | grep -i cert
# Q3: Certificate expired?
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -dates
# Q4: Recent changes?
rpm -qa --last | head -20 # Recent package updates
ausearch -m SYSCALL --start recent | grep cert # Recent cert file access
# Q5: Disk full?
df -h /etc/pki
# Q6: SELinux blocking?
ausearch -m avc -ts recent | grep cert
Decision Tree (First Response)
Certificate Issue Detected
│
├─ Service won't start?
│ ├─ File not found → Quick Fix #1: Restore from backup
│ ├─ Permission denied → Quick Fix #2: Fix permissions
│ └─ Invalid cert → Quick Fix #3: Use temporary cert
│
├─ Certificate expired?
│ └─ Quick Fix #4: Generate temp self-signed OR restore backup
│
├─ Chain validation failure?
│ └─ Quick Fix #5: Add missing CA OR use LEGACY policy
│
└─ Unknown/Complex?
└─ Escalate + Apply Quick Fix #6: Rollback to last known good
33.3 Quick Fix #1: Restore from Backup
Scenario: Certificate/key file missing or corrupted
Time: 2-5 minutes
#!/bin/bash
# emergency-restore-cert.sh
SERVICE=$1 # apache, nginx, postfix, etc.
BACKUP_DIR="/var/backups/certificates"
echo "=== EMERGENCY: Restoring $SERVICE Certificate ==="
# Stop service
systemctl stop $SERVICE
# Find most recent backup
LATEST=$(ls -dt $BACKUP_DIR/*/ | head -2)
echo "Using backup from: $LATEST"
# Restore certificate
if [ -f "$LATEST/${SERVICE}.crt" ]; then
cp "$LATEST/${SERVICE}.crt" /etc/pki/tls/certs/
chmod 644 /etc/pki/tls/certs/${SERVICE}.crt
echo "✅ Certificate restored"
else
echo "❌ No backup found for $SERVICE"
exit 1
fi
# Restore key
if [ -f "$LATEST/${SERVICE}.key" ]; then
cp "$LATEST/${SERVICE}.key" /etc/pki/tls/private/
chmod 600 /etc/pki/tls/private/${SERVICE}.key
echo "✅ Private key restored"
fi
# Start service
systemctl start $SERVICE
# Test
sleep 2
systemctl status $SERVICE
if systemctl is-active --quiet $SERVICE; then
echo "✅ SUCCESS: $SERVICE is running"
exit 0
else
echo "❌ FAILED: $SERVICE did not start"
journalctl -xe -u $SERVICE | tail -20
exit 1
fi
33.4 Quick Fix #2: Fix Permissions Emergency
Scenario: Service fails with “permission denied” on certificate files
Time: 30 seconds
#!/bin/bash
# emergency-fix-permissions.sh
echo "=== EMERGENCY: Fixing Certificate Permissions ==="
# Fix certificate directory
chmod 755 /etc/pki/tls/certs/
chmod 644 /etc/pki/tls/certs/*.crt 2>/dev/null
# Fix private key directory
chmod 711 /etc/pki/tls/private/
chmod 600 /etc/pki/tls/private/*.key 2>/dev/null
# Fix ownership (adjust for your service)
chown root:root /etc/pki/tls/certs/*.crt 2>/dev/null
chown root:root /etc/pki/tls/private/*.key 2>/dev/null
# Fix SELinux contexts
restorecon -Rv /etc/pki/tls/
echo "✅ Permissions fixed"
# Show results
echo ""
echo "Certificate permissions:"
ls -lZ /etc/pki/tls/certs/*.crt 2>/dev/null | head -5
echo ""
echo "Key permissions:"
ls -lZ /etc/pki/tls/private/*.key 2>/dev/null | head -5
33.5 Quick Fix #3: Generate Temporary Self-Signed Certificate
Scenario: Certificate expired or invalid, need immediate fix
Time: 1-2 minutes
⚠️ WARNING: Self-signed certs cause browser warnings! Only for emergency internal use!
#!/bin/bash
# emergency-self-signed-cert.sh
HOSTNAME=${1:-$(hostname -f)}
DAYS=${2:-30}
CERT_PATH="/etc/pki/tls/certs/${HOSTNAME}-temp.crt"
KEY_PATH="/etc/pki/tls/private/${HOSTNAME}-temp.key"
echo "=== EMERGENCY: Generating Temporary Self-Signed Certificate ==="
echo "Hostname: $HOSTNAME"
echo "Valid for: $DAYS days"
# Generate self-signed certificate
openssl req -x509 -nodes -days $DAYS \
-newkey rsa:2048 \
-keyout "$KEY_PATH" \
-out "$CERT_PATH" \
-subj "/C=US/ST=Emergency/L=Emergency/O=Emergency/CN=$HOSTNAME" \
-addext "subjectAltName=DNS:$HOSTNAME,DNS:$(hostname -s)"
if [ $? -eq 0 ]; then
# Set permissions
chmod 600 "$KEY_PATH"
chmod 644 "$CERT_PATH"
echo "✅ Temporary certificate generated"
echo " Certificate: $CERT_PATH"
echo " Key: $KEY_PATH"
echo ""
echo "⚠️ CRITICAL: This is a TEMPORARY fix!"
echo " - Request proper certificate immediately"
echo " - Document this emergency action"
echo " - Plan proper replacement within $DAYS days"
echo ""
echo "To use with Apache:"
echo " SSLCertificateFile $CERT_PATH"
echo " SSLCertificateKeyFile $KEY_PATH"
# Show certificate
openssl x509 -in "$CERT_PATH" -noout -text | grep -E "(Subject:|Not After)"
else
echo "❌ FAILED to generate certificate"
exit 1
fi
33.6 Quick Fix #4: Emergency Certificate Renewal
Scenario: Certificate expired, need proper renewal ASAP
Time: 5-15 minutes (depends on CA)
#!/bin/bash
# emergency-renew-cert.sh
CERT_PATH=$1
KEY_PATH=$2
HOSTNAME=$3
echo "=== EMERGENCY: Renewing Expired Certificate ==="
# Generate new CSR
CSR_PATH="/tmp/emergency-$(date +%s).csr"
openssl req -new -key "$KEY_PATH" -out "$CSR_PATH" \
-subj "/CN=$HOSTNAME" \
-addext "subjectAltName=DNS:$HOSTNAME"
if [ $? -eq 0 ]; then
echo "✅ CSR generated: $CSR_PATH"
echo ""
echo "NEXT STEPS:"
echo "1. Submit CSR to CA immediately:"
echo " cat $CSR_PATH"
echo ""
echo "2. While waiting for CA:"
echo " - Use temporary self-signed cert (see Quick Fix #3)"
echo " - Or restore from backup (see Quick Fix #1)"
echo ""
echo "3. Once CA returns certificate:"
echo " cp new-cert.crt $CERT_PATH"
echo " systemctl reload <service>"
# If using FreeIPA
if command -v ipa-getcert &>/dev/null; then
echo ""
echo "4. If using FreeIPA, try automatic renewal:"
echo " sudo ipa-getcert resubmit -f $CERT_PATH"
fi
else
echo "❌ FAILED to generate CSR"
exit 1
fi
33.7 Quick Fix #5: Trust Chain Emergency
Scenario: “Unable to get local issuer certificate” error
Time: 1-2 minutes
#!/bin/bash
# emergency-fix-trust.sh
CA_CERT=$1 # Path to CA certificate
if [ -z "$CA_CERT" ] || [ ! -f "$CA_CERT" ]; then
echo "❌ Usage: $0 /path/to/ca-cert.crt"
exit 1
fi
echo "=== EMERGENCY: Adding CA to Trust Store ==="
# Copy CA to trust anchors
cp "$CA_CERT" /etc/pki/ca-trust/source/anchors/
# Update trust store
update-ca-trust extract
echo "✅ CA added to system trust store"
# Verify
if trust list | grep -q "$(basename "$CA_CERT" .crt)"; then
echo "✅ VERIFIED: CA is now trusted"
else
echo "⚠️ Warning: Could not verify CA was added"
fi
# Test certificate validation
echo ""
echo "Test your certificate now:"
echo " openssl verify /path/to/your/cert.crt"
Alternative: Temporary LEGACY Policy (RHEL 8+)
# If trust issue is due to weak algorithms
# TEMPORARY - revert after proper fix!
echo "=== EMERGENCY: Setting LEGACY Crypto Policy ==="
update-crypto-policies --show # Save current
sudo update-crypto-policies --set LEGACY
systemctl restart <service>
echo "⚠️ CRITICAL: This is temporary!"
echo "Proper fix required within 24 hours"
33.8 Quick Fix #6: Rollback to Last Known Good
Scenario: Recent change broke everything, need to revert
Time: 2-5 minutes
#!/bin/bash
# emergency-rollback.sh
echo "=== EMERGENCY: Rolling Back to Last Known Good Configuration ==="
# Stop service
systemctl stop httpd
# Backup current (broken) state
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p /var/backups/emergency/$TIMESTAMP
cp -a /etc/pki/tls/certs/*.crt /var/backups/emergency/$TIMESTAMP/ 2>/dev/null
cp -a /etc/pki/tls/private/*.key /var/backups/emergency/$TIMESTAMP/ 2>/dev/null
cp -a /etc/httpd/conf.d/ssl.conf /var/backups/emergency/$TIMESTAMP/ 2>/dev/null
# Restore from last backup
LAST_GOOD="/var/backups/certificates/last-known-good"
if [ -d "$LAST_GOOD" ]; then
cp -a "$LAST_GOOD"/*.crt /etc/pki/tls/certs/
cp -a "$LAST_GOOD"/*.key /etc/pki/tls/private/
cp -a "$LAST_GOOD"/ssl.conf /etc/httpd/conf.d/ 2>/dev/null
# Fix permissions
chmod 644 /etc/pki/tls/certs/*.crt
chmod 600 /etc/pki/tls/private/*.key
echo "✅ Rolled back to last known good"
else
echo "❌ No last-known-good backup found!"
echo "Looking for any recent backup..."
ls -ldt /var/backups/certificates/*/ | head -5
exit 1
fi
# Start service
systemctl start httpd
# Verify
sleep 2
if systemctl is-active --quiet httpd; then
echo "✅ SUCCESS: Service restored"
else
echo "❌ Service still not starting"
journalctl -xe -u httpd | tail -20
exit 1
fi
33.9 Service-Specific Emergency Procedures
Apache (httpd) Emergency Recovery
#============================================#
# APACHE EMERGENCY RECOVERY
#============================================#
# 1. Stop Apache
systemctl stop httpd
# 2. Check configuration syntax
apachectl configtest
# If fails, fix or restore ssl.conf from backup
# 3. Check certificate files exist
ls -l /etc/pki/tls/certs/server.crt
ls -l /etc/pki/tls/private/server.key
# 4. Emergency: Disable SSL temporarily
mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.disabled
systemctl start httpd
# Service now runs on HTTP only (port 80)
# 5. Fix certificates, then re-enable SSL
mv /etc/httpd/conf.d/ssl.conf.disabled /etc/httpd/conf.d/ssl.conf
systemctl reload httpd
NGINX Emergency Recovery
#============================================#
# NGINX EMERGENCY RECOVERY
#============================================#
# 1. Stop NGINX
systemctl stop nginx
# 2. Test configuration
nginx -t
# If fails, check which line/file has issue
# 3. Emergency: Comment out SSL config
sed -i 's/^\(\s*ssl_certificate\)/# \1/' /etc/nginx/nginx.conf
sed -i 's/^\(\s*listen.*443\)/# \1/' /etc/nginx/nginx.conf
sed -i 's/^\(\s*listen.*ssl\)/# \1/' /etc/nginx/nginx.conf
# 4. Start on HTTP only
systemctl start nginx
# 5. Fix certificates, restore SSL config
# Uncomment lines or restore from backup
systemctl reload nginx
certmonger Emergency
#============================================#
# CERTMONGER EMERGENCY RECOVERY
#============================================#
# 1. Check certmonger status
systemctl status certmonger
getcert list
# 2. If cert shows CA_UNREACHABLE
# Check IPA connectivity
ipa ping
# 3. Emergency: Stop tracking, manual renewal
REQUEST_ID=$(getcert list | grep "Request ID" | head -1 | awk -F"'" '{print $2}')
getcert stop-tracking -i $REQUEST_ID
# 4. Manual renewal with IPA
ipa-getcert request -f /etc/pki/tls/certs/server.crt \
-k /etc/pki/tls/private/server.key \
-D $(hostname -f) \
-K host/$(hostname -f)@REALM
# 5. If IPA unavailable, use temporary self-signed
./emergency-self-signed-cert.sh
33.10 Communication Templates
Incident Notification (Internal)
Subject: [URGENT] Certificate Issue - <Service> Down
INCIDENT SUMMARY:
- Service: <Apache/NGINX/etc>
- Impact: <Production/Staging> website down
- Started: <Time>
- Status: Investigating / Applying fix / Resolved
ROOT CAUSE:
- Certificate expired on <Date>
- OR: Certificate file permissions incorrect
- OR: CA trust chain missing
IMMEDIATE ACTION TAKEN:
- Temporary self-signed certificate applied
- Service restored at <Time>
NEXT STEPS:
- Request proper certificate from CA
- Replace temporary cert by <Date/Time>
- Post-mortem scheduled for <Date>
WORKAROUND:
- Users may see security warnings (expected)
- Service is functional despite warnings
Customer Communication (External)
Subject: Service Restoration - Brief Outage
Dear Customers,
We experienced a brief service interruption between <Start Time> and
<End Time> due to a certificate configuration issue. The service has
been fully restored.
You may notice a temporary security warning. This is expected and
safe to proceed. We are working to replace the temporary certificate
with a permanent one within the next few hours.
We apologize for any inconvenience.
Status updates: <URL>
Support: <Email/Phone>
33.11 Post-Emergency Checklist
After emergency recovery:
## Post-Emergency Checklist
### Immediate (Within 1 Hour)
- [ ] Service confirmed running
- [ ] Monitoring restored
- [ ] Stakeholders notified
- [ ] Temporary fix documented
### Short-Term (Within 24 Hours)
- [ ] Proper certificate obtained
- [ ] Temporary cert replaced
- [ ] Configuration validated
- [ ] Backups verified working
### Follow-Up (Within 1 Week)
- [ ] Root cause analysis completed
- [ ] Post-mortem document created
- [ ] Prevention measures identified
- [ ] Monitoring/alerting improved
- [ ] Documentation updated
- [ ] Team debriefed
### Prevention
- [ ] Add monitoring for this scenario
- [ ] Update runbooks
- [ ] Schedule earlier renewals
- [ ] Automate if possible
- [ ] Test recovery procedures
33.12 Emergency Contacts and Resources
Keep This Handy
## Emergency Certificate Response Card
### Quick Commands
openssl x509 -in cert.crt -noout -dates # Check expiry
systemctl status <service> # Service status
journalctl -xe -u <service> # Recent logs
getcert list # certmonger status
### Emergency Scripts Location
/usr/local/bin/emergency-*.sh
### Backup Location
/var/backups/certificates/
### Last Known Good
/var/backups/certificates/last-known-good/
### CA Information
CA URL: <URL>
CA Contact: <Email/Phone>
FreeIPA Server: <Hostname>
### Escalation
Team Lead: <Name> <Phone>
Manager: <Name> <Phone>
On-Call: <Pager/Phone>
### Documentation
Runbooks: <Wiki URL>
Previous Incidents: <Ticket System>
33.13 Emergency Scenarios Playbook
Scenario 1: Certificate Expired (Production Down)
Impact: HIGH - Service unavailable Time Pressure: Critical Response:
-
Assess (30 seconds)
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -dates -
Quick Fix (2 minutes)
./emergency-self-signed-cert.sh $(hostname -f) 30 # Update service config to use temp cert systemctl restart <service> -
Communicate (5 minutes)
- Notify stakeholders
- Update status page
-
Proper Fix (15-60 minutes)
# Request new cert from CA # OR use certmonger ipa-getcert resubmit -f /etc/pki/tls/certs/server.crt -
Replace temp cert, verify, document
Scenario 2: Wrong Certificate Deployed
Impact: MEDIUM - Service up but errors Time Pressure: Moderate Response:
-
Stop bleeding - Rollback
./emergency-rollback.sh -
Verify service restored
-
Identify correct certificate
-
Deploy correct cert with validation
-
Document what went wrong
Scenario 3: CA Server Down (Can’t Renew)
Impact: MEDIUM - Future renewals blocked Time Pressure: Depends on cert expiry Response:
-
Check cert expiry timeline
openssl x509 -in cert.crt -noout -checkend $((86400*7)) -
If > 7 days: Wait for CA recovery, monitor
-
If < 7 days:
- Generate temporary self-signed
- Contact CA support
- Escalate to management
-
Alternative: Use different CA temporarily
Scenario 4: SELinux Blocking Certificates
Impact: LOW-MEDIUM - Service won’t start Time Pressure: Moderate Response:
-
Check denials
ausearch -m avc -ts recent | grep cert -
Quick fix - Relabel
restorecon -Rv /etc/pki/tls/ -
If persists - Temporary permissive
setenforce 0 # TEMPORARY! systemctl restart <service> -
Proper fix - Generate policy
audit2allow -a -M mycert semodule -i mycert.pp setenforce 1
33.14 Emergency Toolkit
Create Emergency Response Kit
#!/bin/bash
# create-emergency-kit.sh
# Creates a portable emergency response kit
KIT_DIR="/root/cert-emergency-kit"
mkdir -p "$KIT_DIR"
# Copy emergency scripts
cp emergency-*.sh "$KIT_DIR/"
# Create quick reference
cat > "$KIT_DIR/QUICK_REFERENCE.txt" << 'EOF'
=== CERTIFICATE EMERGENCY QUICK REFERENCE ===
1. CHECK STATUS
systemctl status <service>
openssl x509 -in cert.crt -noout -dates
2. EXPIRED CERT
./emergency-self-signed-cert.sh $(hostname -f)
3. MISSING FILES
./emergency-restore-cert.sh <service>
4. PERMISSIONS
./emergency-fix-permissions.sh
5. ROLLBACK
./emergency-rollback.sh
6. LOGS
journalctl -xe -u <service>
tail -f /var/log/httpd/ssl_error_log
===========================
Last Updated: $(date)
EOF
# Set permissions
chmod 700 "$KIT_DIR"
chmod 755 "$KIT_DIR"/*.sh
echo "✅ Emergency kit created: $KIT_DIR"
ls -lh "$KIT_DIR"
33.15 Key Takeaways
- Speed over perfection in emergencies
- Temporary fixes are OK - Fix properly later
- Communication is critical - Keep stakeholders informed
- Document everything - For post-mortem
- Practice emergency procedures - Don’t wait for real incident
- Have backups ready - Test them regularly
- Know your escalation path - When to call for help
- Post-mortem is mandatory - Learn and improve
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ CERTIFICATE EMERGENCY RESPONSE │
├──────────────────────────────────────────────────────────────┤
│ EXPIRED CERT: ./emergency-self-signed-cert.sh $(hostname) │
│ MISSING FILE: ./emergency-restore-cert.sh <service> │
│ PERMISSIONS: ./emergency-fix-permissions.sh │
│ TRUST ISSUE: ./emergency-fix-trust.sh /path/to/ca.crt │
│ ROLLBACK: ./emergency-rollback.sh │
│ │
│ DISABLE SSL: mv ssl.conf ssl.conf.disabled │
│ systemctl restart <service> │
│ │
│ CHECK EXPIRY: openssl x509 -in cert.crt -noout -dates │
│ SERVICE LOGS: journalctl -xe -u <service> │
└──────────────────────────────────────────────────────────────┘
⚠️ REMEMBER: Fix first, investigate later!
🧪 Hands-On Lab
Lab 16: Emergency Procedures
Learn rapid certificate recovery techniques for production emergencies
- 📁 Location:
labs/en_US/16-emergency-procedures/ - ⏱️ Time: 30-40 minutes
- 🎯 Level: Advanced
Chapter Navigation
| ← Previous: Chapter 32 - SOS Report Analysis | Next: Chapter 34 - RHEL Migration Planning & Preparation → |
|---|
Chapter 34: RHEL Migration Planning & Preparation
Plan for Success: RHEL migrations require careful certificate planning. Learn how to audit, prepare, and plan certificate migration to avoid outages.
34.1 Why Certificate Planning Matters
Without Planning:
❌ Upgrade RHEL → Certificates fail validation
❌ Services won't start
❌ Production outage
❌ Rollback required
❌ Failed migration
With Planning:
✅ Pre-audit identifies issues
✅ Certificates prepared in advance
✅ Test migration successful
✅ Production migration smooth
✅ No certificate-related outages
34.2 Pre-Migration Certificate Audit
Complete Certificate Inventory
#!/bin/bash
# pre-migration-cert-audit.sh
# Complete certificate audit before RHEL migration
echo "=== Pre-Migration Certificate Audit ==="
echo "System: $(hostname)"
echo "Current RHEL: $(cat /etc/redhat-release)"
echo "Date: $(date)"
echo ""
# Find all certificates
echo "=== Certificate Inventory ==="
find /etc/pki/tls/certs/ /etc/httpd/ /etc/nginx/ /etc/postfix/ /etc/openldap/ \
-name "*.crt" -o -name "*.pem" 2>/dev/null | \
while read cert; do
if openssl x509 -in "$cert" -noout 2>/dev/null; then
echo "Certificate: $cert"
echo " Subject: $(openssl x509 -in "$cert" -noout -subject)"
echo " Issuer: $(openssl x509 -in "$cert" -noout -issuer)"
echo " Expires: $(openssl x509 -in "$cert" -noout -enddate | cut -d= -f2)"
# Check signature algorithm
SIG_ALG=$(openssl x509 -in "$cert" -noout -text | grep "Signature Algorithm" | head -2)
echo " Signature: $SIG_ALG"
# Check key size
KEY_SIZE=$(openssl x509 -in "$cert" -noout -text | grep "Public-Key" | grep -oP '\d+')
echo " Key Size: $KEY_SIZE bits"
# Flag issues
if echo "$SIG_ALG" | grep -qi "sha1"; then
echo " ⚠️ WARNING: SHA-1 signature (will fail on RHEL 9+)"
fi
if [ "$KEY_SIZE" -lt 2048 ]; then
echo " ⚠️ WARNING: Key < 2048 bits (may fail on RHEL 8+)"
fi
if ! openssl x509 -in "$cert" -noout -ext subjectAltName 2>/dev/null | grep -q "DNS:"; then
echo " ⚠️ WARNING: Certificat missing SAN"
fi
# Check expiration
if ! openssl x509 -in "$cert" -noout -checkend $((86400*90)); then
echo " ⚠️ WARNING: Expires within 90 days"
fi
echo ""
fi
done
# certmonger tracking
echo "=== certmonger Tracked Certificates ==="
if command -v getcert &>/dev/null; then
sudo getcert list | grep -E "(Request ID|certificate:|status:)"
else
echo "certmonger not installed"
fi
# Custom CAs
echo ""
echo "=== Custom CAs in Trust Store ==="
ls -la /etc/pki/ca-trust/source/anchors/
# Service configurations
echo ""
echo "=== Service Certificate Configurations ==="
echo "Apache:"
grep -h "SSLCertificate" /etc/httpd/conf.d/*.conf 2>/dev/null | grep -v "^#"
echo ""
echo "NGINX:"
grep -rh "ssl_certificate" /etc/nginx/ 2>/dev/null | grep -v "^#"
echo ""
echo "Postfix:"
sudo postconf | grep -E "smtpd_tls_cert|smtp_tls_cert"
echo ""
echo "=== Audit Complete ==="
echo "Save this output for migration reference!"
34.3 Certificate Issues to Fix Before Migration
Critical Pre-Migration Fixes
Fix 1: SHA-1 Signatures (RHEL 8→9)
# Find SHA-1 signed certificates
for cert in /etc/pki/tls/certs/*.crt; do
if openssl x509 -in "$cert" -noout -text 2>/dev/null | \
grep -qi "Signature Algorithm.*sha1"; then
echo "⚠️ SHA-1: $cert"
fi
done
# Action: Reissue ALL SHA-1 certificates before migrating to RHEL 9
Fix 2: Small Keys (< 2048 bits)
# Find small keys
for cert in /etc/pki/tls/certs/*.crt; do
SIZE=$(openssl x509 -in "$cert" -noout -text 2>/dev/null | \
grep "Public-Key" | grep -oP '\d+')
if [ "$SIZE" -lt 2048 ] 2>/dev/null; then
echo "⚠️ Small key ($SIZE): $cert"
fi
done
# Action: Reissue with 2048+ bit keys
Fix 3: Missing SANs
# Find certificates without SANs
for cert in /etc/pki/tls/certs/*.crt; do
if ! openssl x509 -in "$cert" -noout -ext subjectAltName 2>/dev/null | grep -q "DNS:"; then
echo "⚠️ No SANs: $cert"
fi
done
# Action: Reissue with proper SANs (required for modern browsers)
Fix 4: Expiring Soon
# Find certificates expiring within migration window
for cert in /etc/pki/tls/certs/*.crt; do
if ! openssl x509 -in "$cert" -noout -checkend $((86400*90)) 2>/dev/null; then
echo "⚠️ Expiring soon: $cert"
openssl x509 -in "$cert" -noout -enddate
fi
done
# Action: Renew before migration to avoid mid-migration expiration
34.4 Backup Strategy
What to Backup
#============================================#
# PRE-MIGRATION CERTIFICATE BACKUP
#============================================#
BACKUP_DIR="/var/backups/pre-migration-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
# Backup certificates and keys
sudo tar czf "$BACKUP_DIR/certificates.tar.gz" \
/etc/pki/tls/ \
/etc/pki/ca-trust/source/anchors/ \
/etc/pki/nssdb/
# Backup service configurations
sudo tar czf "$BACKUP_DIR/service-configs.tar.gz" \
/etc/httpd/conf.d/*.conf \
/etc/nginx/nginx.conf \
/etc/nginx/conf.d/ \
/etc/postfix/main.cf \
/etc/openldap/ \
/var/lib/pgsql/data/postgresql.conf \
/var/lib/pgsql/data/pg_hba.conf \
2>/dev/null
# Backup certmonger database
sudo tar czf "$BACKUP_DIR/certmonger.tar.gz" \
/var/lib/certmonger/
# Save certmonger list
sudo getcert list > "$BACKUP_DIR/certmonger-list.txt" 2>/dev/null
# Save crypto-policy (RHEL 8+)
update-crypto-policies --show > "$BACKUP_DIR/crypto-policy.txt" 2>/dev/null
# Create inventory CSV
./pre-migration-cert-audit.sh > "$BACKUP_DIR/certificate-inventory.txt"
# Set permissions
sudo chmod 700 "$BACKUP_DIR"
echo "✅ Backup complete: $BACKUP_DIR"
ls -lh "$BACKUP_DIR"
34.5 Testing Plan
Test Environment Setup
## Migration Testing Checklist
### Test Environment
- [ ] Clone production to test VM/container
- [ ] Same RHEL version as production
- [ ] Same certificates (copies, not originals!)
- [ ] Same service configurations
- [ ] Network isolated from production
### Test Migration
- [ ] Run migration on test system
- [ ] Verify all services start
- [ ] Test certificate validation
- [ ] Check crypto-policy (RHEL 7→8/9)
- [ ] Test client connections
- [ ] Verify certmonger tracking (if used)
- [ ] Document any issues
### Issue Resolution
- [ ] Fix issues found in test
- [ ] Update migration plan
- [ ] Re-test
- [ ] Document workarounds
### Production Readiness
- [ ] Test migration successful
- [ ] Issues documented and resolved
- [ ] Rollback plan ready
- [ ] Team trained
- [ ] Maintenance window scheduled
34.6 Migration Timeline
Sample Migration Schedule
Week 1-2: Planning & Audit
├─ Complete certificate inventory
├─ Identify issues (SHA-1, small keys, etc.)
├─ Plan remediation
└─ Set up test environment
Week 3-4: Remediation
├─ Reissue problematic certificates
├─ Update configurations
├─ Test in current environment
└─ Verify automation works
Week 5-6: Testing
├─ Clone production to test
├─ Perform test migration
├─ Validate certificates post-migration
├─ Document issues and fixes
└─ Update migration runbook
Week 7: Pre-Migration Prep
├─ Final certificate audit
├─ Renew expiring certificates
├─ Complete backups
├─ Brief team
└─ Verify rollback plan
Week 8: Migration
├─ Maintenance window
├─ Execute migration
├─ Validate certificates
├─ Monitor for 24-48 hours
└─ Document lessons learned
34.7 Rollback Planning
Certificate Rollback Procedure
#============================================#
# CERTIFICATE ROLLBACK PLAN
#============================================#
# If migration fails due to certificate issues:
# Step 1: RHEL rollback (using leapp or snapshots)
# See RHEL migration documentation
# Step 2: Restore certificates (if needed)
sudo tar xzf /var/backups/pre-migration-YYYYMMDD/certificates.tar.gz -C /
# Step 3: Restore service configs
sudo tar xzf /var/backups/pre-migration-YYYYMMDD/service-configs.tar.gz -C /
# Step 4: Restore certmonger
sudo tar xzf /var/backups/pre-migration-YYYYMMDD/certmonger.tar.gz -C /
# Step 5: Restart services
sudo systemctl restart httpd nginx postfix slapd
# Step 6: Verify
curl -v https://localhost/
sudo getcert list
34.8 Communication Plan
Stakeholder Communication Template
## RHEL Migration - Certificate Impact Assessment
### Migration Details
- **From:** RHEL X.Y
- **To:** RHEL X.Y
- **Date:** YYYY-MM-DD
- **Window:** XX:00 - XX:00 UTC
### Certificate Impact Analysis
- **Total Certificates:** XX
- **Certificates Requiring Action:** XX
- **Services Affected:** Apache, NGINX, Postfix, LDAP, etc.
### Pre-Migration Actions Required
- [ ] Reissue XX SHA-1 certificates
- [ ] Renew XX expiring certificates
- [ ] Update XX service configurations
- [ ] Test crypto-policy compatibility (RHEL 8+)
### During Migration
- **Expected Downtime:** X hours
- **Certificate Validation:** Post-migration
- **Rollback Plan:** Available if needed
### Post-Migration Validation
- [ ] All services start successfully
- [ ] Certificate validation working
- [ ] crypto-policy applied (RHEL 8+)
- [ ] certmonger tracking maintained
- [ ] Client connections successful
### Risk Mitigation
- Full backups completed
- Test migration successful
- Rollback procedure documented
- Team on standby
### Contact
- **Migration Lead:** Name <email>
- **Escalation:** Manager <email>
34.9 Migration Checklist
Complete Pre-Migration Checklist
## Certificate Migration Readiness Checklist
### Audit & Inventory (Week 1-3)
- [ ] Complete certificate inventory
- [ ] Document all certificate locations
- [ ] Identify all services using certificates
- [ ] Map certificate to service dependencies
- [ ] Document custom CAs in use
### Issue Identification (Week 2-4)
- [ ] Identify SHA-1 signed certificates
- [ ] Identify small keys (< 2048 bits)
- [ ] Identify certificates without SANs
- [ ] Identify expiring certificates (< 180 days)
- [ ] Identify hard-coded TLS configs (vs crypto-policy)
### Remediation (Week 3-6)
- [ ] Reissue all SHA-1 certificates
- [ ] Reissue small key certificates
- [ ] Add SANs to all certificates
- [ ] Renew expiring certificates
- [ ] Remove hard-coded TLS configs (prepare for crypto-policy)
### Testing (Week 5-7)
- [ ] Set up test environment
- [ ] Clone production certificates to test
- [ ] Perform test migration
- [ ] Validate all services start
- [ ] Test client connections
- [ ] Test crypto-policy (RHEL 7→8/9)
- [ ] Document issues found
- [ ] Resolve issues in test
- [ ] Re-test until clean
### Backup (Week 7)
- [ ] Full system backup
- [ ] Certificate-specific backup
- [ ] Service configuration backup
- [ ] certmonger database backup
- [ ] Test restore procedure
### Documentation (Week 7)
- [ ] Migration runbook complete
- [ ] Rollback procedure documented
- [ ] Issue workarounds documented
- [ ] Team briefed
- [ ] Stakeholders notified
### Final Prep (Day before)
- [ ] Verify backups
- [ ] Verify test environment
- [ ] Review runbook
- [ ] Confirm maintenance window
- [ ] Team roles assigned
34.10 Key Takeaways
- Plan ahead - Start 6-8 weeks before migration
- Audit thoroughly - Know every certificate
- Fix issues early - Don’t wait until migration day
- Test extensively - Multiple test runs
- Backup everything - Certificates, configs, certmonger DB
- Document clearly - Runbook, rollback, issues
- Communicate proactively - Keep stakeholders informed
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ MIGRATION PLANNING QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Timeline: 6-8 weeks before migration │
│ │
│ Pre-audit: Find all certificates │
│ Check signatures (SHA-1 → SHA-256) │
│ Check key sizes (< 2048 → 2048+) │
│ Check SANs (missing → add) │
│ Check expiration (< 180 days → renew) │
│ │
│ Backup: tar czf certs.tar.gz /etc/pki/tls/ │
│ getcert list > certmonger-list.txt │
│ update-crypto-policies --show > policy.txt │
│ │
│ Test: Clone to test environment │
│ Perform test migration │
│ Validate certificates work │
│ Document and fix issues │
└──────────────────────────────────────────────────────────────┘
⚠️ SHA-1 certificates WILL FAIL on RHEL 9+
⚠️ crypto-policies introduced in RHEL 8
✅ Test multiple times before production
Chapter Navigation
Chapter 35: RHEL 7→8 Migration
Big Jump: Migrating from RHEL 7 to RHEL 8 introduces crypto-policies - a revolutionary change in certificate management. Plan carefully!
35.1 Certificate Impact: MODERATE-HIGH
What Changes
| Feature | RHEL 7 | RHEL 8 | Impact |
|---|---|---|---|
| OpenSSL | 1.0.2k | 1.1.1k | Moderate |
| TLS Versions | 1.0/1.1/1.2 | 1.2/1.3 (DEFAULT) | HIGH |
| Crypto-Policies | None | NEW! | HIGH |
| Default Ciphers | Mixed | Stricter | Moderate |
| certmonger | Basic | Enhanced | Low |
| Management | Manual | Automated (crypto-policies) | HIGH |
Key Change: crypto-policies revolutionizes TLS management!
35.2 Pre-Migration Preparation
Certificate-Specific Pre-Migration Tasks
#============================================#
# RHEL 7→8 CERTIFICATE PREPARATION
#============================================#
# Task 1: Audit all certificates (see Ch 34)
./pre-migration-cert-audit.sh > rhel7-cert-audit.txt
# Task 2: Check for TLS 1.0/1.1 dependencies
# Review service configs
grep -r "TLSv1\|TLSv1.1" /etc/httpd/ /etc/nginx/ /etc/postfix/
# Task 3: Identify manual cipher configurations
# These will be overridden by crypto-policies!
grep -r "SSLCipherSuite\|ssl_ciphers\|smtp.*ciphers" /etc/httpd/ /etc/nginx/ /etc/postfix/
# Task 4: Test TLS 1.2 compatibility
# Ensure all clients support TLS 1.2+
# Task 5: Backup everything
sudo tar czf rhel7-complete-backup-$(date +%Y%m%d).tar.gz \
/etc/pki/ \
/etc/httpd/ \
/etc/nginx/ \
/etc/postfix/ \
/var/lib/certmonger/
35.3 Migration Using leapp
The leapp Utility
IMPORTANT: Use leapp for RHEL 7→8 migration (NOT redhat-upgrade-tool!)
leapp is Red Hat’s supported upgrade utility for RHEL 7→8 and 8→9.
#============================================#
# RHEL 7→8 MIGRATION WITH LEAPP
#============================================#
# Prerequisites
# - RHEL 7.9 (latest)
# - Valid Red Hat subscription
# - All updates applied
# - Backups complete
# Step 1: Update RHEL 7 to latest
sudo yum update -y
sudo reboot
# Step 2: Install leapp
sudo yum install leapp-upgrade -y
# Step 3: Run pre-upgrade check
sudo leapp preupgrade
# Review report:
cat /var/log/leapp/leapp-report.txt
# Common certificate-related inhibitors:
# - SHA-1 certificates
# - Weak cipher configurations
# - Unsupported packages
# Step 4: Fix issues identified
# Reissue SHA-1 certificates
# Update configurations
# Step 5: Perform upgrade
sudo leapp upgrade
# System downloads RHEL 8, prepares upgrade
# Reboots automatically
# Step 6: After reboot, system is RHEL 8!
cat /etc/redhat-release
# Red Hat Enterprise Linux release 8.X (Ootpa)
35.4 Post-Migration Certificate Validation
Immediate Post-Migration Checks
#============================================#
# POST-MIGRATION CERTIFICATE VALIDATION
#============================================#
# Check 1: Verify RHEL 8
cat /etc/redhat-release
openssl version
# Should show: OpenSSL 1.1.1k
# Check 2: Verify crypto-policy
update-crypto-policies --show
# DEFAULT (should be set automatically)
# Check 3: Check certificate files still present
ls -la /etc/pki/tls/certs/
ls -la /etc/pki/tls/private/
# Check 4: Verify permissions unchanged
ls -l /etc/pki/tls/private/*.key
# Should still be 600
# Check 5: Check custom CAs
ls -la /etc/pki/ca-trust/source/anchors/
# Check 6: Update trust store (just in case)
sudo update-ca-trust
# Check 7: Verify certmonger tracking
sudo getcert list
# All certificates should still be tracked
# Check 8: Check service configurations
# crypto-policies may have updated them
cat /etc/crypto-policies/back-ends/httpd.config
35.5 Service Restart and Testing
Restart All Services
#============================================#
# RESTART SERVICES AFTER MIGRATION
#============================================#
# Restart certificate-using services
sudo systemctl restart httpd
sudo systemctl restart nginx
sudo systemctl restart postfix
sudo systemctl restart slapd
sudo systemctl restart postgresql
sudo systemctl restart mariadb
# Check service status
systemctl status httpd nginx postfix | grep "Active:"
# Test each service
curl -v https://localhost/ # Apache/NGINX
openssl s_client -connect localhost:443 # HTTPS
openssl s_client -starttls smtp -connect localhost:25 # Postfix
openssl s_client -connect localhost:636 # LDAPS
35.6 Common RHEL 7→8 Certificate Issues
Issue 1: TLS 1.0/1.1 Clients Can’t Connect
Symptom: Old clients fail after migration
Cause: DEFAULT crypto-policy blocks TLS 1.0/1.1
Quick Fix (Temporary):
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd nginx postfix
Proper Fix:
# Update clients to support TLS 1.2+
# Or create custom policy module
Issue 2: Hard-Coded Ciphers Conflict with crypto-policy
Symptom: Service won’t start or behaves unexpectedly
Cause: Old config has SSLCipherSuite that conflicts
Fix:
# Remove hard-coded cipher configs
# Let crypto-policy handle it
# Apache: Remove from ssl.conf
# SSLProtocol ...
# SSLCipherSuite ...
# NGINX: Remove from nginx.conf
# ssl_protocols ...
# ssl_ciphers ...
# Postfix: Remove from main.cf
# smtpd_tls_protocols ...
# smtpd_tls_mandatory_ciphers ...
Issue 3: certmonger Tracking Lost
Symptom: getcert list shows empty or missing certificates
Rare but possible if migration had issues
Fix:
# Restore certmonger database from backup
sudo systemctl stop certmonger
sudo tar xzf /var/backups/pre-migration-*/certmonger.tar.gz -C /
sudo systemctl start certmonger
# Verify
sudo getcert list
35.7 Crypto-Policy Transition
Adopting Crypto-Policies
RHEL 7: No crypto-policies, manual config per service RHEL 8: crypto-policies manage TLS system-wide
#============================================#
# TRANSITION TO CRYPTO-POLICIES
#============================================#
# After migration to RHEL 8:
# Step 1: Check current policy
update-crypto-policies --show
# DEFAULT
# Step 2: Remove manual TLS configs from services
# (Let crypto-policy handle it)
# Step 3: Test with DEFAULT policy
sudo systemctl restart httpd nginx postfix
# Step 4: If old clients need TLS 1.0/1.1 (temporary!)
sudo update-crypto-policies --set LEGACY
# Step 5: Plan to move back to DEFAULT
# Update clients, then:
sudo update-crypto-policies --set DEFAULT
35.8 Migration Runbook Example
Step-by-Step Execution
## RHEL 7→8 Migration Runbook - Certificate Section
### Pre-Migration (T-24 hours)
- [ ] Verify backups complete and tested
- [ ] Verify all certificates valid > 90 days
- [ ] No SHA-1 certificates remaining
- [ ] Test environment migration successful
### Migration Window Start (T=0)
- [ ] Announce maintenance window
- [ ] Take final backup
- [ ] Run: `sudo leapp upgrade`
- [ ] System reboots automatically
### Post-Reboot (T+30 min)
- [ ] Verify RHEL 8: `cat /etc/redhat-release`
- [ ] Check crypto-policy: `update-crypto-policies --show`
- [ ] Verify certificates present: `ls /etc/pki/tls/certs/`
- [ ] Check certmonger: `sudo getcert list`
### Service Validation (T+45 min)
- [ ] Restart all services
- [ ] Test Apache: `curl -v https://localhost/`
- [ ] Test NGINX: `curl -v https://localhost:8443/`
- [ ] Test Postfix: `openssl s_client -starttls smtp -connect localhost:25`
- [ ] Test LDAP: `ldapsearch -H ldaps://localhost:636 -x -b ""`
- [ ] Test databases (if applicable)
### Client Testing (T+60 min)
- [ ] Test from Windows clients
- [ ] Test from Linux clients
- [ ] Test from application clients
- [ ] Verify no TLS errors
### Monitoring (T+2 hours to T+48 hours)
- [ ] Monitor logs for certificate errors
- [ ] Monitor service health
- [ ] Check certmonger renewals
- [ ] Verify no crypto-policy issues
### Completion
- [ ] Document any issues encountered
- [ ] Update runbook with lessons learned
- [ ] Close maintenance window
- [ ] Notify stakeholders of successful migration
35.9 Key Takeaways
- Use leapp utility for RHEL 7→8 migration (supported method)
- crypto-policies are NEW in RHEL 8 - Major change!
- TLS 1.0/1.1 disabled by default - Test client compatibility
- Remove manual TLS configs - Let crypto-policy manage
- Test extensively before production migration
- LEGACY policy available for compatibility (temporary!)
- certmonger tracking should survive migration
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ RHEL 7→8 MIGRATION CERTIFICATE CHECKLIST │
├──────────────────────────────────────────────────────────────┤
│ Before: Audit all certificates │
│ Reissue SHA-1 certificates │
│ Test TLS 1.2 compatibility │
│ Backup everything │
│ │
│ Migration: Use leapp upgrade (NOT redhat-upgrade-tool!) │
│ System reboots automatically │
│ │
│ After: Verify RHEL 8 │
│ Check crypto-policy (DEFAULT) │
│ Restart all services │
│ Test client connections │
│ Monitor for 48 hours │
│ │
│ New Feature: crypto-policies (system-wide TLS control) │
│ Blocked: TLS 1.0/1.1 (in DEFAULT policy) │
│ Fallback: LEGACY policy (if needed, temporary!) │
└──────────────────────────────────────────────────────────────┘
✅ Use leapp (officially supported)
⚠️ Major change: crypto-policies introduced
⚠️ Test client TLS 1.2 support before migration
🧪 Hands-On Lab
Lab 17: RHEL 7→8 Migration
Migrate certificates during OS upgrade to RHEL 8
- 📁 Location:
labs/en_US/17-rhel7to8-migration/ - ⏱️ Time: 40-50 minutes
- 🎯 Level: Advanced
Chapter Navigation
| ← Previous: Chapter 34 - RHEL Migration Planning & Preparation | Next: Chapter 36 - RHEL 8→9 Migration → |
|---|
Chapter 36: RHEL 8→9 Migration
OpenSSL 3.x Transition: RHEL 8→9 brings OpenSSL 3.x with provider architecture and stricter validation. Plan carefully for this significant change.
36.1 Certificate Impact: HIGH
What Changes
| Feature | RHEL 8 | RHEL 9 | Impact |
|---|---|---|---|
| OpenSSL | 1.1.1k | 3.5.5 | HIGH |
| Architecture | Traditional | Provider-based | HIGH |
| TLS 1.0/1.1 | LEGACY policy | Completely removed | HIGH |
| SHA-1 | Deprecated | Blocked | HIGH |
| Validation | Standard | Stricter | Moderate |
| Crypto-Policies | Basic | Subpolicies | Low |
| certmonger | Enhanced | Still native for IPA/tracking | Low |
Key Change: OpenSSL 3.x is a major architectural change!
36.2 Pre-Migration Requirements
Critical Certificate Fixes
Requirement 1: NO SHA-1 Signatures
#============================================#
# CHECK FOR SHA-1 (WILL FAIL ON RHEL 9!)
#============================================#
# Find SHA-1 signed certificates
for cert in /etc/pki/tls/certs/*.crt; do
SIG=$(openssl x509 -in "$cert" -noout -text 2>/dev/null | \
grep "Signature Algorithm" | head -2)
if echo "$SIG" | grep -qi "sha1"; then
echo "🚨 CRITICAL: SHA-1 signature: $cert"
echo " $SIG"
echo " ⚠️ MUST reissue before RHEL 9 migration!"
fi
done
# Action: Reissue ALL SHA-1 certificates before migration
# No exceptions - they WILL fail on RHEL 9
Requirement 2: All Certificates Valid
# Ensure no expired certificates
for cert in /etc/pki/tls/certs/*.crt; do
if ! openssl x509 -in "$cert" -noout -checkend 0 2>/dev/null; then
echo "❌ Expired: $cert"
fi
done
Requirement 3: Test Custom Applications
# If you have custom applications using OpenSSL
# They may need updates for OpenSSL 3.x API
rpm -qa | grep -E "custom|local"
# Test these applications in RHEL 9 environment before migration
36.3 Migration Using leapp
RHEL 8→9 Upgrade Process
#============================================#
# RHEL 8→9 MIGRATION WITH LEAPP
#============================================#
# Prerequisites
# - RHEL 8.10 (latest recommended)
# - Valid subscription
# - All updates applied
# - Backups complete
# - SHA-1 certificates reissued!
# Step 1: Update RHEL 8 fully
sudo dnf update -y
sudo reboot
# Step 2: Install leapp
sudo dnf install leapp-upgrade -y
# Step 3: Run pre-upgrade check
sudo leapp preupgrade
# Review report
cat /var/log/leapp/leapp-report.txt
# Certificate-related checks:
# - SHA-1 certificate warnings
# - OpenSSL compatibility
# - Custom app compatibility
# Step 4: Address inhibitors
# Fix any blocking issues
# Step 5: Perform upgrade
sudo leapp upgrade
# Downloads RHEL 9, prepares upgrade
# Reboots to perform upgrade
# Reboots again into RHEL 9
# Step 6: Verify RHEL 9
cat /etc/redhat-release
# Red Hat Enterprise Linux release 9.X (Plow)
openssl version
# OpenSSL 3.5.5
36.4 Post-Migration Validation
Certificate-Specific Validation
#============================================#
# POST-MIGRATION CERTIFICATE VALIDATION (RHEL 9)
#============================================#
# Check 1: OpenSSL version
openssl version
# OpenSSL 3.5.5 ← Confirm
# Check 2: Check providers
openssl list -providers
# Should show: default, fips, legacy, base
# Check 3: Verify certificates still present
ls -la /etc/pki/tls/certs/
ls -la /etc/pki/tls/private/
# Check 4: Test certificate validation
for cert in /etc/pki/tls/certs/*.crt; do
openssl verify "$cert" 2>&1 | grep -v "OK" && echo "Issue: $cert"
done
# Check 5: Verify crypto-policy
update-crypto-policies --show
# DEFAULT (should be maintained)
# Check 6: Test certificate operations
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -text
# Check 7: Verify certmonger tracking
sudo getcert list
# All certificates should still be tracked
# Check 8: Check trust store
trust list | head -20
36.5 Service Validation
Test All Services
#============================================#
# SERVICE VALIDATION POST-MIGRATION
#============================================#
# Restart services
sudo systemctl restart httpd nginx postfix slapd postgresql mariadb 2>/dev/null
# Test each service
echo "Testing Apache..."
curl -v https://localhost/ 2>&1 | grep -E "(SSL connection|subject:)"
echo "Testing with OpenSSL 3.x..."
openssl s_client -connect localhost:443 -tls1_3
echo "Testing Postfix..."
openssl s_client -starttls smtp -connect localhost:25 </dev/null
echo "Testing LDAPS..."
openssl s_client -connect localhost:636 </dev/null
# Check for provider errors
sudo journalctl --since "1 hour ago" | grep -i "provider\|unsupported"
36.6 Common RHEL 8→9 Issues
Issue 1: SHA-1 Certificates Rejected
Symptom:
openssl verify server.crt
# error 3 at 0 depth lookup: CA md too weak
Cause: Certificate has SHA-1 signature (blocked on RHEL 9)
Solution:
# NO WORKAROUND - Must reissue
# This should have been done pre-migration!
# Emergency: Reissue immediately
openssl req -new -key server.key -out server.csr -sha256
# Submit to CA, install new certificate
Issue 2: Legacy Algorithm Errors
Symptom:
openssl md5 file.txt
# Error: unsupported
Cause: MD5 and other legacy algorithms require explicit provider
Solution:
# Use legacy provider
openssl md5 -provider legacy file.txt
# Better: Update to use SHA-256
openssl sha256 file.txt
Issue 3: Custom Application OpenSSL 3.x Incompatibility
Symptom: Custom application fails with OpenSSL errors
Cause: Application compiled against OpenSSL 1.1.1, API changed in 3.x
Solution:
# Recompile application against OpenSSL 3.x
# Or update application code for new API
# Temporary workaround (if available):
# Use compat library (if provided)
36.7 crypto-policy Considerations
Crypto-Policy After Migration
#============================================#
# CRYPTO-POLICY POST-MIGRATION
#============================================#
# Check current policy (should be maintained)
update-crypto-policies --show
# RHEL 9 supports subpolicies!
# Example: Completely disable SHA-1
sudo update-crypto-policies --set DEFAULT:NO-SHA1
# List available modules
ls /usr/share/crypto-policies/policies/modules/
# Test policy
sudo systemctl restart httpd
curl -v https://localhost/
36.8 certmonger After Migration
Verify certmonger Functionality
#============================================#
# CERTMONGER POST-MIGRATION
#============================================#
# Check certmonger status
systemctl status certmonger
# List tracked certificates
sudo getcert list
# Check for issues
sudo getcert list | grep "status:" | grep -v "MONITORING"
# If using FreeIPA, test connectivity
ipa ping
# Force test renewal
sudo ipa-getcert resubmit -f /etc/pki/tls/certs/test.crt
# RHEL 9 still uses certmonger for IPA, local CA, and tracking workflows
# Use certbot separately for public Let's Encrypt certificates
36.9 Migration Runbook
Certificate-Focused Runbook
## RHEL 8→9 Migration - Certificate Section
### Pre-Migration (T-24 hours)
- [ ] Verify NO SHA-1 certificates (critical!)
- [ ] All certificates valid > 90 days
- [ ] Backups complete and tested
- [ ] Test migration successful
- [ ] Custom apps tested on RHEL 9
### Migration Window Start (T=0)
- [ ] Final backup
- [ ] Run: `sudo leapp upgrade`
- [ ] System reboots (twice)
### Post-Reboot Validation (T+45 min)
- [ ] Verify RHEL 9: `cat /etc/redhat-release`
- [ ] Verify OpenSSL 3.5.5: `openssl version`
- [ ] Check providers: `openssl list -providers`
- [ ] Verify certificates: `ls /etc/pki/tls/certs/`
- [ ] Check crypto-policy: `update-crypto-policies --show`
- [ ] Verify certmonger: `sudo getcert list`
### Service Restart (T+60 min)
- [ ] Restart all certificate-using services
- [ ] Test Apache/NGINX
- [ ] Test Postfix
- [ ] Test LDAP
- [ ] Test databases
### Certificate Validation (T+90 min)
- [ ] No SHA-1 rejections
- [ ] All certificates validate: `openssl verify`
- [ ] TLS 1.3 working: `openssl s_client -tls1_3`
- [ ] No provider errors in logs
- [ ] certmonger status all MONITORING
### Client Testing (T+2 hours)
- [ ] Test from all client types
- [ ] Verify no compatibility issues
- [ ] Check application functionality
### Post-Migration (24-48 hours)
- [ ] Monitor for OpenSSL 3.x issues
- [ ] Check certmonger renewals
- [ ] Monitor service logs
- [ ] Document any issues
36.10 Key Takeaways
- OpenSSL 3.x is major change - Provider architecture is new
- SHA-1 MUST be eliminated before migration - No exceptions!
- Use leapp for migration (officially supported)
- Test custom applications on RHEL 9 first
- Stricter validation catches more issues (good for security!)
- certmonger remains the native tracker for IPA and managed renewals on RHEL 9
- Subpolicies available for fine-tuning
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ RHEL 8→9 MIGRATION CERTIFICATE CHECKLIST │
├──────────────────────────────────────────────────────────────┤
│ CRITICAL: NO SHA-1 certificates! (will be rejected) │
│ Reissue all SHA-1 certs before migration │
│ │
│ Before: Verify NO SHA-1 signatures │
│ Test custom apps on RHEL 9 │
│ Backup everything │
│ │
│ Migration: Use leapp upgrade │
│ System reboots twice │
│ │
│ After: Verify OpenSSL 3.5.5 │
│ Check providers: openssl list -providers │
│ Test legacy algorithms need -provider legacy │
│ Restart all services │
│ Verify certmonger tracking maintained │
│ │
│ New: Provider architecture │
│ Subpolicies (DEFAULT:NO-SHA1) │
│ certmonger tracking still intact │
└──────────────────────────────────────────────────────────────┘
🚨 SHA-1 is BLOCKED - reissue before migration!
✅ OpenSSL 3.x brings stricter security
✅ Use certmonger for IPA/tracked renewals; certbot for public ACME
🧪 Hands-On Lab
Lab 18: RHEL 8→9 Migration
Handle OpenSSL 3.x and stricter security in RHEL 9
- 📁 Location:
labs/en_US/18-rhel8to9-migration/ - ⏱️ Time: 40-50 minutes
- 🎯 Level: Advanced
Chapter Navigation
| ← Previous: Chapter 35 - RHEL 7→8 Migration | Next: Chapter 37 - Migration Troubleshooting & Recovery → |
|---|
Chapter 37: Migration Troubleshooting & Recovery
When Things Go Wrong: Migrations don’t always go smoothly. This chapter covers common migration problems and recovery procedures.
37.1 Common Migration Issues
Top 10 Certificate Migration Problems
| Problem | Symptoms | Cause | Quick Fix |
|---|---|---|---|
| 1. Services won’t start | systemctl status fails | Config syntax changed | Restore config, update syntax |
| 2. SHA-1 rejection (RHEL 9) | “ca md too weak” | SHA-1 signature | Reissue certificate |
| 3. TLS version mismatch | Clients can’t connect | TLS 1.0/1.1 blocked | LEGACY policy (temp) |
| 4. crypto-policy issues | Various errors | New policy system | Understand & configure |
| 5. certmonger lost tracking | getcert list empty | DB corruption | Restore from backup |
| 6. Missing CAs | Cert verify failed | Trust store reset | Re-add CAs |
| 7. Permission changes | Permission denied | Ownership changed | Fix permissions |
| 8. SELinux denials | Service blocked | Context changed | Relabel files |
| 9. Provider errors (RHEL 9) | Algorithm unsupported | OpenSSL 3.x change | Use -provider legacy |
| 10. Performance degradation | Slow connections | Stricter crypto | Expected, or tune |
37.2 Rollback Procedures
When to Rollback
Rollback if:
- Critical services can’t start
- Certificate issues can’t be quickly fixed
- Business impact is severe
- Within rollback window (usually 24-48 hours)
leapp Rollback
#============================================#
# ROLLBACK RHEL MIGRATION
#============================================#
# leapp creates snapshot during upgrade
# Rollback BEFORE rebooting into new version
# During upgrade (if issues detected):
# Don't reboot - investigate and fix
# After upgrade but issues found:
# Check if within rollback window
# leapp doesn't have automatic rollback
# Use snapshot/backup to restore
# With LVM snapshot (if created pre-migration):
# Boot from snapshot
# Or restore from backup
Certificate-Specific Rollback
#============================================#
# RESTORE CERTIFICATES AFTER FAILED MIGRATION
#============================================#
# Scenario: Migrated, but certificate issues
# Need to restore certificate state
# Step 1: Stop services
sudo systemctl stop httpd nginx postfix slapd
# Step 2: Restore certificates
sudo tar xzf /var/backups/pre-migration-*/certificates.tar.gz -C /
# Step 3: Restore service configs
sudo tar xzf /var/backups/pre-migration-*/service-configs.tar.gz -C /
# Step 4: Restore certmonger
sudo systemctl stop certmonger
sudo tar xzf /var/backups/pre-migration-*/certmonger.tar.gz -C /
sudo systemctl start certmonger
# Step 5: Restore crypto-policy (if RHEL 8+)
POLICY=$(cat /var/backups/pre-migration-*/crypto-policy.txt)
sudo update-crypto-policies --set $POLICY
# Step 6: Start services
sudo systemctl start httpd nginx postfix slapd
# Step 7: Verify
curl -v https://localhost/
sudo getcert list
37.3 Service Won’t Start After Migration
Diagnosis
#============================================#
# SERVICE STARTUP TROUBLESHOOTING
#============================================#
# Check service status
systemctl status httpd
# View detailed errors
sudo journalctl -xe -u httpd
# Test configuration
# Apache:
sudo apachectl configtest
# NGINX:
sudo nginx -t
# Postfix:
sudo postfix check
# Common certificate-related errors:
# - File not found
# - Permission denied
# - Certificate format invalid
# - ca md too weak (SHA-1)
Solutions
Problem: Configuration Syntax Changed
# Some directives changed between versions
# Check release notes for changes
# Temporarily restore old config
sudo cp /var/backups/pre-migration-*/ssl.conf /etc/httpd/conf.d/
# Update to new syntax
# Research correct syntax for new version
Problem: Permission Changed During Migration
# Fix permissions
sudo chmod 600 /etc/pki/tls/private/*.key
sudo chmod 644 /etc/pki/tls/certs/*.crt
# Fix ownership
sudo chown root:root /etc/pki/tls/private/*.key
# Fix SELinux contexts
sudo restorecon -Rv /etc/pki/tls/
37.4 Client Connection Failures Post-Migration
TLS Version Incompatibility
Symptom: Clients can’t connect after migration to RHEL 8/9
Diagnosis:
# Test from server
openssl s_client -connect localhost:443 -tls1_2
# Works
openssl s_client -connect localhost:443 -tls1
# Fails (expected on RHEL 8/9 DEFAULT)
# Check crypto-policy
update-crypto-policies --show
# DEFAULT ← Blocks TLS 1.0/1.1
Temporary Solution:
# Allow TLS 1.0/1.1 temporarily
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd nginx postfix
# Test clients
# Document which clients need TLS 1.0/1.1
# Plan to update those clients, then revert to DEFAULT
Proper Solution:
# Update clients to support TLS 1.2+
# Then use DEFAULT policy
sudo update-crypto-policies --set DEFAULT
37.5 certmonger Issues Post-Migration
certmonger Tracking Lost
Symptom:
sudo getcert list
# (empty or missing certificates)
Solution:
# Restore certmonger database
sudo systemctl stop certmonger
sudo tar xzf /var/backups/pre-migration-*/certmonger.tar.gz -C /
sudo systemctl start certmonger
# Verify
sudo getcert list
# If still issues, re-add certificates manually
certmonger CA_UNREACHABLE After Migration
Common after RHEL upgrade
Solution:
# Renew Kerberos ticket
sudo kinit -k host/$(hostname -f)@REALM
# Restart certmonger
sudo systemctl restart certmonger
# Resubmit requests
for cert in $(sudo getcert list | grep "certificate:" | sed -n "s/.*location='\\([^']*\\)'.*/\\1/p"); do
sudo ipa-getcert resubmit -f "$cert"
done
37.6 Emergency Recovery Procedures
Emergency: All Services Down
Situation: Migration complete but nothing works
Quick Recovery:
#!/bin/bash
# emergency-post-migration-recovery.sh
echo "=== EMERGENCY: Post-Migration Certificate Recovery ==="
# 1. Check RHEL version (confirm migration happened)
cat /etc/redhat-release
# 2. Emergency: Disable SSL temporarily
# Apache
sudo mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.disabled
sudo systemctl start httpd
# Now Apache runs on HTTP only (port 80)
# 3. Identify certificate issues
sudo journalctl -xe | grep -i cert | tail -50
# 4. For RHEL 9: Check for SHA-1 rejections
grep "ca md too weak" /var/log/messages
# 5. Generate temporary self-signed certificates
/usr/local/bin/emergency-self-signed-cert.sh $(hostname -f) 90
# 6. Re-enable SSL with temp cert
sudo mv /etc/httpd/conf.d/ssl.conf.disabled /etc/httpd/conf.d/ssl.conf
# Update to use temp cert
sudo systemctl restart httpd
# 7. Services restored (with warnings)
# Plan proper certificate fixes
echo "✅ Emergency recovery complete"
echo "⚠️ Using temporary certificates - fix ASAP!"
37.7 Post-Migration Validation Script
Comprehensive Validation
#!/bin/bash
# post-migration-cert-validation.sh
echo "=== Post-Migration Certificate Validation ==="
ISSUES=0
# Check RHEL version
echo "1. RHEL Version:"
cat /etc/redhat-release
# Check OpenSSL
echo ""
echo "2. OpenSSL Version:"
openssl version
# Check crypto-policy (RHEL 8+)
if command -v update-crypto-policies &>/dev/null; then
echo ""
echo "3. Crypto-Policy:"
update-crypto-policies --show
fi
# Check certificates
echo ""
echo "4. Certificate Status:"
CERT_COUNT=0
EXPIRED=0
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
((CERT_COUNT++))
if ! openssl x509 -in "$cert" -noout -checkend 0 2>/dev/null; then
echo " ❌ EXPIRED: $cert"
((EXPIRED++))
((ISSUES++))
fi
# Check for SHA-1 (RHEL 9)
if [ "$(cat /etc/redhat-release)" =~ "release 9" ]; then
if openssl x509 -in "$cert" -noout -text | grep -qi "sha1.*Signature"; then
echo " ❌ SHA-1: $cert"
((ISSUES++))
fi
fi
done
echo " Total certificates: $CERT_COUNT"
echo " Expired: $EXPIRED"
# Check certmonger
echo ""
echo "5. certmonger Status:"
if command -v getcert &>/dev/null; then
sudo getcert list | grep "status:" | sort | uniq -c
UNREACHABLE=$(sudo getcert list | grep -c "CA_UNREACHABLE")
if [ $UNREACHABLE -gt 0 ]; then
echo " ⚠️ $UNREACHABLE certificates CA_UNREACHABLE"
((ISSUES++))
fi
else
echo " certmonger not installed"
fi
# Check services
echo ""
echo "6. Service Status:"
for svc in httpd nginx postfix slapd; do
if systemctl is-active --quiet $svc 2>/dev/null; then
echo " ✅ $svc: running"
elif systemctl is-enabled --quiet $svc 2>/dev/null; then
echo " ❌ $svc: not running (should be)"
((ISSUES++))
fi
done
# Test connections
echo ""
echo "7. Connection Tests:"
timeout 3 curl -ks https://localhost/ &>/dev/null && \
echo " ✅ HTTPS: OK" || echo " ❌ HTTPS: FAILED"
# Summary
echo ""
echo "==================================="
if [ $ISSUES -eq 0 ]; then
echo "✅ Migration validation PASSED!"
exit 0
else
echo "⚠️ $ISSUES issues found - review above"
exit 1
fi
37.8 Key Takeaways
- Have rollback plan ready before migration
- Most issues are fixable without rollback
- Crypto-policy changes cause most compatibility issues
- SHA-1 rejection is non-negotiable on RHEL 9
- Test, test, test before production migration
- Document everything during troubleshooting
- Emergency procedures (Ch 33) apply during migration too
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ MIGRATION TROUBLESHOOTING QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Service fails: Check: journalctl -xe -u <service> │
│ Try: Restore config from backup │
│ │
│ Cert rejected: Check: Signature algorithm (SHA-1?) │
│ Fix: Reissue with SHA-256+ │
│ │
│ Client fails: Check: TLS version support │
│ Temp: update-crypto-policies --set LEGACY │
│ Fix: Update client │
│ │
│ certmonger: Check: getcert list │
│ Fix: Restore /var/lib/certmonger/ │
│ │
│ Emergency: Disable SSL temporarily │
│ Generate temp self-signed │
│ Restore from backup │
│ │
│ Rollback: Use snapshot/backup │
│ Restore certificates │
│ Restore configs │
└──────────────────────────────────────────────────────────────┘
✅ Most issues are fixable without full rollback
⚠️ Have backups ready
⚠️ Test in non-production first
Chapter Navigation
Chapter 38: FIPS Mode Complete Guide
Federal Compliance: FIPS 140-2/140-3 compliance is required for US federal systems and many regulated industries. Learn how to enable and manage FIPS mode on RHEL.
38.1 What is FIPS?
FIPS = Federal Information Processing Standards
FIPS 140-2/140-3 = Cryptographic Module Validation Program
Purpose:
- ✅ Validate cryptographic modules meet security requirements
- ✅ Ensure proper implementation of approved algorithms
- ✅ Required for US federal government systems
- ✅ Often required for: Banking, healthcare, defense contractors
FIPS Validation Status by RHEL Version
| RHEL Version | FIPS Status | Standard | Notes |
|---|---|---|---|
| RHEL 7 | Validated | FIPS 140-2 | OpenSSL 1.0.2 modules validated |
| RHEL 8 | Validated | FIPS 140-2 | OpenSSL 1.1.1, NSS, libgcrypt validated |
| RHEL 9 | Validated | FIPS 140-2 | OpenSSL 3.x provider, transition to 140-3 in progress |
| RHEL 10 | In process | FIPS 140-2/140-3 | Transition ongoing, check current status |
Important: As of 2025, RHEL 9 uses FIPS 140-2 validated modules. The transition to FIPS 140-3 is in progress but not yet complete. Always verify current validation status at https://csrc.nist.gov/projects/cryptographic-module-validation-program
38.2 Enabling FIPS Mode
Installation-Time FIPS (Recommended)
Best Practice: Enable FIPS during RHEL installation
# At installation boot prompt, add:
fips=1
# System installs in FIPS mode from the start
# All cryptographic operations are FIPS-compliant from boot
Why Installation-Time is Better:
- Kernel properly configured
- All packages installed in FIPS mode
- No post-install migration needed
- Cleaner FIPS state
Post-Installation FIPS (RHEL 8/9/10)
#============================================#
# ENABLE FIPS MODE POST-INSTALLATION
#============================================#
# Check current FIPS status
fips-mode-setup --check
# FIPS mode is disabled.
# Enable FIPS mode
sudo fips-mode-setup --enable
# Output shows what will change:
# - Kernel boot parameters
# - Crypto policy
# - System reconfiguration
# MUST REBOOT!
sudo reboot
# After reboot, verify
fips-mode-setup --check
# FIPS mode is enabled.
# Verify crypto-policy
update-crypto-policies --show
# FIPS
# Verify FIPS provider loaded (RHEL 9+)
openssl list -providers | grep fips
# fips
# name: OpenSSL FIPS Provider
# version: 3.5.5
# status: active
38.3 FIPS Requirements for Certificates
Approved Algorithms
FIPS-Approved for Certificates:
✅ RSA: 2048, 3072, 4096 bits
✅ ECC: P-256 (secp256r1), P-384 (secp384r1), P-521 (secp521r1)
✅ Signatures: SHA-256, SHA-384, SHA-512
✅ TLS: 1.2, 1.3 only
Blocked in FIPS Mode:
❌ RSA < 2048 bits
❌ MD5, SHA-1
❌ TLS 1.0, 1.1
❌ 3DES, RC4, DES
❌ DSA keys
❌ Non-approved elliptic curves
38.4 Generating FIPS-Compliant Certificates
FIPS-Compliant Key Generation
#============================================#
# GENERATE FIPS-COMPLIANT KEYS
#============================================#
# Verify FIPS mode enabled
fips-mode-setup --check
# Generate RSA 2048 key (FIPS-compliant)
openssl genpkey -algorithm RSA -out fips-server.key \
-pkeyopt rsa_keygen_bits:2048
# RSA 3072 (stronger, still FIPS-compliant)
openssl genpkey -algorithm RSA -out fips-server.key \
-pkeyopt rsa_keygen_bits:3072
# EC P-256 (FIPS-approved curve)
openssl genpkey -algorithm EC -out fips-ec.key \
-pkeyopt ec_paramgen_curve:P-256
# EC P-384 (stronger, FIPS-approved)
openssl genpkey -algorithm EC -out fips-ec.key \
-pkeyopt ec_paramgen_curve:P-384
# Verify key generated in FIPS mode
openssl pkey -in fips-server.key -check
FIPS-Compliant CSR
#============================================#
# GENERATE FIPS-COMPLIANT CSR
#============================================#
# CSR with SHA-256 (FIPS-approved)
openssl req -new -key fips-server.key -out fips-server.csr \
-sha256 \
-subj "/C=US/O=Federal Agency/CN=secure.example.gov" \
-addext "subjectAltName=DNS:secure.example.gov"
# SHA-384 (stronger, FIPS-approved)
openssl req -new -key fips-server.key -out fips-server.csr \
-sha384 \
-subj "/C=US/O=Federal Agency/CN=secure.example.gov"
# ❌ NEVER use SHA-1 or MD5 in FIPS mode
# They will be rejected!
38.5 FIPS Mode Verification
Complete FIPS Verification
#============================================#
# VERIFY FIPS MODE IS ACTIVE
#============================================#
# Check 1: fips-mode-setup
fips-mode-setup --check
# FIPS mode is enabled.
# Check 2: Kernel parameter
cat /proc/cmdline | grep fips
# Should show: fips=1
# Check 3: Crypto-policy
update-crypto-policies --show
# FIPS
# Check 4: OpenSSL FIPS provider (RHEL 9+)
openssl list -providers
# Should show fips provider as active
# Check 5: Test FIPS-only operation
# Try non-FIPS algorithm (should fail)
echo "test" | openssl md5
# Error: disabled for FIPS ← Good!
# Check 6: Verify certificate operations use FIPS
openssl version -a | grep FIPS
38.6 FIPS Crypto-Policy
Understanding FIPS Policy
#============================================#
# FIPS CRYPTO-POLICY DETAILS
#============================================#
# Policy is automatically set to FIPS when FIPS mode enabled
update-crypto-policies --show
# FIPS
# What FIPS policy enforces:
cat /etc/crypto-policies/back-ends/opensslcnf.config
# Key settings:
# - TLS 1.2 minimum
# - Only FIPS-approved ciphers
# - Only FIPS-approved signature algorithms
# - Minimum 2048-bit keys
Cannot change from FIPS policy while in FIPS mode!
38.7 Services in FIPS Mode
Apache in FIPS Mode
#============================================#
# APACHE IN FIPS MODE
#============================================#
# Apache automatically uses FIPS policy
# No manual configuration needed!
# Verify
sudo systemctl restart httpd
# Test
openssl s_client -connect localhost:443
# Should show:
# - TLS 1.2 or 1.3
# - FIPS-approved cipher
# - No weak algorithms
# View actual Apache FIPS config
cat /etc/crypto-policies/back-ends/httpd.config
Other Services
All services automatically comply with FIPS policy:
- NGINX → Uses FIPS ciphers/protocols
- Postfix → FIPS-compliant TLS
- OpenSSH → FIPS algorithms only
- Databases → FIPS-approved SSL
38.8 Common FIPS Issues
Issue 1: Non-FIPS Algorithm Attempted
Symptom:
Error: disabled for FIPS
Examples:
# MD5 (not FIPS-approved)
openssl md5 file.txt
# Error: digital envelope routines:EVP_DigestInit_ex:disabled for fips
# SHA-1 signature (not FIPS-approved for signing)
openssl dgst -sha1 -sign key.pem file.txt
# Error: disabled for fips
Solution:
# Use FIPS-approved algorithms
openssl sha256 file.txt # Use SHA-256 instead of MD5
openssl dgst -sha256 -sign key.pem file.txt # Use SHA-256 for signing
Issue 2: Legacy Application Incompatible
Symptom: Application fails in FIPS mode
Cause: Application uses non-FIPS algorithms (MD5, SHA-1, weak ciphers)
Solutions:
# Solution 1: Update application to use FIPS algorithms
# Solution 2: If application can't be updated:
# May not be able to run in FIPS mode
# Consider if FIPS is actually required
# Solution 3: Container isolation (advanced)
# Run non-FIPS app in container without FIPS
38.9 Disabling FIPS Mode
When and How to Disable
#============================================#
# DISABLE FIPS MODE (if needed)
#============================================#
# Check current status
fips-mode-setup --check
# Disable FIPS
sudo fips-mode-setup --disable
# MUST REBOOT
sudo reboot
# After reboot
fips-mode-setup --check
# FIPS mode is disabled.
# Crypto-policy reverts to DEFAULT
update-crypto-policies --show
# DEFAULT
Note: Disabling FIPS may have compliance implications!
38.10 Key Takeaways
- FIPS 140-2 is current standard on RHEL (140-3 transition in progress)
- Enable at installation for cleanest FIPS state
- Post-install enable requires reboot
- Only FIPS-approved algorithms allowed
- Crypto-policy automatically set to FIPS
- Services automatically comply
- Test applications before enabling FIPS in production
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ FIPS MODE QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ Status: fips-mode-setup --check │
│ Enable: sudo fips-mode-setup --enable && reboot │
│ Disable: sudo fips-mode-setup --disable && reboot │
│ │
│ Standard: FIPS 140-2 (validated) │
│ FIPS 140-3 (transition in progress) │
│ │
│ Approved: RSA 2048+, ECC P-256/384/521 │
│ SHA-256/384/512 │
│ TLS 1.2/1.3 │
│ │
│ Blocked: MD5, SHA-1, TLS 1.0/1.1 │
│ RSA < 2048, 3DES, RC4 │
│ │
│ Policy: Automatically set to FIPS │
│ Verify: openssl list -providers | grep fips │
└──────────────────────────────────────────────────────────────┘
⚠️ FIPS 140-2 is current (140-3 transition ongoing)
⚠️ Requires reboot to enable/disable
✅ All RHEL services automatically comply
🧪 Hands-On Lab
Lab 19: FIPS Mode Configuration
Enable and configure FIPS 140-2 compliance mode
- 📁 Location:
labs/en_US/19-fips-mode/ - ⏱️ Time: 40-50 minutes
- 🎯 Level: Advanced
Chapter Navigation
| ← Previous: Chapter 37 - Migration Troubleshooting & Recovery | Next: Chapter 39 - FIPS-Compliant Certificates → |
|---|
Chapter 39: FIPS-Compliant Certificates
Compliance Ready: Learn how to generate, validate, and manage FIPS-compliant certificates on RHEL for federal and regulated environments.
39.1 FIPS Certificate Requirements
Mandatory Requirements
For FIPS 140-2/140-3 Compliance:
✅ Key Algorithm: RSA 2048+ or ECC P-256/384/521
✅ Signature: SHA-256, SHA-384, or SHA-512
✅ TLS Protocols: 1.2 or 1.3 only
✅ Generated in FIPS mode (for new keys)
✅ Validated module used for operations
❌ NO MD5, SHA-1
❌ NO RSA < 2048 bits
❌ NO TLS 1.0/1.1
❌ NO 3DES, RC4, DES
❌ NO non-approved algorithms
39.2 Generating FIPS Certificates
Complete FIPS Certificate Workflow
#============================================#
# COMPLETE FIPS CERTIFICATE GENERATION
#============================================#
# Prerequisites: FIPS mode must be enabled
fips-mode-setup --check
# FIPS mode is enabled.
# Step 1: Generate FIPS-compliant RSA key
openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/fips-server.key \
-pkeyopt rsa_keygen_bits:2048
# Or stronger (3072/4096)
openssl genpkey -algorithm RSA \
-out /etc/pki/tls/private/fips-server.key \
-pkeyopt rsa_keygen_bits:3072
# Step 2: Set permissions
sudo chmod 600 /etc/pki/tls/private/fips-server.key
# Step 3: Generate CSR with SHA-256
openssl req -new \
-key /etc/pki/tls/private/fips-server.key \
-out /tmp/fips-server.csr \
-sha256 \
-subj "/C=US/O=Federal Agency/OU=IT/CN=secure.example.gov" \
-addext "subjectAltName=DNS:secure.example.gov,DNS:www.secure.example.gov"
# Step 4: Verify CSR
openssl req -in /tmp/fips-server.csr -noout -text | grep -E "(Signature Algorithm|Public-Key)"
# Signature Algorithm: sha256WithRSAEncryption ← Must be SHA-256+
# Public-Key: (2048 bit) ← Must be 2048+
# Step 5: Submit to FIPS-compliant CA
# Get certificate back
# Step 6: Verify certificate compliance
openssl x509 -in fips-server.crt -noout -text | grep "Signature Algorithm"
# Signature Algorithm: sha256WithRSAEncryption ← Good!
FIPS-Compliant EC Keys
#============================================#
# ELLIPTIC CURVE KEYS FOR FIPS
#============================================#
# P-256 (FIPS-approved)
openssl genpkey -algorithm EC \
-out /etc/pki/tls/private/fips-ec.key \
-pkeyopt ec_paramgen_curve:P-256
# P-384 (stronger, FIPS-approved)
openssl genpkey -algorithm EC \
-out /etc/pki/tls/private/fips-ec.key \
-pkeyopt ec_paramgen_curve:P-384
# Generate CSR
openssl req -new -key /etc/pki/tls/private/fips-ec.key \
-out /tmp/fips-ec.csr \
-sha256 \
-subj "/CN=secure.example.gov"
39.3 Validating FIPS Compliance
Certificate Compliance Check
#!/bin/bash
# check-fips-compliance.sh
# Verify certificate is FIPS-compliant
CERT=$1
if [ -z "$CERT" ] || [ ! -f "$CERT" ]; then
echo "Usage: $0 /path/to/certificate.crt"
exit 1
fi
echo "=== FIPS Compliance Check ==="
echo "Certificate: $CERT"
echo ""
COMPLIANT=true
# Check signature algorithm
SIG_ALG=$(openssl x509 -in "$CERT" -noout -text | grep "Signature Algorithm" | head -2)
echo "Signature Algorithm: $SIG_ALG"
if echo "$SIG_ALG" | grep -Eqi "md5|sha1"; then
echo " ❌ FAIL: MD5/SHA-1 not FIPS-approved"
COMPLIANT=false
else
echo " ✅ PASS: FIPS-approved signature"
fi
# Check key size
KEY_SIZE=$(openssl x509 -in "$CERT" -noout -text | grep "Public-Key" | grep -oP '\d+')
echo ""
echo "Key Size: $KEY_SIZE bits"
if [ "$KEY_SIZE" -lt 2048 ]; then
echo " ❌ FAIL: Key size < 2048 bits"
COMPLIANT=false
else
echo " ✅ PASS: Key size adequate"
fi
# Check key algorithm
KEY_ALG=$(openssl x509 -in "$CERT" -noout -text | grep "Public Key Algorithm")
echo ""
echo "Key Algorithm: $KEY_ALG"
if echo "$KEY_ALG" | grep -qi "dsa"; then
echo " ❌ FAIL: DSA not FIPS-approved"
COMPLIANT=false
fi
# Final result
echo ""
echo "================================"
if [ "$COMPLIANT" = true ]; then
echo "✅ Certificate is FIPS-COMPLIANT"
exit 0
else
echo "❌ Certificate is NOT FIPS-compliant"
echo " Reissue with FIPS-approved parameters"
exit 1
fi
39.4 FIPS CA Selection
CA Must Be FIPS-Validated
Internal CA:
- Use FreeIPA in FIPS mode
- Dogtag PKI (FreeIPA’s CA) has FIPS validation
External CA:
- Verify CA is FIPS 140-2/140-3 validated
- Request FIPS compliance documentation
- Common FIPS CAs: DigiCert Federal, Entrust, IdenTrust
39.5 Service Configuration for FIPS
Services Automatically FIPS-Compliant
When FIPS mode enabled, all services automatically use FIPS crypto-policy:
# Apache - no special config needed
# Just ensure certificate is FIPS-compliant
# NGINX - automatically uses FIPS policy
# Postfix - FIPS-compliant automatically
# Verify each service
openssl s_client -connect localhost:443
# Check cipher used - should be FIPS-approved
39.6 Key Takeaways
- FIPS 140-2 is current validated standard on RHEL
- FIPS 140-3 transition is in progress
- Enable at installation for best results
- RSA 2048+ or ECC P-256/384 only
- SHA-256+ signatures required
- Services automatically comply with FIPS policy
- Test applications before enabling FIPS
Quick Reference Card
┌───────────────────────────────────────────────────────────────┐
│ FIPS-COMPLIANT CERTIFICATES QUICK REFERENCE │
├───────────────────────────────────────────────────────────────┤
│ Standard: FIPS 140-2 (current validated) │
│ FIPS 140-3 (transition in progress) │
│ │
│ Keys: RSA 2048/3072/4096 │
│ ECC P-256/384/521 │
│ │
│ Signature: SHA-256, SHA-384, SHA-512 │
│ (NO MD5, NO SHA-1) │
│ │
│ Generate: openssl genpkey -algorithm RSA ... (in FIPS mode) │
│ CSR: openssl req -new -sha256 ... │
│ Verify: Check signature alg, key size │
│ │
│ Test: echo test | openssl md5 │
│ (should fail if FIPS working) │
└───────────────────────────────────────────────────────────────┘
✅ FIPS mode must be enabled for compliance
✅ All operations use validated cryptographic modules
⚠️ Check current 140-2/140-3 status for your needs
Chapter Navigation
| ← Previous: Chapter 38 - FIPS Mode Complete Guide | Next: Chapter 40 - RHEL Security Hardening for Certificates → |
|---|
Chapter 40: RHEL Security Hardening for Certificates
Defense in Depth: Beyond FIPS, learn how to harden certificate security on RHEL using SELinux, TPM, smart cards, and security scanning tools.
40.1 Security Hardening Overview
Layers of Certificate Security:
- File Permissions - Protect private keys
- SELinux - Mandatory access control
- Firewall - Limit exposure
- Auditing - Track access
- TPM - Hardware key protection
- Smart Cards - Physical tokens
- Monitoring - Detect issues
- Compliance Scanning - Verify configuration
40.2 SELinux for Certificates
Proper SELinux Contexts
#============================================#
# SELINUX CERTIFICATE CONTEXTS
#============================================#
# Check current contexts
ls -Z /etc/pki/tls/certs/*.crt
ls -Z /etc/pki/tls/private/*.key
# Correct contexts:
# Certificates: system_u:object_r:cert_t:s0
# Private keys: system_u:object_r:cert_t:s0
# Fix contexts if wrong
sudo restorecon -Rv /etc/pki/tls/
# Verify
ls -Z /etc/pki/tls/certs/server.crt
# system_u:object_r:cert_t:s0 ← Correct
SELinux Certificate Policy
#============================================#
# SELINUX CERTIFICATE HARDENING
#============================================#
# Ensure SELinux enforcing
getenforce
# Enforcing ← Good
# If permissive, enable enforcing
sudo setenforce 1
# Make permanent
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
# Check for certificate-related denials
sudo ausearch -m avc -ts recent | grep cert
# If denials found, generate policy
sudo ausearch -m avc -ts recent | audit2allow -M mycert-policy
sudo semodule -i mycert-policy.pp
40.3 File Permissions Hardening
Strict Permission Model
#============================================#
# HARDENED FILE PERMISSIONS
#============================================#
# Certificates (public) - minimal access
sudo chmod 444 /etc/pki/tls/certs/*.crt
sudo chown root:root /etc/pki/tls/certs/*.crt
# Private keys (secret!) - owner only
sudo chmod 400 /etc/pki/tls/private/*.key
sudo chown root:root /etc/pki/tls/private/*.key
# Even stricter: Immutable (can't be modified even by root without removing flag)
sudo chattr +i /etc/pki/tls/certs/critical.crt
sudo chattr +i /etc/pki/tls/private/critical.key
# Remove immutable when need to update
# sudo chattr -i /etc/pki/tls/private/critical.key
# Verify
ls -l /etc/pki/tls/private/
# -r--------. 1 root root ← 400, very restrictive
40.4 TPM (Trusted Platform Module)
Using TPM for Key Storage
TPM Benefits:
- ✅ Hardware-protected keys
- ✅ Keys never leave TPM
- ✅ Tamper-resistant
- ✅ Platform attestation
#============================================#
# TPM FOR CERTIFICATE KEYS (ADVANCED)
#============================================#
# Check if TPM available
ls /dev/tpm*
# Install TPM tools
sudo dnf install tpm2-tools -y
# Generate key in TPM
tpm2_createprimary -C o -g sha256 -G rsa -c primary.ctx
tpm2_create -G rsa -u rsa.pub -r rsa.priv -C primary.ctx
# Use TPM key with OpenSSL requires additional setup
# (Complex, enterprise use case)
# For certmonger with TPM:
# Experimental/advanced - check Red Hat docs
40.5 Smart Cards and PIV
Using Smart Cards for Authentication
#============================================#
# SMART CARD SETUP (PIV/CAC)
#============================================#
# Install smart card support
sudo dnf install opensc pcsc-lite -y
# Start PC/SC daemon
sudo systemctl enable --now pcscd
# Check if card readable
pkcs11-tool --list-slots
# List certificates on card
pkcs11-tool --list-objects
# Use smart card with SSH
# /etc/ssh/sshd_config:
# PubkeyAuthentication yes
# Extract public key from card
ssh-keygen -D /usr/lib64/opensc-pkcs11.so > ~/.ssh/authorized_keys
40.6 Audit and Monitoring
auditd for Certificate Access
#============================================#
# AUDIT CERTIFICATE ACCESS
#============================================#
# Add audit rules for private key access
sudo auditctl -w /etc/pki/tls/private/ -p war -k certificate-access
# Make permanent
echo "-w /etc/pki/tls/private/ -p war -k certificate-access" | \
sudo tee -a /etc/audit/rules.d/certificate.rules
# Reload rules
sudo augenrules --load
# Monitor access
sudo ausearch -k certificate-access
# Real-time monitoring
sudo ausearch -k certificate-access -ts recent -i
40.7 OpenSCAP Scanning
Security Compliance Scanning
#============================================#
# OPENSCAP CERTIFICATE SCANNING
#============================================#
# Install OpenSCAP
sudo dnf install openscap-scanner scap-security-guide -y
# Scan for certificate issues
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_pci-dss \
--results scan-results.xml \
--report scan-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# View report
firefox scan-report.html
# Certificate-related checks:
# - File permissions
# - SELinux contexts
# - Weak algorithms
# - Expiration
40.8 Security Hardening Checklist
## Certificate Security Hardening Checklist
### File Security
- [ ] Private keys mode 400 or 600 (never 644!)
- [ ] Certificates mode 444 or 644
- [ ] Ownership: root:root or service user
- [ ] SELinux contexts: cert_t
- [ ] Consider immutable flag (+i) for critical certs
### Access Control
- [ ] SELinux enforcing
- [ ] Audit rules for private key access
- [ ] Firewall limiting TLS ports
- [ ] Principle of least privilege applied
### Algorithm Security
- [ ] SHA-256+ signatures only
- [ ] RSA 2048+ or ECC P-256+ keys
- [ ] TLS 1.2+ only (no 1.0/1.1)
- [ ] Strong ciphers (via crypto-policy)
- [ ] FIPS mode if required
### Operational Security
- [ ] Certificates monitored for expiration
- [ ] Automatic renewal enabled (certmonger)
- [ ] Backups encrypted
- [ ] Keys never emailed or in tickets
- [ ] Access logged and reviewed
- [ ] Regular security scans
### Network Security
- [ ] Firewall rules restrictive
- [ ] Only necessary ports open
- [ ] Certificate pinning (where applicable)
- [ ] HSTS enabled for web servers
- [ ] OCSP stapling enabled
### Compliance
- [ ] OpenSCAP scans passing
- [ ] STIG compliance verified
- [ ] CIS benchmarks met
- [ ] Documentation current
- [ ] Audit trail maintained
40.9 Key Takeaways
- Defense in depth - Multiple security layers
- SELinux enforcing - Mandatory for production
- File permissions critical - 400/600 for keys
- Audit everything - Track key access
- TPM for high security - Hardware protection
- OpenSCAP for compliance - Automated scanning
- Monitor continuously - Security is ongoing
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ CERTIFICATE SECURITY HARDENING │
├──────────────────────────────────────────────────────────────┤
│ Permissions: chmod 400 /etc/pki/tls/private/*.key │
│ chmod 444 /etc/pki/tls/certs/*.crt │
│ │
│ SELinux: getenforce (must be Enforcing) │
│ restorecon -Rv /etc/pki/tls/ │
│ ls -Z (check contexts) │
│ │
│ Audit: auditctl -w /etc/pki/tls/private/ -p war │
│ ausearch -k certificate-access │
│ │
│ Scan: oscap xccdf eval --profile pci-dss ... │
│ │
│ Immutable: chattr +i /etc/pki/tls/private/key.key │
│ chattr -i (to modify) │
└──────────────────────────────────────────────────────────────┘
✅ SELinux enforcing is mandatory
✅ Audit private key access
✅ Use 400 (not 600) for maximum security
🧪 Hands-On Lab
Lab 20: Security Hardening
Apply security best practices to certificate configurations
- 📁 Location:
labs/en_US/20-security-hardening/ - ⏱️ Time: 30-40 minutes
- 🎯 Level: Advanced
Chapter Navigation
Chapter 41: Compliance & Auditing
Meet Requirements: Learn how to meet security compliance requirements (STIG, CIS, PCI-DSS) and audit certificate configurations on RHEL.
41.1 Compliance Frameworks
Common Certificate-Related Requirements
| Framework | Focus | Certificate Requirements |
|---|---|---|
| STIG | DoD Security | FIPS, strong algorithms, auditing |
| CIS Benchmark | Industry best practices | TLS 1.2+, strong ciphers, permissions |
| PCI-DSS | Payment card industry | Strong crypto, no weak TLS/ciphers |
| HIPAA | Healthcare | Encryption, access control, auditing |
| NIST 800-53 | Federal systems | FIPS, approved algorithms, monitoring |
41.2 STIG Compliance
DISA STIG Requirements for Certificates
Key STIG Requirements:
## Certificate STIG Controls
### V-238200: SSH must use strong ciphers
- Requirement: Only FIPS-approved algorithms
- Check: /etc/ssh/sshd_config
- Fix: Use crypto-policies (RHEL 8+)
### V-238201: Web server must use strong TLS
- Requirement: TLS 1.2+ only
- Check: Apache/NGINX configuration
- Fix: Disable TLS 1.0/1.1
### V-238202: Certificates must be from DoD-approved CA
- Requirement: Use approved CA
- Check: Certificate issuer
- Fix: Obtain from approved source
### V-238203: Private keys must be protected
- Requirement: Mode 600 or stricter
- Check: ls -l /etc/pki/tls/private/
- Fix: chmod 600
### V-238204: Certificate expiration must be monitored
- Requirement: Automated monitoring
- Check: Monitoring system in place
- Fix: Implement (see Chapter 26)
STIG Compliance Scanning
#============================================#
# STIG COMPLIANCE SCAN FOR CERTIFICATES
#============================================#
# Install SCAP Security Guide
sudo dnf install scap-security-guide openscap-scanner -y
# Run STIG scan
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_stig \
--results stig-results.xml \
--report stig-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# View report
firefox stig-report.html
# Check certificate-specific findings
grep -i "cert\|tls\|ssl" stig-report.html
41.3 CIS Benchmark Compliance
CIS Controls for Certificates
CIS RHEL Benchmark Recommendations:
## CIS Certificate Controls
### 5.2.14: Ensure only strong ciphers are used
- Check: Crypto-policy DEFAULT or FUTURE
- Command: `update-crypto-policies --show`
### 5.2.15: Ensure only strong algorithms used
- Check: No MD5, SHA-1, weak keys
- Scan: Check all certificates
### 5.2.16: Ensure TLS 1.2 minimum
- Check: Crypto-policy or service config
- Test: `openssl s_client -tls1_2`
### 5.3.1: Ensure permissions on private keys
- Requirement: 600 or stricter
- Check: `ls -l /etc/pki/tls/private/`
### 5.3.2: Ensure certificate expiration monitoring
- Requirement: Automated checks
- Implementation: certmonger or monitoring script
CIS Compliance Scan
#============================================#
# CIS BENCHMARK SCAN
#============================================#
# Run CIS scan
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--results cis-results.xml \
--report cis-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# Generate remediation script
sudo oscap xccdf generate fix \
--profile xccdf_org.ssgproject.content_profile_cis \
--fix-type bash \
stig-results.xml > remediation.sh
# Review and run remediation
chmod +x remediation.sh
sudo ./remediation.sh
41.4 PCI-DSS Compliance
PCI-DSS Certificate Requirements
PCI-DSS v4.0 Requirements:
## PCI-DSS Certificate Controls
### Requirement 4.2.1: Strong cryptography
- TLS 1.2 minimum (1.3 recommended)
- Strong cipher suites only
- Check: crypto-policy DEFAULT or FUTURE
### Requirement 4.2.1.1: Insecure protocols disabled
- NO SSL, TLS 1.0, TLS 1.1
- Check: `openssl s_client -tls1`
- Should fail on compliant system
### Requirement 4.2.1.2: Strong encryption algorithms
- AES-128 minimum
- NO 3DES, DES, RC4
- Check: `openssl ciphers -v`
### Requirement 8.3.2: Certificate-based authentication
- For administrative access
- Implementation: Client certificates, smart cards
### Requirement 10: Audit certificate access
- Log all private key access
- Implementation: auditd rules
PCI-DSS Validation Script
#!/bin/bash
# pci-dss-cert-check.sh
echo "=== PCI-DSS Certificate Compliance Check ==="
# Check 1: TLS 1.2+ only
echo "1. TLS Version Check:"
if openssl s_client -connect localhost:443 -tls1 &>/dev/null; then
echo " ❌ FAIL: TLS 1.0 is enabled"
else
echo " ✅ PASS: TLS 1.0 disabled"
fi
# Check 2: Strong ciphers
echo ""
echo "2. Cipher Strength:"
WEAK=$(openssl ciphers -v | grep -Ei "3des|rc4|des-cbc" | wc -l)
if [ $WEAK -gt 0 ]; then
echo " ❌ FAIL: Weak ciphers available"
else
echo " ✅ PASS: No weak ciphers"
fi
# Check 3: Certificate expiration monitoring
echo ""
echo "3. Expiration Monitoring:"
if systemctl is-active --quiet certmonger || \
systemctl list-timers | grep -q cert-monitor; then
echo " ✅ PASS: Monitoring enabled"
else
echo " ⚠️ WARNING: No automated monitoring detected"
fi
# Check 4: Private key permissions
echo ""
echo "4. Private Key Permissions:"
BAD_PERMS=$(find /etc/pki/tls/private/ -name "*.key" -not -perm 600 2>/dev/null | wc -l)
if [ $BAD_PERMS -gt 0 ]; then
echo " ❌ FAIL: $BAD_PERMS keys with wrong permissions"
else
echo " ✅ PASS: All keys properly protected"
fi
echo ""
echo "=== Check Complete ==="
41.5 Audit Procedures
Certificate Audit Checklist
## Quarterly Certificate Audit Checklist
### Inventory Review
- [ ] All certificates documented
- [ ] Certificate inventory current
- [ ] Ownership documented
- [ ] Purpose documented
### Expiration Review
- [ ] No expired certificates
- [ ] No certificates expiring < 30 days
- [ ] Renewal process documented
- [ ] Monitoring alerts working
### Security Review
- [ ] SHA-256+ signatures only (no SHA-1 or MD5)
- [ ] RSA 2048+ or ECC P-256+ keys
- [ ] TLS 1.2+ only (no 1.0/1.1)
- [ ] Private key permissions correct (600)
- [ ] SELinux contexts correct
- [ ] No unnecessary certificates
### Configuration Review
- [ ] Service configurations reviewed
- [ ] Crypto-policy appropriate
- [ ] No weak cipher overrides
- [ ] HSTS enabled (web servers)
- [ ] Certificate pinning documented
### Access Review
- [ ] Audit logs reviewed
- [ ] Unauthorized access investigated
- [ ] Key access limited to authorized personnel
- [ ] Backup access controlled
### Compliance Review
- [ ] STIG/CIS/PCI compliance verified
- [ ] Security scans passing
- [ ] Remediation complete
- [ ] Documentation updated
41.6 Automated Compliance Reporting
Generate Compliance Report
#!/bin/bash
# generate-compliance-report.sh
REPORT_FILE="compliance-report-$(date +%Y%m%d).txt"
cat > "$REPORT_FILE" << EOF
=== Certificate Compliance Report ===
Generated: $(date)
System: $(hostname)
RHEL Version: $(cat /etc/redhat-release)
=== Configuration ===
OpenSSL Version: $(openssl version)
Crypto-Policy: $(update-crypto-policies --show 2>/dev/null || echo "N/A (RHEL 7)")
FIPS Mode: $(fips-mode-setup --check 2>/dev/null || echo "N/A")
SELinux: $(getenforce)
=== Certificate Inventory ===
EOF
# Count certificates
TOTAL=$(find /etc/pki/tls/certs/ -name "*.crt" -type f 2>/dev/null | wc -l)
echo "Total Certificates: $TOTAL" >> "$REPORT_FILE"
# Check expirations
echo "" >> "$REPORT_FILE"
echo "Expiration Status:" >> "$REPORT_FILE"
EXPIRING=0
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
if ! openssl x509 -in "$cert" -noout -checkend $((86400*30)) 2>/dev/null; then
echo " ⚠️ Expires within 30 days: $cert" >> "$REPORT_FILE"
((EXPIRING++))
fi
done
echo "Certificates expiring < 30 days: $EXPIRING" >> "$REPORT_FILE"
# Check algorithms
echo "" >> "$REPORT_FILE"
echo "Algorithm Compliance:" >> "$REPORT_FILE"
SHA1_COUNT=0
for cert in /etc/pki/tls/certs/*.crt; do
[ -f "$cert" ] || continue
if openssl x509 -in "$cert" -noout -text 2>/dev/null | grep -qi "sha1.*signature"; then
echo " ❌ SHA-1 signature: $cert" >> "$REPORT_FILE"
((SHA1_COUNT++))
fi
done
echo "SHA-1 certificates: $SHA1_COUNT (should be 0)" >> "$REPORT_FILE"
# Check permissions
echo "" >> "$REPORT_FILE"
echo "Permission Compliance:" >> "$REPORT_FILE"
BAD_PERMS=$(find /etc/pki/tls/private/ -name "*.key" -not -perm 600 2>/dev/null | wc -l)
echo "Keys with incorrect permissions: $BAD_PERMS (should be 0)" >> "$REPORT_FILE"
# certmonger status
if command -v getcert &>/dev/null; then
echo "" >> "$REPORT_FILE"
echo "certmonger Status:" >> "$REPORT_FILE"
sudo getcert list | grep "status:" | sort | uniq -c >> "$REPORT_FILE"
fi
echo "" >> "$REPORT_FILE"
echo "=== Report Complete ===" >> "$REPORT_FILE"
cat "$REPORT_FILE"
echo ""
echo "Report saved to: $REPORT_FILE"
41.7 Key Takeaways
- Compliance is ongoing - Not one-time
- Multiple frameworks exist - STIG, CIS, PCI, HIPAA
- OpenSCAP automates scanning on RHEL
- Document everything - Required for audits
- Regular audits essential - Quarterly minimum
- Remediation must be tracked - Fix and verify
- Monitoring is compliance - Continuous validation
Quick Reference Card
┌──────────────────────────────────────────────────────────────┐
│ COMPLIANCE & AUDITING QUICK REFERENCE │
├──────────────────────────────────────────────────────────────┤
│ STIG: oscap ... --profile stig │
│ CIS: oscap ... --profile cis │
│ PCI-DSS: oscap ... --profile pci-dss │
│ │
│ Common Requirements: │
│ - TLS 1.2+ only │
│ - Strong algorithms (SHA-256+, RSA 2048+) │
│ - No weak ciphers (3DES, RC4) │
│ - Private keys protected (mode 600) │
│ - Expiration monitoring │
│ - Audit logging enabled │
│ - FIPS mode (for federal) │
│ │
│ Tools: OpenSCAP, aide, auditd │
│ Scan: oscap xccdf eval --profile <profile> ... │
│ Remediate: oscap xccdf generate fix ... │
└──────────────────────────────────────────────────────────────┘
✅ Compliance is continuous, not one-time
✅ Automate scanning with OpenSCAP
✅ Document all configurations and exceptions
Chapter Navigation
Learning Path Guide
How to use this tutorial effectively based on your role and experience level.
🎯 For Complete Beginners
Goal: Learn certificates from zero
Path: Read in order (8 weeks)
Week 1: Foundations (Ch 1-7)
- Ch 1: Cryptography, PKI Structure & Fundamentals
- Ch 2: Introduction to Certificates on RHEL
- Ch 3: RHEL Certificate Tools Overview
- Ch 4: Basic Cryptography for RHEL Admins
- Ch 5: X.509 Certificates on RHEL
- Ch 6: RHEL Trust Store Deep Dive
- Ch 7: Digital Signatures & Verification
- Outcome: Understand certificates, trust chains, and RHEL tools
Week 2: Version Mastery (Ch 8-13)
- Ch 8: Differences between versions
- Ch 9: RHEL 7
- Ch 10: RHEL 8
- Ch 11: RHEL 9
- Ch 12: RHEL 10
- Ch 13: Compatibility
- Outcome: Know version differences
Week 3-4: Services (Ch 14-21)
- Configure Apache, NGINX, Postfix, LDAP, Databases, FreeIPA
- Outcome: Can configure any service
Week 5: Automation (Ch 22-26)
- certmonger, crypto-policies, Ansible, monitoring
- Outcome: Automate certificate lifecycle
Week 6: Troubleshooting (Ch 27-33)
- Master systematic troubleshooting
- Outcome: Can fix any certificate issue! ⭐
Week 7: Migration (Ch 34-37)
- RHEL upgrade procedures
- Outcome: Safely migrate RHEL versions
Week 8: Security (Ch 38-41)
- FIPS, hardening, compliance
- Outcome: Meet security requirements
🔧 For System Administrators
Goal: Configure and maintain certificates
Recommended Path:
-
Quick Start (3-5 hours)
- Ch 1: Cryptography & PKI Fundamentals
- Ch 3: Tools
- Ch 27: Troubleshooting Methodology
-
Your RHEL Version (1-2 hours)
- Ch 9 (RHEL 7), Ch 10 (RHEL 8), Ch 11 (RHEL 9), or Ch 12 (RHEL 10)
-
Your Services (3-4 hours)
- Ch 14-21: Pick chapters for services you use
-
Automation (2-3 hours)
- Ch 22: certmonger
- Ch 23: Crypto-policies (if RHEL 8+)
-
Reference
- Keep Ch 27-33 handy for troubleshooting
Total Time: ~10-15 hours to proficiency
🚨 For Support Engineers
Goal: Troubleshoot certificate issues fast
Fast Track Path:
-
Start Here (1 hour)
- Ch 27: Troubleshooting Methodology ⭐
- TROUBLESHOOTING-QUICK-START.md
-
Common Issues (2 hours)
- Ch 28: Common Errors
- Ch 29: Service-Specific
- Ch 30: certmonger Issues
- Ch 31: Crypto-Policy Issues
-
Tools (1 hour)
- Ch 3: RHEL Tools
- Ch 32: SOS Reports
-
Emergency (30 min)
- Ch 33: Emergency Procedures
-
Reference as Needed
- Ch 9-12: Version-specific chapters
- Ch 14-21: Service chapters
Total Time: 5-8 hours to troubleshooting proficiency
Then: Use chapters as reference during incidents
🏢 For Enterprise Architects
Goal: Design certificate infrastructure
Strategic Path:
-
Overview (1 hour)
- Ch 1-2: Fundamentals and introduction
-
Enterprise CA (2 hours)
- Ch 19: FreeIPA
-
Automation (3 hours)
- Ch 22: certmonger
- Ch 23: Crypto-policies
- Ch 25: Ansible
-
Best Practices (2 hours)
- Ch 21: Service Best Practices
- Ch 26: Monitoring
-
Security (3 hours)
- Ch 38-41: FIPS, hardening, compliance
-
Migration (2 hours)
- Ch 34-37: If planning upgrades
Total Time: ~13 hours for architecture knowledge
🔒 For Security/Compliance Teams
Goal: Ensure compliance and security
Compliance Path:
-
Foundation (1 hour)
- Ch 1: Cryptography & PKI Fundamentals
- Ch 2: Introduction to Certificates on RHEL
-
Security Focus (4 hours)
- Ch 38: FIPS Mode
- Ch 39: FIPS Certificates
- Ch 40: Security Hardening
- Ch 41: Compliance & Auditing ⭐
-
Crypto-Policies (1 hour)
- Ch 23: Understanding system-wide controls
-
Monitoring (1 hour)
- Ch 26: Monitoring & Alerting
-
Audit Procedures (1 hour)
- Ch 32: SOS Reports
- Ch 41: Auditing procedures
Total Time: ~8-10 hours for compliance expertise
🎓 For Training/Certification Prep
Goal: Complete mastery
Complete Path: All chapters in order
Time Investment: 40-50 hours
Outcome: Expert-level knowledge of RHEL certificate management
📚 Jump-In Points
By Problem Type:
“Service won’t start” → Ch 28 (Common Errors), Ch 29 (Service-Specific)
“Clients can’t connect” → Ch 13 (Compatibility), Ch 31 (Crypto-Policy)
“certmonger not renewing” → Ch 30 (certmonger Troubleshooting)
“Planning RHEL upgrade” → Ch 34-37 (Migration)
“Need FIPS compliance” → Ch 38-39 (FIPS)
“Setting up new service” → Ch 14-21 (Service chapters)
By RHEL Version:
Using RHEL 7 → Ch 9 (RHEL 7 Management)
Using RHEL 8 → Ch 10 (Crypto-Policies are key!)
Using RHEL 9 → Ch 11 (OpenSSL 3.x, SHA-1 blocked)
Using RHEL 10 → Ch 12 (Latest features)
Mixed environment → Ch 13 (Cross-Version Compatibility)
🗺️ Tutorial Map
START HERE
│
├─ New to certificates?
│ └─ Ch 1 → Ch 2 → Ch 3 → Continue in order
│
├─ Need to troubleshoot NOW?
│ └─ Ch 27 → Ch 28 → Ch 29 → Ch 33
│
├─ Configuring a service?
│ └─ Ch 14-21 (pick your service)
│
├─ Planning migration?
│ └─ Ch 34 → Ch 35/36 → Ch 37
│
├─ Need automation?
│ └─ Ch 22 (certmonger) → Ch 23 (crypto-policies)
│
└─ Compliance required?
└─ Ch 38-41 (FIPS, security, auditing)
⏱️ Time Estimates
| Path | Time | Chapters |
|---|---|---|
| Quick Start | 3-5 hours | 1, 3, 27 |
| Troubleshooting | 5-8 hours | 27-33 |
| Complete Beginner | 40-50 hours | All in order |
| Sysadmin | 10-15 hours | Selected chapters |
| Support Engineer | 5-8 hours | Troubleshooting focus |
| Compliance | 8-10 hours | Security chapters |
💡 Study Tips
- Hands-on is essential - Practice on RHEL VM
- Follow examples - Copy-paste and understand
- Use quick references - At end of each chapter
- Bookmark troubleshooting - Chapters 27-33
- Know your RHEL version - Focus on relevant chapters
- Build a lab - Use FreeIPA for practice
Start Learning: Chapter 1: Cryptography, PKI Structure & Fundamentals →
Need Help Fast: Troubleshooting Quick-Start →
Version Reference: RHEL Version Cheat Sheet →
Troubleshooting Quick-Start Guide
When you have a certificate problem, start here!
🚨 Emergency? Jump to Chapter 33!
If production is down, go immediately to Chapter 33: Emergency Procedures
📋 The 7-Step Method (Chapter 27)
1. Identify: RHEL version, OpenSSL, crypto-policy
2. Verify: Expiry, hostname, key match, algorithm
3. Trust: CA validation, chain, intermediates
4. Config: Service files, paths, permissions
5. System: Crypto-policy, FIPS, SELinux, firewall
6. Test: Live connections, curl, openssl s_client
7. Logs: Service logs, journal, SELinux audit
Full methodology: Chapter 27
⚡ Quick Diagnostics
First 60 Seconds
# What RHEL version?
cat /etc/redhat-release
# Certificate expired?
openssl x509 -in /etc/pki/tls/certs/server.crt -noout -checkend 0
# Service running?
systemctl status httpd
# Recent errors?
journalctl -xe | grep -i cert | tail -20
# Crypto-policy? (RHEL 8+)
update-crypto-policies --show
🔍 Common Problems
Certificate Expired
# Check
openssl x509 -in cert.crt -noout -dates
# Fix
sudo getcert resubmit -f cert.crt # If using certmonger tracking
# Or renew manually, or use Chapter 33 emergency procedures
Trust Chain Broken
# Check
openssl verify cert.crt
# Fix
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
Permission Denied
# Check
ls -l /etc/pki/tls/private/server.key
# Fix
sudo chmod 600 /etc/pki/tls/private/server.key
sudo chown root:root /etc/pki/tls/private/server.key
Hostname Mismatch
# Check
openssl x509 -in cert.crt -noout -ext subjectAltName
# Fix
# Reissue certificate with correct SANs
No Shared Cipher (RHEL 8+)
# Check
update-crypto-policies --show
# Temporary fix
sudo update-crypto-policies --set LEGACY
sudo systemctl restart httpd
# Proper fix: Update client to support TLS 1.2+
SHA-1 Rejected (RHEL 9+)
# Check
openssl x509 -in cert.crt -noout -text | grep "Signature Algorithm"
# Fix
# Must reissue with SHA-256+ (no workaround)
📖 Where to Look
| Problem Type | Go To Chapter |
|---|---|
| General troubleshooting | Chapter 27 |
| Common errors | Chapter 28 |
| Apache/NGINX/Postfix issues | Chapter 29 |
| certmonger problems | Chapter 30 |
| crypto-policy issues | Chapter 31 |
| SOS report analysis | Chapter 32 |
| Production emergency | Chapter 33 |
| RHEL 7 specific | Chapter 9 |
| RHEL 8 specific | Chapter 10 |
| RHEL 9 specific | Chapter 11 |
| RHEL 10 specific | Chapter 12 |
| After migration | Chapters 35-36 |
⚙️ Service-Specific Commands
# Apache
apachectl configtest
tail -f /var/log/httpd/ssl_error_log
# NGINX
nginx -t
tail -f /var/log/nginx/error.log
# Postfix
postfix check
tail -f /var/log/maillog | grep TLS
# OpenLDAP
slapcat -b "cn=config" | grep TLS
# Note: Keys must be owned by ldap:ldap!
# PostgreSQL
sudo -u postgres psql -c "SHOW ssl;"
# Note: Keys must be owned by postgres:postgres!
# certmonger
getcert list
journalctl -u certmonger -f
🎯 Quick Reference
Most Common Issues:
- Certificate expired → Renew
- Missing CA → Add to trust store
- Wrong permissions → chmod 600
- Cert/key mismatch → Regenerate CSR
- Hostname mismatch → Reissue with SANs
- TLS version → Check crypto-policy
- SELinux denying → restorecon
- certmonger CA_UNREACHABLE → Check IPA/Kerberos
Emergency: Chapter 33
Methodology: Chapter 27
RHEL Version Cheat Sheet for Certificates
Quick reference for certificate differences across RHEL versions.
Version Overview
| RHEL | Released | OpenSSL | TLS Support | Crypto-Policies | Key Feature |
|---|---|---|---|---|---|
| 7 | 2014 | 1.0.2k-26 | 1.0/1.1/1.2 | ❌ No | Manual config |
| 8 | 2019 | 1.1.1k-14 | 1.2/1.3 | ✅ NEW! | System-wide policies |
| 9 | May 2022 | 3.5.5-2 | 1.2/1.3 | ✅ Enhanced | OpenSSL 3.x, strict |
| 10 | May 2025 | 3.5.5-2 | 1.3 pref | ✅ Enhanced | PQC prep, modern |
Quick Detection
# Check RHEL version
cat /etc/redhat-release
# Check OpenSSL (indirect version check)
openssl version
# 1.0.2k = RHEL 7
# 1.1.1k = RHEL 8
# 3.5.5 = RHEL 9 or 10
# Check crypto-policies (RHEL 8+ only)
update-crypto-policies --show 2>/dev/null || echo "RHEL 7 (no crypto-policies)"
TLS Configuration by Version
RHEL 7
# Manual configuration required everywhere
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
RHEL 8/9/10
# crypto-policies handle it automatically!
# No SSLProtocol or SSLCipherSuite needed
# Just include certificate paths
Common Commands by Version
| Task | RHEL 7 | RHEL 8/9/10 |
|---|---|---|
| Generate Key | openssl genrsa -out key 2048 | openssl genpkey -algorithm RSA -out key |
| Check Policy | N/A | update-crypto-policies --show |
| TLS Config | Manual per service | Automatic via crypto-policies |
| certmonger | Basic | Enhanced (IPA/tracking workflows) |
Troubleshooting by Version
RHEL 7
- Check for TLS 1.0/1.1 issues
- Manual cipher configurations
- No crypto-policies
RHEL 8
- Check crypto-policy first!
- TLS 1.0/1.1 disabled in DEFAULT
- LEGACY policy for compatibility
RHEL 9
- OpenSSL 3.x provider issues
- SHA-1 BLOCKED
- Use
-provider legacyfor old algorithms
RHEL 10
- Same as RHEL 9
- Even stricter defaults
- Check minor version docs
Migration Impact
| Migration | Certificate Impact | Key Changes |
|---|---|---|
| 7→8 | Moderate-High | crypto-policies, TLS 1.0/1.1 blocked |
| 8→9 | High | OpenSSL 3.x, SHA-1 blocked, stricter |
| 9→10 | Low | Same OpenSSL, incremental hardening |
Quick Fixes by Version
“no shared cipher” Error
- RHEL 7: Update cipher config manually
- RHEL 8/9/10:
sudo update-crypto-policies --set LEGACY(temp!)
SHA-1 Certificate
- RHEL 7/8: Works (deprecated)
- RHEL 9/10: BLOCKED - must reissue
TLS 1.0 Client
- RHEL 7: Works by default
- RHEL 8/9/10: Blocked in DEFAULT, use LEGACY (temp!)
Full Details: See Chapters 9-12
Appendix A: Kubernetes cert-manager
cert-manager is a CNCF project that automates certificate issuance inside Kubernetes clusters.
1. Architecture
- Issuer / ClusterIssuer – Defines CA or ACME server.
- Certificate – Desired state for a cert (DNS names, duration).
- Controller – Reconciles resources, stores secrets.
graph LR
subgraph K8s
A[Certificate] --> B[Secret TLS]
A --> C(Issuer)
end
C -->|ACME| LE[Let's Encrypt]
2. Installation
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.1/cert-manager.yaml
3. Example: Ingress TLS
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: le-key
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: web-tls
namespace: default
spec:
secretName: web-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: example.com
dnsNames:
- example.com
- www.example.com
4. Renewal & Status
kubectl describe certificate web-tls shows condition Ready and next renewal time.
🧪 Hands-On Lab
Lab 21: Kubernetes cert-manager
Automate certificate management in Kubernetes
- 📁 Location:
labs/en_US/21-kubernetes-cert-manager/ - ⏱️ Time: 50-60 minutes
- 🎯 Level: Advanced
Appendix B: HashiCorp Vault PKI
Vault provides a dynamic PKI where certificates are issued on-demand with short TTLs.
1. Why Vault?
- Centralised policy enforcement
- Dynamic, short-lived certs reduce revocation needs
- API-driven (REST + CLI)
2. Enabling PKI Engine
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
vault write pki/root/generate/internal common_name="corp.example" ttl=87600h
vault write pki/config/urls issuing_certificates="https://vault.corp/v1/pki/ca" \
crl_distribution_points="https://vault.corp/v1/pki/crl"
3. Roles & Issue
vault write pki/roles/web allowed_domains="web.corp" allow_subdomains=true max_ttl=72h
vault write pki/issue/web common_name=app01.web.corp ttl=24h
4. Agent Sidecar Auto-Renew
# vault-agent.hcl
pid_file = "/var/run/agent.pid"
auto_auth {
method "kubernetes" {
mount_path = "auth/k8s"
role = "web"
}
sink "file" {
config = {
path = "/etc/tls/web.pem"
}
}
}
🧪 Hands-On Lab
Lab 22: HashiCorp Vault PKI
Dynamic certificate issuance with Vault
- 📁 Location:
labs/en_US/22-vault-pki/ - ⏱️ Time: 45-55 minutes
- 🎯 Level: Advanced
Appendix C: Zero Trust Architecture
PKI in Zero Trust Architecture
Zero Trust (ZT) assumes no implicit trust based on network location. Every request must be authenticated and authorised.
1. Google BeyondCorp & NIST SP 800-207
These frameworks recommend strong identity, transport encryption, and continuous evaluation.
2. PKI’s Role
- Device identity via certificates.
- mTLS for east-west traffic.
- Short-lived credentials auto-rotated.
3. Certificate Profile for ZT
| Extension | Purpose |
|---|---|
| SAN: URI:spiffe:// | Workload ID |
| Key Usage: digitalSignature | AuthN |
| EKU: clientAuth, serverAuth | Mutual TLS |
| Validity ≤ 24h | Limit blast radius |
4. Policy Enforcement Points
- Gateways terminate TLS and verify client certs.
- Service Mesh sidecars perform mTLS transparently.
- Endpoint Agents maintain device certificates.
5. Lab: Issue SPIFFE Certificates with cert-manager
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: spiffe-workload
spec:
duration: 24h
commonName: spiffe://prod/web/api
uriSANs:
- spiffe://prod/web/api
issuerRef:
name: mesh-issuer
kind: ClusterIssuer
Appendix D: DevSecOps Integration
PKI in DevSecOps & CI/CD
Integrating certificate management into CI/CD pipelines ensures every build artifact and environment uses trusted identities.
1. Signing Build Artifacts
- Containers – cosign, Notary v2.
- Packages – RPM/DEB GPG signatures.
- Binaries – Windows Authenticode.
2. Automated TLS for Preview Environments
Pipelines trigger cert-manager to issue ephemeral certs for short-lived branches.
3. Example: GitHub Actions with cosign
name: Build & Sign
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t ghcr.io/org/app:${{ github.sha }} .
- name: Login registry
run: echo ${{ secrets.GH_TOKEN }} | docker login ghcr.io -u user --password-stdin
- name: Push image
run: docker push ghcr.io/org/app:${{ github.sha }}
- name: Sign image
env:
COSIGN_EXPERIMENTAL: "1"
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
run: cosign sign -key env://COSIGN_KEY ghcr.io/org/app:${{ github.sha }}
4. Policy Gates
Supply-chain security scanners verify signatures and enforce policy before deploy.
5. Secrets Management
Store private keys for signing in HashiCorp Vault or AWS KMS, not in repo secrets.
Appendix E: PKI Policy Theory
Policies, Baselines & Audits
1. Why Policies Matter
Policies formalise how certificates may be issued and used, ensuring consistency and legal defensibility.
2. Baseline Requirements (CAB Forum)
Requirements that publicly-trusted CAs must follow, including:
- Domain validation methods
- Key sizes ≥ 2048-bit RSA / 256-bit ECC
- Maximum validity 398 days
3. Certificate Policy (CP) vs CPS
| Document | Audience | Content |
|---|---|---|
| CP | Relying parties | What assurance the PKI provides |
| CPS | Auditors, operators | How the CA meets the CP |
4. Audits & Compliance
- WebTrust / ETSI audits for public CAs.
- Internal PKIs may align with NIST SP 800-53 or ISO 27001.
5. RHEL Case Study
RHEL’s certmonger can automatically renew host certificates in accordance with enterprise CP/CPS guidelines.
Appendix F: IoT Certificates
IoT Device Certificates
Internet of Things devices require unique identities for secure communication. Certificates provide cryptographic proof of device authenticity.
1. Why Certificates for IoT?
- Device Identity – Each sensor, gateway, or actuator gets a unique cert.
- Zero-Trust Networks – Devices authenticate mutually with cloud/edge.
- Supply Chain Security – Certificates provisioned at manufacturing prevent cloning.
- OTA Updates – Code-signing ensures firmware integrity.
2. Certificate Provisioning Models
Factory Provisioning
Certificates burned into device secure storage (TPM, secure element) before shipment.
sequenceDiagram
Factory->>HSM: Generate key pair
HSM->>Factory: Return CSR
Factory->>CA: Sign CSR
CA-->>Factory: Certificate
Factory->>Device: Inject cert + private key
Just-in-Time Provisioning
Device generates key on first boot, submits CSR to registration service.
# Device generates key
openssl ecparam -genkey -name prime256v1 -out device.key
# Create CSR with device serial
openssl req -new -key device.key -out device.csr -subj "/CN=device-12345/serialNumber=12345"
# Submit to enrollment API
curl -X POST https://enroll.iot.example.com/csr \
-H "Authorization: Bearer $ENROLL_TOKEN" \
--data-binary @device.csr -o device.crt
3. AWS IoT Core
Register Device Certificate
# Generate key and CSR
openssl ecparam -genkey -name prime256v1 -out iot-device.key
openssl req -new -key iot-device.key -out iot-device.csr -subj "/CN=iot-device-001"
# Use AWS CLI to sign
aws iot create-certificate-from-csr --certificate-signing-request file://iot-device.csr \
--set-as-active > cert-response.json
# Extract certificate
jq -r '.certificatePem' cert-response.json > iot-device.crt
Attach Policy
aws iot attach-policy --policy-name IoTDevicePolicy --target arn:aws:iot:region:account:cert/certId
MQTT Connect
import paho.mqtt.client as mqtt
import ssl
client = mqtt.Client()
client.tls_set(
ca_certs="AmazonRootCA1.pem",
certfile="iot-device.crt",
keyfile="iot-device.key",
tls_version=ssl.PROTOCOL_TLSv1_2
)
client.connect("xxxxxx.iot.us-east-1.amazonaws.com", 8883)
client.publish("device/telemetry", "{'temp': 22.5}")
4. Azure IoT Hub
X.509 Device Authentication
# Generate device cert signed by custom CA
openssl req -new -key device.key -out device.csr -subj "/CN=device-001"
openssl x509 -req -in device.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out device.crt -days 365 -sha256
# Upload CA to Azure IoT Hub (portal or CLI)
az iot hub certificate create --hub-name MyIoTHub --name MyCACert --path ca.crt
# Connect device
from azure.iot.device import IoTHubDeviceClient
device_client = IoTHubDeviceClient.create_from_x509_certificate(
hostname="MyIoTHub.azure-devices.net",
device_id="device-001",
x509=X509(cert_file="device.crt", key_file="device.key")
)
device_client.connect()
device_client.send_message("Hello from device-001")
5. Microchip ATECC608 Secure Element
Hardware crypto chip stores private keys that never leave the silicon.
Provisioning Flow
- Device generates key pair inside ATECC608.
- CSR created using on-chip key.
- CA signs CSR, certificate stored in device EEPROM.
// Arduino example with ATECC library
#include <ArduinoECCX08.h>
void setup() {
ECCX08.begin();
// Generate CSR
byte csr[256];
ECCX08.getCSR(csr);
// Send CSR to CA via HTTP/MQTT
// Receive signed certificate
// Store in EEPROM
}
6. Certificate Rotation for Constrained Devices
Short-Lived Certificates
Issue 7-day certs with automated renewal via lightweight protocols like EST (RFC 7030).
# EST simple enroll
curl --cacert ca.crt --cert current-device.crt --key device.key \
https://est.example.com/.well-known/est/simpleenroll \
--data-binary @new-device.csr -o renewed-device.crt
Bootstrap Certificates
Initial long-lived cert used solely for enrollment, then replaced by short-lived operational certs.
7. LoRaWAN & Secure Join
LoRaWAN 1.1+ supports secure join with device-specific keys derived from certificates:
Device → JoinRequest (signed with DevEUI cert)
Network Server → JoinAccept (encrypted session keys)
8. Best Practices Summary
| Practice | Rationale |
|---|---|
| Use ECC (P-256, P-384) | Smaller keys, lower power consumption |
| Hardware Root of Trust | TPM, ATECC, or TrustZone prevent key extraction |
| Certificate Validity ≤ 1 year | Limit blast radius of compromise |
| Revocation via OCSP/CRL | Disable compromised devices remotely |
| Separate PKI for IoT | Isolate device CA from enterprise IT |
9. Real-World Deployments
- Automotive – Vehicle ECUs use certificates for V2X communication (IEEE 1609.2).
- Industrial IoT – PLCs and SCADA devices authenticated via IEC 62351.
- Smart Home – Matter protocol mandates X.509 certificates for device commissioning.
Security Tip: Never embed the same private key in multiple devices. Each device must have a unique certificate to enable selective revocation.
Appendix G: VPN Certificates
VPN Certificates — OpenVPN, WireGuard & IPsec
Virtual Private Networks use certificates to authenticate endpoints and establish encrypted tunnels.
1. OpenVPN with PKI
Setup Easy-RSA
git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa gen-req server nopass
./easyrsa sign-req server server
./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1
./easyrsa gen-dh
openvpn --genkey secret ta.key
Server Configuration
/etc/openvpn/server.conf:
port 1194
proto udp
dev tun
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/server.crt
key /etc/openvpn/pki/private/server.key
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/ta.key 0
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
cipher AES-256-GCM
auth SHA256
user nobody
group nobody
persist-key
persist-tun
status /var/log/openvpn-status.log
verb 3
Start:
sudo systemctl enable --now openvpn-server@server
Client Configuration
client1.ovpn:
client
dev tun
proto udp
remote vpn.example.com 1194
ca ca.crt
cert client1.crt
key client1.key
tls-auth ta.key 1
cipher AES-256-GCM
auth SHA256
verb 3
Connect:
sudo openvpn --config client1.ovpn
2. WireGuard (Certificate-less but Key-based)
WireGuard uses Curve25519 public keys instead of X.509 certificates. However, you can wrap WireGuard keys in certificates for enterprise identity management.
Generate Keys
wg genkey | tee privatekey | wg pubkey > publickey
Server Config
/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <server-private-key>
Address = 10.9.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <client-public-key>
AllowedIPs = 10.9.0.2/32
Enable:
sudo systemctl enable --now wg-quick@wg0
Client Config
[Interface]
PrivateKey = <client-private-key>
Address = 10.9.0.2/24
DNS = 8.8.8.8
[Peer]
PublicKey = <server-public-key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Connect:
sudo wg-quick up wg0
3. IPsec (strongSwan) with X.509
Install strongSwan
sudo dnf install strongswan -y
Generate Certificates
# CA
ipsec pki --gen --type rsa --size 4096 --outform pem > ca.key.pem
ipsec pki --self --ca --lifetime 3650 --in ca.key.pem --type rsa \
--dn "CN=VPN CA" --outform pem > ca.crt.pem
# Server cert
ipsec pki --gen --type rsa --size 2048 --outform pem > server.key.pem
ipsec pki --req --type priv --in server.key.pem --dn "CN=vpn.example.com" \
--san vpn.example.com --outform pem > server.req.pem
ipsec pki --issue --cacert ca.crt.pem --cakey ca.key.pem --type pkcs10 \
--in server.req.pem --lifetime 365 --outform pem > server.crt.pem
# Client cert
ipsec pki --gen --type rsa --size 2048 --outform pem > client.key.pem
ipsec pki --req --type priv --in client.key.pem --dn "CN=client@example.com" \
--outform pem > client.req.pem
ipsec pki --issue --cacert ca.crt.pem --cakey ca.key.pem --type pkcs10 \
--in client.req.pem --lifetime 365 --outform pem > client.crt.pem
Copy to /etc/ipsec.d/:
sudo cp ca.crt.pem /etc/ipsec.d/cacerts/
sudo cp server.crt.pem /etc/ipsec.d/certs/
sudo cp server.key.pem /etc/ipsec.d/private/
Server Configuration
/etc/ipsec.conf:
config setup
charondebug="ike 2, knl 2, cfg 2"
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
authby=pubkey
conn rw
leftcert=server.crt.pem
leftid=@vpn.example.com
leftsubnet=0.0.0.0/0
leftfirewall=yes
right=%any
rightid=%any
rightsourceip=10.10.10.0/24
auto=add
Start:
sudo systemctl enable --now strongswan
Client Configuration
Install client cert/key, then connect via NetworkManager or command-line.
4. Certificate Revocation in VPNs
OpenVPN CRL
./easyrsa revoke client1
./easyrsa gen-crl
Update server.conf:
crl-verify /etc/openvpn/pki/crl.pem
strongSwan OCSP
In /etc/ipsec.conf:
conn rw
leftcert=server.crt.pem
leftsendcert=always
rightca="CN=VPN CA"
rightcert=*
ocsp_uri=http://ocsp.example.com
5. Comparison
| VPN | Certificate Support | Key Management | Use Case |
|---|---|---|---|
| OpenVPN | Full X.509 PKI | Easy-RSA, manual | Enterprise site-to-site & remote access |
| WireGuard | Public keys (no X.509 by default) | Simple key pairs | Modern, high-performance, IoT |
| IPsec (strongSwan) | Full X.509 PKI | ipsec pki tools | Standards-compliant, interop with Cisco/Juniper |
6. Best Practices
- Separate CA for VPN – Isolate VPN certs from web TLS certs.
- Short Validity – Issue VPN client certs with 30-90 day expiry.
- CRL/OCSP – Enable revocation checking on the server.
- Hardware Tokens – Store client keys in YubiKeys or smartcards for remote workers.
Real-world: Many organisations migrate from OpenVPN to WireGuard for performance but wrap WireGuard keys in X.509 for integration with existing IAM systems.
Appendix H: Secure Boot and Certificate Usage
Secure Boot, Trust Chains, and Certificate Operations on RHEL
Secure Boot is where firmware, boot loaders, kernels, and kernel-loaded code stop being “just files on disk” and become authenticated code objects. If you understand TLS certificates but not Secure Boot, you are missing a large part of how trust actually starts on a modern system.
This appendix focuses on three things:
- What Secure Boot is actually doing.
- Where certificates and keys are used in the boot chain.
- How RHEL uses
shim,GRUB,mokutil,pesign, kernel keyrings, and module signing in real deployments.
1. What Secure Boot Is
UEFI Secure Boot is a firmware-backed signature verification model for the boot path. The firmware checks whether an EFI executable is signed by a trusted key or certificate before it is allowed to run.
That means Secure Boot is not the same thing as:
- Full-disk encryption
- TPM-sealed secrets
- Measured Boot
- File Integrity Monitoring
- User-space application whitelisting
- TLS trust for web services
Secure Boot is specifically about authorizing code during the boot path and for kernel-space extensions such as loadable kernel modules.
In plain terms, the question Secure Boot asks is:
“Should this firmware component, EFI binary, boot loader, kernel, or module be trusted to execute?”
2. Why Certificates Matter in Secure Boot
Certificates are the transport mechanism for trust. They bind a public key to an identity or policy context so a verifier can decide whether a signature should be accepted.
In the Secure Boot ecosystem, certificates and keys are used in different places:
| Location | Purpose |
|---|---|
| UEFI firmware variables | Store platform trust anchors and revocations |
shim | Carries embedded vendor trust for next-stage verification |
| Kernel keyrings | Hold trusted keys used to authenticate modules and related artifacts |
| MOK list | Adds owner-controlled trust without rewriting firmware databases |
| Signature blocks on EFI binaries | Prove that boot components were signed by a trusted private key |
This is a different trust domain from the RHEL TLS trust store in /etc/pki/ca-trust/. That trust store is for user-space PKI operations such as HTTPS, LDAPS, SMTP TLS, package retrieval, and application validation. It does not decide what EFI binary the firmware boots.
3. The UEFI Secure Boot Trust Model
3.1 Core Firmware Databases
UEFI Secure Boot commonly revolves around four important variable-backed databases:
| Database | Meaning | Typical Role |
|---|---|---|
PK | Platform Key | Top-level owner of platform Secure Boot policy |
KEK | Key Exchange Key database | Authorizes updates to allowed and revoked signature databases |
db | Allowed signatures / certificates | Trust list for EFI executables and drivers |
dbx | Forbidden signatures / certificates / hashes | Revocation list used to block known-bad binaries or certs |
These are conceptually simple, but administrators routinely confuse them:
PKcontrols the platform’s Secure Boot authority.KEKcontrols updates to the allowlist and denylist.dbsays what is allowed to boot.dbxsays what must never be accepted, even if it was once trusted.
3.2 Setup Mode vs User Mode
UEFI firmware usually has different operational states:
- Setup Mode: platform ownership is not finalized; key enrollment changes are possible.
- User Mode: Secure Boot policy is actively enforced using installed keys.
- Custom Mode: vendor-specific mode that may allow manual key management.
This matters because people often mistake “Secure Boot enabled in firmware menus” for “Secure Boot fully enforced with the expected trust policy.” Those are not always the same state.
4. What Actually Gets Signed
Secure Boot is not one signature over “the system.” It is a chain of separately signed or separately trusted objects.
Common authenticated objects include:
- EFI applications
- EFI boot loaders
- GRUB EFI binaries
- Linux kernels
- Loadable kernel modules
- Sometimes vendor firmware update executables
Just because a component participates in boot does not mean it is independently signed in the same way. For example:
- EFI binaries are typically authenticated as PE/COFF executables with embedded signatures.
- Kernel modules are signed in a Linux-specific way and verified by the kernel against trusted X.509 keys.
- The initramfs is part of the boot flow, but in the classic RHEL Secure Boot model it is not simply “another independently PE-signed EFI executable.”
That distinction matters. Sloppy docs blur everything into “the whole boot chain is signed.” That is not precise enough to troubleshoot or design policy.
5. RHEL Secure Boot Chain of Trust
5.1 High-Level Flow
On a typical RHEL system with UEFI Secure Boot enabled:
- Firmware validates the first-stage EFI boot loader against trusted keys in firmware databases.
- The first-stage loader is typically
shim. shimcarries an embedded Red Hat trust anchor used to authenticate the next stage.shimvalidates the RHEL GRUB EFI binary.- GRUB validates the kernel it loads using the public key embedded in
shim(GRUB does not carry its own trust keys). - The kernel uses its trusted keyrings to validate loadable kernel modules and related kernel-space code.
That gives you a chain of authorization from firmware to kernel-space extensibility.
5.2 Why shim Exists
shim exists because hardware vendors broadly ship systems with Microsoft-trusted UEFI signing roots already enrolled. Red Hat can therefore have the first-stage loader signed so it is accepted by commodity hardware without asking every hardware vendor to pre-install a Red Hat-only firmware trust anchor.
After firmware accepts shim, shim becomes the bridge between firmware trust and vendor OS trust.
In practice on RHEL:
- Firmware trusts the Microsoft-recognized signature path for
shim. shimcontains an embedded Red Hat Secure Boot CA certificate.- That embedded Red Hat certificate is used to validate GRUB and the kernel.
5.3 Why RHEL Does Not Rely on Arbitrary GRUB Module Loading
Under Secure Boot, RHEL does not want arbitrary unsigned code loaded inside the boot loader security boundary. Red Hat documentation explains that GRUB module loading is disabled in Secure Boot contexts because there is no broad-purpose signing and verification model for arbitrary GRUB modules equivalent to the controlled signed path Red Hat ships.
The operational point is simple:
- If you are on a Secure Boot system, do not assume “GRUB can just load anything from disk.”
- The whole design is trying to keep unsigned code out of the boot path.
6. Certificates and Keys Used by RHEL Secure Boot
6.1 Firmware Certificates
Firmware trust comes from keys and certificates stored in UEFI variables (PK, KEK, db, dbx) as described in section 3.1. These are not managed with update-ca-trust.
6.2 Embedded shim Certificate
Red Hat documentation for RHEL 8/9/10 describes shim as containing a Red Hat public certificate used to authenticate GRUB and the kernel. This embedded trust is one of the reasons shim is central to the RHEL Secure Boot design.
6.3 Machine Owner Key (MOK)
The Machine Owner Key facility is the practical escape hatch that keeps Secure Boot usable in the real world.
Without MOK, you would be trapped between two bad options:
- Disable Secure Boot whenever you need custom code.
- Convince your hardware vendor to permanently add your public certificate to firmware databases.
MOK provides a third option:
- You enroll your own public certificate on the system.
shimandMokManagermanage that enrollment.- On boot, the key is propagated into a kernel-trusted keyring so your signed custom components can be accepted.
6.4 Kernel Keyrings
On modern RHEL, kernel module signature verification relies on kernel keyrings, primarily:
| Keyring | Role |
|---|---|
.builtin_trusted_keys | Built-in trusted keys embedded into the kernel or loaded as part of the trusted boot path |
.platform | Platform-derived trust, including keys sourced from Secure Boot databases and MOK |
.blacklist | Revoked keys and hashes that must be rejected |
RHEL documentation for module signing explicitly notes:
- module signatures are checked against trusted X.509 keys from
.builtin_trusted_keysand.platform - revoked entries from the blacklist are excluded from verification
- MOK keys propagate to
.platformon Secure Boot-enabled boots
That means trust is additive, but revocation still wins.
7. What Secure Boot Does and Does Not Protect
7.1 What It Protects
Secure Boot is designed to reduce the chance that the system executes unauthorized boot-path code such as:
- tampered EFI binaries
- rogue boot loaders
- modified kernels
- unsigned or untrusted kernel modules
7.2 What It Does Not Automatically Solve
Secure Boot does not automatically protect:
- user-space binaries
- shell scripts
- configuration files
- secrets at rest
- runtime memory tampering after a system is already compromised
- TLS server identity for services
- local privilege escalation through signed-but-vulnerable code
Secure Boot is a boot authorization control. It is not a complete host integrity framework.
7.3 Secure Boot vs Measured Boot vs TPM
People mash these together because they all touch startup trust. That is lazy thinking.
They are related, but different:
| Feature | Main Function |
|---|---|
| Secure Boot | Blocks execution of unauthorized code in the boot path |
| Measured Boot | Records boot measurements into TPM PCRs |
| TPM | Stores protected secrets and measurements; can seal data to boot state |
| IMA appraisal | Extends integrity policy beyond early boot into file appraisal and runtime loading decisions |
A practical RHEL implication:
- Secure Boot decides whether code is accepted to boot or load.
- TPM-based workflows may depend on PCR values that reflect Secure Boot policy and firmware database state.
If firmware keys or revocation databases change, TPM measurements can change as well. That matters for LUKS auto-unlock, attestation, and sealed-secret workflows.
8. Kernel Lockdown on RHEL
On RHEL, booting in EFI Secure Boot mode activates kernel lockdown behavior. This is important because Secure Boot alone is not enough if the running kernel still exposes interfaces that let privileged users tamper with kernel memory or bypass trust decisions.
Lockdown is intended to close that gap.
Typical restrictions include limits or outright blocks on things like:
- loading unsigned modules
- direct kernel image modification paths
- direct access interfaces such as
/dev/mem - some unsigned
kexecpaths - interfaces that could weaken the boot trust boundary
This is why admins sometimes see messages like:
Lockdown: X: Y is restricted; see man kernel_lockdown.7
That is not random kernel drama. It is Secure Boot policy following through into runtime enforcement.
9. The RHEL Administrator’s Mental Model
If you only keep one model in your head, keep this one:
- Firmware trusts keys in
dband rejects keys or hashes indbx. - Firmware authorizes
shim. shimauthorizes Red Hat’s next-stage boot components and also supports MOK operations.- MOK lets you add owner-controlled public certificates.
- The kernel trusts built-in and platform/MOK-derived keys for module verification.
- Lockdown prevents obvious runtime bypasses.
If any one of those steps is broken, your custom boot or module workflow fails.
10. Secure Boot on RHEL: Daily Operational Checks
10.1 Check Whether Secure Boot Is Enabled
sudo mokutil --sb-state
Typical output is along the lines of:
SecureBoot enabled
10.2 Check for Secure Boot and Integrity Messages in the Kernel Log
sudo dmesg | grep -Ei 'secure boot|integrity|lockdown|EFI: Loaded cert'
On RHEL systems, integrity log messages often show where keys came from, such as:
UEFI:db- embedded
shim UEFI:MokListRT
10.3 List Trusted Platform Keys
sudo keyctl list %:.platform
sudo keyctl list %:.builtin_trusted_keys
sudo keyctl list %:.blacklist
What you are looking for:
- firmware-derived certificates
- MOK-enrolled certificates
- revoked hashes or keys in
.blacklist
10.4 Inspect Signatures on EFI Binaries
On RHEL, pesign is the supported tool path for inspecting and adding signatures to relevant EFI binaries:
sudo pesign --show-signature --in /boot/efi/EFI/redhat/shimx64.efi
sudo pesign --show-signature --in /boot/efi/EFI/redhat/grubx64.efi
On AArch64 systems, the names commonly change to shimaa64.efi and grubaa64.efi.
11. Key RHEL Packages and Tools
When working with custom Secure Boot signing on RHEL 8/9/10, Red Hat documentation centers on these tools:
sudo dnf install pesign openssl kernel-devel mokutil keyutils
Main roles:
| Tool | Purpose |
|---|---|
pesign | Sign and inspect EFI binaries and kernels |
efikeygen | Generate a Secure Boot-oriented X.509 key pair in the pesign database |
mokutil | Enroll and inspect Machine Owner Keys and Secure Boot state |
keyctl | Inspect kernel keyrings |
sign-file | Append Linux kernel module signatures |
certutil / pk12util | Export keys and certificates from the NSS database used by pesign |
openssl | Extract or transform key material when needed |
12. Generating a Custom Secure Boot Key on RHEL
12.1 Generate a Key for Module Signing
Red Hat documents efikeygen as the standard way to create a self-signed X.509 pair for Secure Boot workflows:
sudo efikeygen \
--dbdir /etc/pki/pesign \
--self-sign \
--module \
--common-name 'CN=Organization signing key' \
--nickname 'Custom Secure Boot key'
12.2 Generate a Key for Kernel Signing
sudo efikeygen \
--dbdir /etc/pki/pesign \
--self-sign \
--kernel \
--common-name 'CN=Organization signing key' \
--nickname 'Custom Secure Boot key'
12.3 FIPS-Aware Note
Red Hat documentation notes that in FIPS mode you may need to specify the NSS token explicitly:
sudo efikeygen \
--dbdir /etc/pki/pesign \
--self-sign \
--kernel \
--common-name 'CN=Organization signing key' \
--nickname 'Custom Secure Boot key' \
--token 'NSS FIPS 140-2 Certificate DB'
The generated material is stored under /etc/pki/pesign/.
13. Enrolling the Public Certificate with MOK
This is the step people skip, and then they waste hours blaming Secure Boot instead of their own process.
Generating a key is not enough. The target system must trust the corresponding public certificate.
13.1 Export the Public Certificate
sudo certutil -d /etc/pki/pesign \
-n 'Custom Secure Boot key' \
-Lr > sb_cert.cer
13.2 Import It into MOK
sudo mokutil --import sb_cert.cer
You will be prompted to set a temporary enrollment password.
13.3 Reboot and Complete Enrollment
On the next boot:
shimnotices the pending enrollment.MokManager.efistarts.- You choose
Enroll MOK. - You enter the password you set during
mokutil --import. - The certificate is added to the persistent MOK list.
Once enrolled on a Secure Boot-enabled system, the key is propagated into the .platform keyring on subsequent boots.
14. Signing Custom Kernel Modules on RHEL
This is one of the most common real-world Secure Boot tasks. Out-of-tree drivers, vendor modules, HBA agents, monitoring probes, or security products often fail here.
14.1 Export the Public Certificate
Export the public certificate as described in section 13.1 to produce sb_cert.cer.
14.2 Export the Private Key from the NSS Database
sudo pk12util -o sb_cert.p12 \
-n 'Custom Secure Boot key' \
-d /etc/pki/pesign
Then extract the private key:
openssl pkcs12 \
-in sb_cert.p12 \
-out sb_cert.priv \
-nocerts \
-noenc
That produces an unencrypted private key. Treat that file like a live weapon, because that is what it is.
14.3 Sign the Module
sudo /usr/src/kernels/$(uname -r)/scripts/sign-file \
sha256 \
sb_cert.priv \
sb_cert.cer \
my_module.ko
This appends the module signature directly to the kernel module file.
14.4 Verify the Signer
modinfo my_module.ko | grep signer
14.5 Load the Module
sudo insmod my_module.ko
or after placing it under the module tree:
sudo cp my_module.ko /lib/modules/$(uname -r)/extra/
sudo depmod -a
sudo modprobe my_module
14.6 Validity-Date Operational Warning
Red Hat documentation warns administrators to sign kernels and modules within the certificate validity period and also notes that sign-file does not warn about bad timing decisions. Do not treat the absence of a tool warning as proof that your signing workflow is sane.
15. Signing Kernels and EFI Binaries on RHEL
15.1 Sign a Kernel on x86_64
sudo pesign \
--certificate 'Custom Secure Boot key' \
--in vmlinuz-version \
--sign \
--out vmlinuz-version.signed
Inspect the result:
sudo pesign --show-signature --in vmlinuz-version.signed
Replace the unsigned image with the signed one:
sudo mv vmlinuz-version.signed vmlinuz-version
If you skip this step, the system still boots the unsigned original.
15.2 Sign a GRUB EFI Binary on x86_64
sudo pesign \
--in /boot/efi/EFI/redhat/grubx64.efi \
--out /boot/efi/EFI/redhat/grubx64.efi.signed \
--certificate 'Custom Secure Boot key' \
--sign
Inspect the result:
sudo pesign --in /boot/efi/EFI/redhat/grubx64.efi.signed --show-signature
Replace the unsigned binary with the signed one:
sudo mv /boot/efi/EFI/redhat/grubx64.efi.signed /boot/efi/EFI/redhat/grubx64.efi
15.3 AArch64 Note
On AArch64 systems, you will typically work with:
/boot/efi/EFI/redhat/shimaa64.efi/boot/efi/EFI/redhat/grubaa64.efi
RHEL documentation also covers the decompression/recompression workflow for signing kernel images on 64-bit ARM.
16. Where Secure Boot Trust Appears Inside the Running RHEL Kernel
RHEL documentation shows that a Secure Boot-enabled system can expose evidence of loaded trust sources in both kernel logs and keyrings.
Examples of what you may see:
- Microsoft certificate entries sourced from UEFI
db - Red Hat Secure Boot keys sourced from embedded
shim - owner-enrolled keys sourced from
MokListRT - revocations reflected in
.blacklist
That gives you three different trust sources in play:
- Firmware trust
- Embedded vendor trust
- Owner-added trust
If you do not know which of the three your system is using for a given module or EFI binary, you are troubleshooting blind.
17. Secure Boot Revocation and dbx
Revocation is where a lot of “but it used to work” incidents come from.
The dbx database contains revoked signatures, certificates, or hashes. If an object chains to a revoked entry, the system rejects it even if it used to be trusted.
Operational consequences:
- old boot loaders can stop working after revocation updates
- vulnerable or deprecated signatures can become unacceptable
- lab systems that never receive firmware updates drift into weird compatibility states
- custom boot chains break if you anchored them to something that later lands in revocation data
Inside Linux, revoked entries are represented through the blacklist keyring. That is why trust alone is not enough; the object must also not be revoked.
18. Microsoft 2011 Secure Boot Certificate Expiration
The Microsoft UEFI CA 2011 signing certificate, which has been the primary trust anchor used by firmware to validate shim on virtually all x86_64 commodity hardware, is scheduled to expire on June 27, 2026.
This does not mean existing systems immediately stop booting. Systems that already have the 2011 certificate enrolled in firmware db will continue to accept binaries signed with that certificate after the expiration date. However, Microsoft will no longer sign new binaries with the 2011 key after expiration, so future shim updates must be signed with the replacement Microsoft UEFI CA 2023 certificate.
18.1 What Red Hat Has Done
Red Hat released new shim binaries for all supported RHEL 8, RHEL 9, and RHEL 10 releases on x86_64 that are dual-signed with both the Microsoft 2011 and Microsoft 2023 Secure Boot signing certificates. This means the new shim will boot on systems that have either or both certificates enrolled in firmware.
On AArch64, starting with RHEL 9.7 and RHEL 10.0, the shim binary is signed only with the Microsoft 2023 certificate.
18.2 Checking Which Certificate Signed Your shim
sudo pesign -S -i /boot/efi/EFI/redhat/shimx64.efi
If you see Microsoft Windows UEFI Driver Publisher, that is the 2011 certificate path. If you see references to the 2023 certificate, the system is using the updated signing path.
18.3 What Administrators Must Do
- Update
shimon all supported RHEL systems to get the dual-signed version before relying on firmware updates that add the 2023 certificate. - Watch for firmware updates from hardware vendors that enroll the Microsoft 2023 certificate into the UEFI
db. Without the 2023 certificate in firmware, futureshimbinaries signed only with the 2023 key will not be accepted. - Be aware of TPM impact: updates to UEFI
dbwill change TPM Platform Configuration Register (PCR) values, particularly PCR7. If you use TPM-based auto-unlock for LUKS-encrypted volumes, Measured Boot attestation, or secrets sealed against PCR7, those bindings will break afterdbchanges. The recommended approach is to first reseal against a PCR value that did not change (such as PCR0), reboot, and then reseal against the new PCR7 value. - Legacy systems (old physical servers, appliances, or systems that never receive firmware updates) that cannot enroll the 2023 certificate will remain limited to booting
shimbinaries signed with the 2011 certificate.
18.4 Why This Matters for This Book
This is a concrete, real-world example of every concept this appendix covers: firmware trust databases, certificate lifecycle, revocation risk, TPM measurement sensitivity, and the operational cost of ignoring signing certificate management. If your organization did not know this was coming, your Secure Boot lifecycle management has a gap.
19. Secure Boot and RHEL Version Notes
19.1 RHEL 7
RHEL 7 established the basic Red Hat Secure Boot model:
shimas first-stage loader- Red Hat embedded key in
shim - signed GRUB and kernel
- MOK for owner-added trust
- signed kernel modules required on Secure Boot-enabled systems
RHEL 7 documentation also makes an important point that many people miss: classic Secure Boot is about kernel-space code integrity, not blanket validation of all user-space content.
19.2 RHEL 8
RHEL 8 keeps the same overall model, with improved public documentation around:
.builtin_trusted_keys.platform.blacklistefikeygenpesign- MOK enrollment procedures
RHEL 8 docs also explicitly show that MOK-enrolled keys propagate to .platform.
19.3 RHEL 9
RHEL 9 documents the same core trust path and is stricter and clearer operationally around:
- module signature validation against trusted keyrings
- MOK-driven platform trust
- signing custom kernels and modules
- lockdown behavior on Secure Boot systems
RHEL 9 is the practical baseline if you are designing a modern RHEL Secure Boot workflow today.
19.4 RHEL 10
RHEL 10 documentation continues the same toolchain and trust model for custom kernel and module signing with pesign, mokutil, efikeygen, keyctl, and kernel keyrings.
The important point is continuity: this is not a random feature that changes shape every release. The tool names and trust concepts stay recognizable across supported RHEL generations.
20. Common RHEL Use Cases
20.1 Third-Party Driver Deployment
Common examples:
- storage controller drivers
- monitoring agents with kernel modules
- endpoint security modules
- custom network drivers
- vendor hardware management modules
If the module is unsigned or signed by an untrusted key, the system refuses it on a Secure Boot-enabled host.
20.2 Custom Kernel Builds
If you build your own kernel, firmware and the boot chain do not care that it came from your CI pipeline. They only care whether the image is signed by a trusted key and whether the system has that trust anchor enrolled.
20.3 Environments Removing Vendor Trust Anchors
Some organizations want tighter control and reduce reliance on default third-party trust anchors. That is possible, but it means you own the full chain:
- signing policy
- private-key protection
- firmware trust management
- GRUB/kernel signing
- revocation strategy
- recovery path if your signing infrastructure fails
Most teams underestimate that operational burden.
20.4 Virtual Machines
Secure Boot also matters in virtualized environments if the guest firmware exposes UEFI Secure Boot support. Do not assume that “it is only a VM” means boot trust is irrelevant. Virtual infrastructure is one of the easiest places for unsigned custom images to proliferate.
21. Troubleshooting Secure Boot on RHEL
21.1 Fast Checks
sudo mokutil --sb-state
sudo mokutil --list-enrolled
sudo keyctl list %:.platform
sudo keyctl list %:.blacklist
sudo dmesg | grep -Ei 'secure boot|lockdown|integrity|module|cert'
21.2 Typical Failure Patterns
| Symptom | Likely Cause |
|---|---|
| Module will not load | Unsigned module or signer not trusted |
| Module shows signer but still fails | Key not enrolled on target system, wrong keyring path, or revoked signer |
| MOK import ran but trust not visible | Enrollment not completed in MokManager after reboot |
| EFI binary fails to boot | Missing trusted signature, bad replacement workflow, or revoked certificate/hash |
| Behavior changed after firmware update | db/dbx changes altered trust or revocation state |
| TPM-unlock workflow breaks after key updates | PCR measurements changed after Secure Boot database updates |
21.3 Kernel Log Clues
Look for messages mentioning:
integrityMokListRTEFI: Loaded certLockdownmodule verification failed
If you do not inspect the kernel log, you are guessing.
22. Best Practices for Secure Boot Certificate Management
22.1 Separate Roles
Do not use one signing key for everything unless you enjoy turning one compromise into a platform-wide incident.
Prefer separate keys for:
- boot loaders / EFI binaries
- kernels
- third-party or internal kernel modules
- emergency or break-glass workflows
22.2 Protect the Private Key Properly
Prefer:
- offline signing
- HSM-backed or token-backed key storage where possible
- minimal operator access
- audited signing steps
- certificate rotation planning
Avoid:
- leaving unencrypted extracted keys on build hosts
- copying signing material across ad hoc CI jobs
- long-lived throwaway lab keys reused in production
22.3 Keep MOK Enrollment Narrow
Every extra trusted certificate expands what the platform can accept. Enroll only what you need. “Just add another MOK to get it working” is how trust sprawl starts.
22.4 Track Revocation and Firmware Updates
If you depend on custom Secure Boot trust:
- monitor firmware and
dbxupdates - test recovery paths before broad rollout
- validate boot on staging hardware
- understand the impact on TPM-sealed secrets and attestation
22.5 Keep Secure Boot and TLS PKI Mentally Separate
The same X.509 concepts appear in both areas, but the operational worlds are different.
Do not confuse:
- firmware trust databases with
/etc/pki/ca-trust - MOK enrollment with CA trust anchor installation
- module signing keys with web TLS server certificates
They use related cryptography, but they solve different problems.
23. Hard Truths Administrators Usually Learn Late
- Secure Boot is easy until you need custom code.
- Custom code is easy until you need durable signing operations.
- Durable signing operations are easy until you need revocation, rotation, attestation, and fleet recovery.
Most teams do not fail Secure Boot because the cryptography is too hard. They fail because their operational model is sloppy:
- no key ownership model
- no certificate lifecycle planning
- no test hardware
- no rollback path
- no idea what is trusted by firmware vs
shimvs MOK vs kernel keyrings
That is not a technical limitation. That is process failure.
24. Quick Reference
┌──────────────────────────────────────────────────────────────────────┐
│ RHEL SECURE BOOT QUICK REFERENCE │
├──────────────────────────────────────────────────────────────────────┤
│ Check state: mokutil --sb-state │
│ List MOKs: mokutil --list-enrolled │
│ List platform keys: keyctl list %:.platform │
│ Built-in keys: keyctl list %:.builtin_trusted_keys │
│ Revocations: keyctl list %:.blacklist │
│ View EFI signature: pesign --show-signature --in <efi-binary> │
│ │
│ RHEL boot path: firmware -> shim -> GRUB -> kernel -> modules │
│ │
│ Firmware DBs: PK / KEK / db / dbx │
│ Owner trust: MOK -> .platform │
│ Kernel trust: .builtin_trusted_keys + .platform │
│ Revocation: .blacklist / dbx │
│ │
│ Common packages: pesign openssl kernel-devel mokutil keyutils │
└──────────────────────────────────────────────────────────────────────┘
25. Key Takeaways
- Secure Boot is a certificate-driven authorization chain for boot and kernel-space code.
- On RHEL,
shimis the bridge between firmware trust and Red Hat-controlled trust. - MOK is the critical mechanism for adding owner-controlled trust without rewriting firmware databases.
- Kernel module loading depends on trusted keyrings, not on the user-space CA bundle.
- Revocation matters as much as trust;
dbxand.blacklistcan break “previously working” artifacts. - If you are signing custom modules or kernels, the problem is not just cryptography. It is lifecycle management.
26. Official RHEL Reading Worth Keeping Nearby
- RHEL 8/9/10 kernel management documentation on signing kernels and modules for Secure Boot
- RHEL 7 kernel administration and Secure Boot guidance
kernel_lockdown(7)mokutil(1)keyctl(1)pesign(1)
Appendix I: Glossary
Glossary of PKI & Certificate Terms
A
ACME (Automated Certificate Management Environment) Protocol (RFC 8555) for automated certificate issuance and renewal, popularised by Let’s Encrypt.
ASN.1 (Abstract Syntax Notation One) Data serialisation format used to encode X.509 certificates.
Asymmetric Cryptography Public-key system where encryption/decryption use complementary key pairs (public and private).
B
Baseline Requirements CAB Forum rules that publicly-trusted CAs must follow (e.g., 398-day maximum validity).
C
CA (Certificate Authority) Entity that issues and signs digital certificates.
CAB Forum (CA/Browser Forum) Industry consortium defining standards for publicly-trusted TLS certificates.
Certificate Chain Sequence of certificates from end-entity -> intermediate(s) -> root CA.
Certificate Policy (CP) High-level document describing what assurances a PKI provides.
Certificate Revocation List (CRL) Signed list of revoked certificate serial numbers.
Certificate Signing Request (CSR) Message requesting a CA to sign a public key, includes subject DN and extensions.
Certificate Transparency (CT) Public log system (RFC 6962) that records all issued certificates for auditability.
Certification Practice Statement (CPS) Detailed operational procedures of how a CA implements its CP.
Cipher Suite Set of cryptographic algorithms used in TLS (e.g., key exchange, encryption, MAC).
CN (Common Name) Field in certificate subject; historically held domain name, now superseded by SAN.
Code Signing Certificate
Certificate with extKeyUsage: codeSigning for authenticating software publishers.
D
DER (Distinguished Encoding Rules) Binary encoding of ASN.1, used for certificates in Java keystores and embedded systems.
DH (Diffie-Hellman) Key-exchange algorithm allowing two parties to agree on a shared secret over insecure channel.
DN (Distinguished Name)
Hierarchical identifier in X.500 format (e.g., /C=US/O=Example/CN=server.example.com).
E
ECC (Elliptic Curve Cryptography) Public-key crypto using elliptic curves; offers smaller keys than RSA for equivalent security.
ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) Key-exchange providing forward secrecy in TLS.
ECDSA (Elliptic Curve Digital Signature Algorithm) Signature scheme based on elliptic curves.
EKU (Extended Key Usage)
X.509 extension specifying allowed certificate purposes (serverAuth, clientAuth, codeSigning, etc.).
End-Entity Certificate Leaf certificate issued to servers, devices, or users (not a CA).
EST (Enrollment over Secure Transport) RFC 7030 protocol for certificate enrollment, simpler than full SCEP.
F
FIPS 140-2 U.S. government standard for cryptographic module security (Levels 1-5).
Forward Secrecy Property ensuring past session keys remain secure even if long-term private key is compromised (achieved via ephemeral DH/ECDHE).
H
HSM (Hardware Security Module) Tamper-resistant device storing cryptographic keys and performing operations in hardware.
HSTS (HTTP Strict Transport Security) Header forcing browsers to use HTTPS for a domain.
I
Intermediate CA CA certificate signed by root CA, used to issue end-entity certificates.
Issuer DN of the CA that signed a certificate.
J
JWKS (JSON Web Key Set) Set of public keys in JSON format, often used with OAuth/OpenID Connect.
JWT (JSON Web Token) Compact token format for transmitting claims; can be signed with RSA/ECDSA.
K
Key Escrow Practice of storing private keys with a third party; controversial for user privacy.
Key Usage
X.509 extension defining permitted cryptographic operations (digitalSignature, keyEncipherment, etc.).
Keystore File storing private keys and certificates (e.g., Java JKS, PKCS#12).
L
LDAP (Lightweight Directory Access Protocol) Protocol for accessing directory services; often used to publish certificates and CRLs.
M
mTLS (Mutual TLS) TLS where both client and server present certificates for bidirectional authentication.
N
NSS (Network Security Services) Mozilla’s crypto library used by Firefox; maintains its own trust store.
O
OCSP (Online Certificate Status Protocol) Real-time protocol (RFC 6960) for checking certificate revocation status.
OCSP Stapling Server includes fresh OCSP response in TLS handshake, improving privacy and performance.
OID (Object Identifier)
Unique numeric identifier in ASN.1 (e.g., 2.5.29.17 for SAN extension).
P
PEM (Privacy Enhanced Mail)
Base64-encoded DER with -----BEGIN CERTIFICATE----- headers.
PFX See PKCS#12.
PIN (Public Key Pinning) Mechanism to associate domain with specific public key(s); deprecated in browsers due to operational risks.
PKI (Public Key Infrastructure) System of hardware, software, policies, and procedures for managing digital certificates.
PKCS (Public-Key Cryptography Standards) Family of standards by RSA Labs (PKCS#1-15).
PKCS#7 Container format for certificates and CRLs (no private key).
PKCS#12
Archive format bundling certificate + private key + chain, password-protected (.p12, .pfx).
R
RA (Registration Authority) Entity validating identity before forwarding CSR to CA.
Root CA Self-signed CA at top of hierarchy; embedded in OS/browser trust stores.
RSA (Rivest-Shamir-Adleman) Widely-used public-key algorithm based on integer factorisation.
S
SAN (Subject Alternative Name) X.509 extension listing additional identities (DNS names, IPs, URIs).
SCT (Signed Certificate Timestamp) Proof from CT log that certificate has been logged.
Self-Signed Certificate Certificate where issuer == subject; not trusted by default.
Serial Number Unique identifier assigned by CA to each certificate.
SHA-256 / SHA-384 Cryptographic hash functions (part of SHA-2 family).
SPIFFE (Secure Production Identity Framework For Everyone)
Standard for workload identity in dynamic environments; uses URI SANs (spiffe://trust-domain/workload).
SSL (Secure Sockets Layer) Deprecated predecessor to TLS.
T
TLS (Transport Layer Security) Cryptographic protocol for secure network communication (current versions: 1.2, 1.3).
TPM (Trusted Platform Module) Hardware chip for secure key storage on devices.
Trust Anchor Root certificate implicitly trusted by a system.
Trust Store
Collection of trusted root CA certificates (e.g., /etc/pki/ca-trust, Windows Certificate Store).
V
VA (Validation Authority) Component handling OCSP/CRL queries.
W
Wildcard Certificate
Certificate covering *.example.com (matches app.example.com, not sub.app.example.com).
X
X.509 ITU-T standard defining certificate format (RFC 5280 is IETF version).
Z
Zero Trust Security model assuming no implicit trust; every request authenticated and authorised.
Appendix J: References
References & Further Reading
Curated list of authoritative resources for deepening your PKI and certificate knowledge.
Standards & RFCs
Core PKI Standards
-
RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and CRL Profile https://datatracker.ietf.org/doc/html/rfc5280
-
RFC 6960 - Online Certificate Status Protocol (OCSP) https://datatracker.ietf.org/doc/html/rfc6960
-
RFC 6962 - Certificate Transparency https://datatracker.ietf.org/doc/html/rfc6962
-
RFC 8555 - Automatic Certificate Management Environment (ACME) https://datatracker.ietf.org/doc/html/rfc8555
-
RFC 7030 - Enrollment over Secure Transport (EST) https://datatracker.ietf.org/doc/html/rfc7030
TLS/SSL
-
RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 https://datatracker.ietf.org/doc/html/rfc8446
-
RFC 6125 - Representation and Verification of Domain-Based Application Service Identity https://datatracker.ietf.org/doc/html/rfc6125
Cryptographic Algorithms
-
RFC 3447 - RSA Cryptography Specifications (PKCS #1 v2.1) https://datatracker.ietf.org/doc/html/rfc3447
-
RFC 6090 - Fundamental Elliptic Curve Cryptography Algorithms https://datatracker.ietf.org/doc/html/rfc6090
-
FIPS 186-5 - Digital Signature Standard (DSS) https://csrc.nist.gov/publications/detail/fips/186/5/final
Industry Guidelines
CA/Browser Forum
-
Baseline Requirements for TLS Certificates https://cabforum.org/baseline-requirements-documents/
-
EV SSL Certificate Guidelines https://cabforum.org/extended-validation/
NIST Publications
-
SP 800-57 - Recommendation for Key Management https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final
-
SP 800-207 - Zero Trust Architecture https://csrc.nist.gov/publications/detail/sp/800-207/final
-
SP 800-52 Rev. 2 - Guidelines for TLS Implementations https://csrc.nist.gov/publications/detail/sp/800-52/rev-2/final
ETSI Standards
- ETSI EN 319 411-1 - Policy and security requirements for Trust Service Providers issuing certificates https://www.etsi.org/standards
Books
Foundational
-
“Network Security with OpenSSL” by Pravir Chandra, Matt Messier, John Viega O’Reilly, 2002 - Comprehensive OpenSSL guide
-
“PKI Uncovered” by Andre Karamanian, Siva Sathianathan Cisco Press, 2011 - Enterprise PKI design patterns
-
“Bulletproof SSL and TLS” by Ivan Ristic Feisty Duck, 2022 - Authoritative guide to deploying TLS correctly
Advanced
-
“Serious Cryptography” by Jean-Philippe Aumasson No Starch Press, 2017 - Modern cryptographic algorithms and protocols
-
“Applied Cryptography” by Bruce Schneier Wiley, 1996 - Classic text on cryptographic protocols
Online Resources
Documentation
-
OpenSSL Documentation https://www.openssl.org/docs/
-
Let’s Encrypt Documentation https://letsencrypt.org/docs/
-
cert-manager Documentation https://cert-manager.io/docs/
-
HashiCorp Vault PKI Secrets Engine https://www.vaultproject.io/docs/secrets/pki
-
FreeIPA Documentation https://www.freeipa.org/page/Documentation
Testing Tools
-
SSL Labs Server Test https://www.ssllabs.com/ssltest/ Test HTTPS server configuration and certificate chain
-
testssl.sh https://testssl.sh/ Command-line TLS/SSL testing tool
-
crt.sh - Certificate Search https://crt.sh/ Query Certificate Transparency logs
-
Hardenize https://www.hardenize.com/ Comprehensive TLS/PKI scanner
Tutorials & Blogs
-
Cloudflare Learning Center - SSL/TLS https://www.cloudflare.com/learning/ssl/
-
Mozilla SSL Configuration Generator https://ssl-config.mozilla.org/ Generate secure TLS configs for common servers
-
PKI Solutions Blog https://pkisolutions.com/blog/ Enterprise PKI insights
Videos & Courses
-
“Public Key Cryptography” (Khan Academy) Introduction to RSA and key exchange
-
“How HTTPS Works” (Cloudflare YouTube) Animated explanation of TLS handshake
-
Pluralsight - “PKI Architecture and Implementation” Comprehensive video course on enterprise PKI
Software & Tools
CA Software
- OpenSSL - https://www.openssl.org/
- FreeIPA - https://www.freeipa.org/
- EJBCA - https://www.ejbca.org/
- step-ca - https://smallstep.com/docs/step-ca
- HashiCorp Vault - https://www.vaultproject.io/
Certificate Management
- cert-manager (Kubernetes) - https://cert-manager.io/
- Certbot (ACME client) - https://certbot.eff.org/
- certmonger (RHEL) - https://pagure.io/certmonger
- Venafi (Enterprise) - https://www.venafi.com/
Libraries
- BouncyCastle (Java/C#) - https://www.bouncycastle.org/
- cryptography (Python) - https://cryptography.io/
- Go crypto/x509 - https://pkg.go.dev/crypto/x509
Communities & Forums
-
Let’s Encrypt Community Forum https://community.letsencrypt.org/
-
r/crypto (Reddit) https://www.reddit.com/r/crypto/
-
r/netsec (Reddit) https://www.reddit.com/r/netsec/
-
IETF TLS Working Group https://datatracker.ietf.org/wg/tls/about/
Research Papers
-
“The Most Dangerous Code in the World” (Martin et al., 2012) Analysis of SSL certificate validation vulnerabilities
-
“Analysis of the HTTPS Certificate Ecosystem” (Durumeric et al., IMC 2013) Large-scale study of TLS deployment
-
“SoK: SSL and HTTPS Revisiting past challenges and evaluating certificate trust model enhancements” (Clark & van Oorschot, S&P 2013)
Compliance Frameworks
-
PCI DSS - Payment Card Industry Data Security Standard https://www.pcisecuritystandards.org/
-
HIPAA - Health Insurance Portability and Accountability Act https://www.hhs.gov/hipaa/
-
SOC 2 - Service Organization Control 2 https://www.aicpa.org/
-
eIDAS - EU electronic identification and trust services https://digital-strategy.ec.europa.eu/en/policies/eidas-regulation
Stay Updated: PKI and TLS standards evolve continuously. Subscribe to the IETF TLS working group mailing list and follow security advisories from your CA and software vendors.