Home Blog Certs Knowledge Base About

LPIC-2 208.2 โ€” Apache HTTPS Configuration

Exam topic 208.2 โ€” Apache HTTPS Configuration (weight: 3). Covers SSL/TLS setup with mod_ssl, certificate management, SNI, virtual hosting over HTTPS, and hardening against known attacks.


SSL/TLS Basics

Client                              Server (Apache + mod_ssl)
  |                                        |
  |---- request public key --------------->|
  |<--- send (CA-signed) public key -------|
  |                                        |
  |  verify signature against CA trust     |
  |  (locally, in browser)                 |
  |                                        |
  |---- encrypt session key with pub key ->|
  |<--- decrypt with private key ----------|
  |         (symmetric session from here)  |

Asymmetric cryptography (PKC):

  • Public key โ€” encrypts messages, distributed openly (in the certificate)
  • Private key โ€” decrypts messages, never leaves the server
  • Certificate is signed by a CA (Certificate Authority) โ€” browser trusts it

SSL File Locations

TypeDebianRed Hat
Certificates/etc/ssl/certs//etc/pki/tls/certs/
Private keys/etc/ssl/private//etc/pki/tls/private/
OpenSSL config/etc/ssl/openssl.cnf/etc/pki/tls/openssl.cnf

Best practice: create a subdirectory per site, e.g. /etc/ssl/webserver/.


Installing mod_ssl

# Red Hat / CentOS
yum install httpd mod_ssl

# Debian / Ubuntu
apt-get install apache2 openssl

Enabling on Debian:

a2enmod ssl
service apache2 restart

# Check:
apachectl status | grep -i ssl

Enabling on Red Hat (in /etc/httpd/conf.d/ssl.conf):

LoadModule ssl_module modules/mod_ssl.so
Listen 443

Generating a Key and CSR (for a Commercial CA)

# Step 1: Generate private key (2048-bit RSA, encrypted with 3DES)
openssl genrsa -des3 -out server.key 2048

# Step 1a (optional): Strip passphrase for automatic Apache restart
openssl rsa -in server.key -out stripped.key
# server.key = encrypted; stripped.key = plaintext (guard carefully)

# Step 2: Create CSR (Certificate Signing Request)
openssl req -new -key server.key -out server.csr
# When prompted for "Common Name", enter the FQDN: www.example.com

Common Name (CN) rules:

CN valueValid for
www.foo.exampleOnly https://www.foo.example
foo.exampleNOT valid for www.foo.example
*.foo.exampleAll subdomains, but NOT foo.example itself

After creating the CSR:

  1. Send server.csr to a commercial CA (DigiCert, Comodo, etc.)
  2. Receive signed certificate newcert.pem
  3. The .csr file is no longer needed

Creating a Self-Signed Certificate

mkdir /etc/ssl/webserver

openssl req -new -x509 -days 365 -nodes \
  -out /etc/ssl/webserver/apache.pem \
  -keyout /etc/ssl/webserver/apache.key
FlagMeaning
-x509Create certificate directly (not a CSR)
-nodesNo passphrase on key (for automatic startup)
-days 365Certificate validity period

Self-signed certificates trigger browser warnings โ€” the CA is unknown. Use only for testing or internal networks.

# Inspect certificate
openssl x509 -in /etc/ssl/webserver/apache.pem -text -noout

# Test SSL connection
openssl s_client -connect example.com:443
openssl s_client -connect example.com:443 -tls1_2

Creating Your Own CA with CA.pl

# Script location:
# Debian:   /usr/lib/ssl/misc/CA.pl
# Red Hat:  /etc/pki/tls/misc/CA.pl

# Step 1: Create CA root  (โ†’ ./demoCA/private/cakey.pem)
/usr/lib/ssl/misc/CA.pl -newca

# Step 2: Create certificate request  (โ†’ newreq.pem, newkey.pem)
/usr/lib/ssl/misc/CA.pl -newreq

# Step 3: Sign the request  (โ†’ newcert.pem)
/usr/lib/ssl/misc/CA.pl -signreq
FileContents
newcert.pemSigned certificate
newkey.pemPrivate key

Rename files for clarity: ssltest.example.com.crt and ssltest.example.com.key.


Key SSL Directives

DirectivePurpose
SSLEngine onEnable SSL for this VirtualHost
SSLCertificateFilePath to public certificate (PEM)
SSLCertificateKeyFilePath to private key (PEM)
SSLCACertificateFileConcatenated CA certificates (one file)
SSLCACertificatePathDirectory of CA certificates
SSLProtocolAllowed/forbidden TLS/SSL versions
SSLCipherSuiteColon-separated list of ciphers
SSLHonorCipherOrderServer (not client) selects cipher order
SSLCompressionEnable/disable SSL compression
SSLVerifyClientrequire = mutual TLS (client cert auth)
ServerTokensHow much version info to reveal in headers
ServerSignatureApache signature on error pages
TraceEnableAllow/deny HTTP TRACE method

SSLCACertificateFile vs SSLCACertificatePath:

# Single file โ€” concatenate all CAs
cat ca1.pem ca2.pem ca3.pem > /etc/ssl/certs/ca-bundle.pem
SSLCACertificateFile /etc/ssl/certs/ca-bundle.pem
# OR
SSLCACertificatePath /etc/ssl/certs/

Full HTTPS Virtual Host Configuration

ports.conf (Debian):

Listen 80
Listen 443

HTTP + HTTPS example:

<VirtualHost *:80>
    ServerName   example.com
    DocumentRoot /var/www/html
    Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
    SSLEngine             On
    SSLCertificateFile    /etc/ssl/webserver/apache.pem
    SSLCertificateKeyFile /etc/ssl/webserver/apache.key

    ServerName    example.com
    DocumentRoot  /var/www/html
    ErrorLog      /var/log/apache2/error.log
</VirtualHost>

SSL + Virtual Hosts Problem

The timing issue:

Client โ†’ TCP โ†’ SSL handshake โ†’ [certificate sent] โ†’ HTTP request โ†’ Apache reads Host:
                    ^
          Certificate already sent!
          Apache doesn't yet know which VirtualHost is needed
Virtual host typeSSLNotes
IP-basedNo problemUnique IP per site
Name-basedProblemOne IP โ€” Apache doesn’t know the hostname until after SSL handshake

Solution: SNI


SNI โ€” Server Name Indication

SNI is a TLS extension where the browser includes the hostname in the first SSL handshake message (client hello), before the encrypted channel is established.

Client ---- client hello + server_name=example.com ----> Apache
                                                           |
                                              Picks the right certificate
                                                           |
           <--------- Sends correct certificate -----------+

Limitation: server_name must contain a hostname/domain โ€” not an IP address.

Browsers without SNI support:

Browser/SystemSNI
All modern browsersSupported
Android 2.x (stock browser)Not supported
IE on Windows XP before SP3Not supported
Java < 1.7Not supported

Multiple HTTPS sites via SNI:

<VirtualHost *:443>
    ServerName site1.example.com
    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/site1.pem
    SSLCertificateKeyFile /etc/ssl/private/site1.key
    DocumentRoot /var/www/site1
</VirtualHost>

<VirtualHost *:443>
    ServerName site2.example.com
    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/site2.pem
    SSLCertificateKeyFile /etc/ssl/private/site2.key
    DocumentRoot /var/www/site2
</VirtualHost>

Fallback for non-SNI clients (multidomain certificate):

# Block without ServerName โ€” catches all requests without SNI
<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/multidomain.pem
    SSLCertificateKeyFile /etc/ssl/private/multidomain.key
</VirtualHost>

Wildcard and Multidomain Certificates

Wildcard (*.example.com):

URLValid?
https://www.example.comYes
https://mail.example.comYes
https://example.comNo (root domain)
https://sub.www.example.comNo (sub-subdomain)
<VirtualHost *:443>
    ServerName virtual01.example.com
    SSLCertificateFile /etc/ssl/certs/wildcard.example.com.pem
    # CN = *.example.com
</VirtualHost>

Certificate Chain

Root CA           โ† in browser trust store
    |
    +-- Intermediate CA (G2)  โ† may not be in browser!
            |
            +-- Your certificate

Problem: browser doesn’t know the intermediate CA โ€” chain error.

Solution:

# Concatenate: your cert first, then intermediate(s)
cat your_cert.pem intermediate.pem > chain.pem
# Modern way (Apache 2.4.8+) โ€” everything in one file
SSLCertificateFile /etc/ssl/certs/chain.pem

Disabling Weak Protocols and Ciphers

# TLS 1.2 only (minimum recommended)
SSLProtocol -All +TLSv1.2

# Strong ciphers only, no RC4
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!RC4

# Server chooses cipher order (not client)
SSLHonorCipherOrder On

# Disable compression (CRIME attack protection)
SSLCompression Off

Protocol status:

ProtocolStatusReason
SSLv2ForbiddenRemoved from Apache 2.4+
SSLv3ForbiddenPOODLE, DROWN
TLS 1.0DeprecatedBEAST
TLS 1.1DeprecatedWeak ciphers
TLS 1.2RecommendedCurrent standard
TLS 1.3PreferredMost secure

Ciphers to block:

CipherIdentifierReason
RC4 / Arcfour!RC4Known vulnerabilities
CBC ciphers!CBCBEAST, Lucky13
NULL ciphers!aNULLNo encryption
MD5!MD5Weak hash
EXPORT!EXPORTIntentionally weakened

Security Directives

ServerTokens

Controls content of the Server: HTTP header:

ValueExample Server: header
Full (default)Apache/2.4.41 (Ubuntu) OpenSSL/1.1.1f PHP/7.4
OSApache/2.4.41 (Ubuntu)
MinorApache/2.4
MajorApache/2
Prod (recommended)Apache
# Global directive (outside any VirtualHost!)
ServerTokens Prod

Completely hiding or changing the header value requires compiling Apache from source.

ServerSignature

ServerSignature Off     # no signature (recommended for production)
ServerSignature On      # show version (useful in proxy chains)
ServerSignature Email   # show version + ServerAdmin email

TraceEnable

ValueBehavior
on (default)TRACE allowed per RFC 2616
offServer returns 405 Method Not Allowed
extendedTRACE with request body โ€” debug only
TraceEnable off

HTTP TRACE is a legitimate part of HTTP/1.1 (RFC 2616). Disable only if there is a specific reason, despite what vulnerability scanners may report.


Include vs IncludeOptional

# Required include โ€” Apache won't start if no files match
Include /etc/apache2/conf-enabled/*.conf

# Optional include โ€” silently ignored if no files match
IncludeOptional /etc/apache2/sites-enabled/*.conf

Exam Cheat Sheet

Attacks and Mitigations

AttackVulnerable componentDirective
POODLESSLv3SSLProtocol -SSLv3
DROWNSSLv2SSLProtocol -SSLv2
CRIMESSL compressionSSLCompression off
BEASTCBC + TLS 1.0SSLProtocol -TLSv1 + !CBC
RC4 attacksRC4/Arcfour cipherSSLCipherSuite !RC4
DowngradeClient forces weak cipherSSLHonorCipherOrder on
XSTHTTP TRACE methodTraceEnable off
Info leakApache version in headersServerTokens Prod

openssl Commands

openssl genrsa -des3 -out server.key 2048           # generate private key
openssl rsa -in server.key -out stripped.key         # remove passphrase
openssl req -new -key server.key -out server.csr     # create CSR
openssl req -new -x509 -days 365 -nodes \
  -out apache.pem -keyout apache.key                 # self-signed cert
openssl x509 -in apache.pem -text -noout             # inspect certificate
openssl s_client -connect example.com:443            # test connection

Complete Hardened HTTPS Config

# Global
ServerTokens Prod
ServerSignature Off
TraceEnable off

Listen 80
Listen 443

<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName    example.com
    DocumentRoot  /var/www/html

    SSLEngine on
    SSLCertificateFile    /etc/ssl/webserver/apache.pem
    SSLCertificateKeyFile /etc/ssl/webserver/apache.key

    SSLProtocol         -All +TLSv1.2
    SSLCipherSuite      EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!RC4
    SSLHonorCipherOrder On
    SSLCompression      Off
</VirtualHost>

CA.pl Script Locations

DistributionPath
Debian/usr/lib/ssl/misc/CA.pl
Red Hat/etc/pki/tls/misc/CA.pl