SSL configuration on a web server

Many websites describe how to correctly set up a SSL/TLS web server with a secure configuration. Here is a list of some that I’ve found:

Here are example configurations for ssl.example.com host with a certificate signed by StartSSL (https://www.startssl.com/) or Let’s encrypt (https://letsencrypt.org/) or AlwaysOnSSL (https://alwaysonssl.com/).

Apache:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName ssl.example.com
    SSLEngine on
    SSLCertificateFile /etc/ssl/ssl.example.com.crt
    SSLCertificateKeyFile /etc/ssl/ssl.example.com.key
    SSLCertificateChainFile /etc/ssl/intermediate.pem
    SSLCACertificateFile /etc/ssl/certs
    SSLProtocol ALL -SSLv2 -SSLv3
    SSLHonorCipherOrder On
    SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    SSLCompression Off

    # Enable this if your want HSTS (recommended, but be careful)
    # Header add Strict-Transport-Security "max-age=15768000"

    # OCSP Stapling, only in httpd 2.3.3 and later
    SSLUseStapling On
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off
    SSLStaplingCache shmcb:/var/run/ocsp(128000)
</VirtualHost>
</IfModule>

Nginx:

server {
    listen 443;
    server_name ssl.example.com;
    ssl on;
    # certs sent to the client in SERVER HELLO are concatenated
    ssl_certificate /etc/ssl/ssl.example.com_and_intermediates.pem;
    ssl_certificate_key /etc/ssl/ssl.example.com.key;
    ssl_dhparam /etc/ssl/dhparam.pem;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    # Enable this if your want HSTS (recommended, but be careful)
    # add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
    # IP address of the DNS resolver to be used to obtain the IP address of
    # the OCSP responder
    resolver 127.0.0.1;
}

To generate the Diffie-Hellman parameters for DHE ciphersuites, use:

openssl dhparam -out /etc/ssl/dhparam.pem 4096

Testing configuration

Here are several commands to be used to check a running HTTPS web server.

Establish an TLS (or SSL) connection:

openssl s_client -connect ssl.example.com:443 -servername ssl.example.com -showcerts

Use a web service, like Qualys’ SSL Server Test: https://www.ssllabs.com/ssltest/

This web page helps displays what your browser supports: https://www.ssllabs.com/ssltest/viewMyClient.html

Using Let’s Encrypt

The official Let’s Encrypt client (https://github.com/letsencrypt/letsencrypt) needs to run as root on the server which will use the certificate. Thankfully there exists other ways of signing certificates:

These methods rely on some openssl commands to use an account key.

  • To create an account key and print the associated public key:

    openssl genrsa 4096 > account.key
    openssl rsa -in account.key -pubout
    
  • To sign what needs to be signed:

    echo -n 'Content' | openssl dgst -sha256 -hex -sign account.key
    

To generate a Certificate Signing Request (CSR) for a domain with X509v3 Subject Alternative Name (SAN), these commands can be used:

openssl genrsa 4096 > ssl.example.com.key
openssl req -new -sha256 -key ssl.example.com.key -subj "/" \
    -reqexts SAN -config <(cat /etc/ssl/openssl.cnf
    <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:ssl.example.com"))

On some systems the OpenSSL configuration file lie elsewhere, for example in /etc/pki/tls/openssl.cnf or in /System/Library/OpenSSL/openssl.cnf (Mac OS).

In order to validate the ownership of a domain, a generated file needs to be served over HTTP (not HTTPS), which may for example give:

$ curl http://ssl.example.com/.well-known/acme-challenge/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg

To do this on a live (production system), a possible way consists in serving /.well-known/acme-challenge from a specific directory, where an administrator will put the needed files. With nginx, a configuration can be:

server {
    listen 127.0.0.1:80;
    listen [::1]:80;
    server_name ssl.example.com;

    # Let's encrypt
    location /.well-known/acme-challenge {
        alias /var/acme-challenge/ssl.example.com;
    }
    location / {
        rewrite ^(.*) https://ssl.example.com$1 permanent;
    }
}

Then an admin can put the files needed for Let’s Encrypt to verify domain ownership in directory /var/acme-challenge/ssl.example.com/.