The Gig of Ham

One geek's contributions to the series of tubes

Dec 16, 2014 - 7 minute read - Comments - Internet linux sysadmin

SSL Labs A+ Certification for Apache 2.4

After gaining some insight from Seth Vargo’s excellent post on doing the same things with nginx, I decided to tweak my Apache 2.4 config to get the same results:

Screenshot - 12152014 - 10:18:30 PM

At first, I got everything but the TLS_FALLBACK_SCSV support. I did some digging and discovered that I had missed and OpenSSL update. Applying that and then restarting apache did the trick. Here are the relevant security announcements with the required versions of OpenSSL for ubuntu, debian, and RHEL/CentOS. The server I configured is running Ubuntu 14.04 LTS.

As Seth mentions, you will need an SSL certificate. I get mine for free from StartSSL. No really, they are free and they are recognized by all modern browsers and phones. They even sign all their certs using SHA256 and provide a full SHA256 chain (if you know where to look). This is important if any of your users are using Chrome or Chromium. (UPDATE: Mozilla Firefox too.) You can pay for some upgrades, like organizational verification (which I do), for an annual fee and then you can still get as many certificates as you like. Same model for their EV certs as well, just be aware that you may need to stack other certification levels on your account before adding EV support. Still, once you pay those fees: all your certificates are EV. Kind of a sweet deal, I just wish they had an API.

Now, on to apache. The configuration is a bit more complex than nginx, I break it into two parts: one per site (which I break out to a file and include in all my sites for easier maintenance), and one for the mod_ssl module itself. Let’s start with the mod_ssl config (which must be defined OUTSIDE a VirtualHost block):

# OCSP Stapling
 SSLUseStapling on
 SSLStaplingResponderTimeout 5
 SSLStaplingReturnResponderErrors off
 SSLStaplingCache "shmcb:logs/stapling_cache(128000)"

The first few lines are self explanatory, the last one needs some discussion. The OCSP stapling cache can be configured many ways, in this case I decided on a shared memory cyclic buffer with a reasonable size. There are other options defined in the docs, but this one is reasonably performant and doesn’t have a major cost or configuration impact.

Now comes the fun part, which is the per site configuration. Apache 2.4 supports Server Name Indication (or SNI), so you can run multiple SSL sites on a single IP address. This is also supported by most modern browsers and phones (the Wikipedia article linked as a very through list). Because I run multiple sites on a single IP, I didn’t want to keep the same huge block of configuration information in each site, so I break out the common bits to a file like this:

<IfModule mod_ssl.c>
 # SSL Engine Switch:
 # Enable/Disable SSL for this virtual host.
 SSLEngine on

 # SSL Engine Options:
 # Set various options for the SSL engine.
 # o FakeBasicAuth:
 # Translate the client X.509 into a Basic Authorisation. This means that
 # the standard Auth/DBMAuth methods can be used for access control. The
 # user name is the `one line' version of the client's X.509 certificate.
 # Note that no password is obtained from the user. Every entry in the user
 # file needs this password: `xxj31ZMTZzkVA'.
 # o ExportCertData:
 # This exports two additional environment variables: SSL_CLIENT_CERT and
 # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
 # server (always existing) and the client (only existing when client
 # authentication is used). This can be used to import the certificates
 # into CGI scripts.
 # o StdEnvVars:
 # This exports the standard SSL/TLS related `SSL_*' environment variables.
 # Per default this exportation is switched off for performance reasons,
 # because the extraction step is an expensive operation and is usually
 # useless for serving static content. So one usually enables the
 # exportation for CGI and SSI requests only.
 # o StrictRequire:
 # This denies access when "SSLRequireSSL" or "SSLRequire" applied even
 # under a "Satisfy any" situation, i.e. when it applies access is denied
 # and no other module can change it.
 # o OptRenegotiate:
 # This enables optimized SSL connection renegotiation handling when SSL
 # directives are used in per-directory context.
 #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
 <FilesMatch "\.(cgi|shtml|phtml|php)$">
 SSLOptions +StdEnvVars
 </FilesMatch>

 # SSL Protocol Adjustments:
 # The safe and default but still SSL/TLS standard compliant shutdown
 # approach is that mod_ssl sends the close notify alert but doesn't wait for
 # the close notify alert from client. When you need a different shutdown
 # approach you can use one of the following variables:
 # o ssl-unclean-shutdown:
 # This forces an unclean shutdown when the connection is closed, i.e. no
 # SSL close notify alert is send or allowed to received. This violates
 # the SSL/TLS standard but is needed for some brain-dead browsers. Use
 # this when you receive I/O errors because of the standard approach where
 # mod_ssl sends the close notify alert.
 # o ssl-accurate-shutdown:
 # This forces an accurate shutdown when the connection is closed, i.e. a
 # SSL close notify alert is send and mod_ssl waits for the close notify
 # alert of the client. This is 100% SSL/TLS standard compliant, but in
 # practice often causes hanging connections with brain-dead browsers. Use
 # this only for browsers where you know that their SSL implementation
 # works correctly.
 # Notice: Most problems of broken clients are also related to the HTTP
 # keep-alive facility, so you usually additionally want to disable
 # keep-alive for those clients, too. Use variable "nokeepalive" for this.
 # Similarly, one has to force some clients to use HTTP/1.0 to workaround
 # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
 # "force-response-1.0" for this.
 BrowserMatch "MSIE [2-6]" \
 nokeepalive ssl-unclean-shutdown \
 downgrade-1.0 force-response-1.0
 # MSIE 7 and newer should be able to use keepalive
 BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

 # Disable insecure SSL versions, and enable secure ones in an explicit fashion
 SSLProtocol -all -SSLv2 -SSLv3 +TLSv1 +TLSv1.1 +TLSv1.2
 # Use the cipher list provided below, not the built-in default
 SSLHonorCipherOrder on
 # SSL compression is bad for attack vectors
 SSLCompression off
 # Modified from Mozilla, only support 256bit AES
 SSLCipherSuite "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"

 # HSTS, so make sure you have the Header module enabled
 Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>

I’ve tried to be explicit as possible with comments from the Apache documentation as well as the other sources I’ve pulled from. The Mozilla reference is to their Server Side TLS settings recommendations. In order to get 100% on the SSL Labs test, you need to disable everything but the 256bit AES DHE and ECDHE ciphers. This is a explicit breakout of the ciphers referenced in Seth’s configuration. With this all in a single file, configuring a virtual host is pretty simple:

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
 ServerAdmin [email protected]
 ServerName example.com
 ServerAlias www.example.com

 DocumentRoot /home/https/example.com/www/wordpress
<Directory /home/https/example.com/www/wordpress>
 Options FollowSymLinks
 AllowOverride None
 Require all granted
 Order allow,deny
 allow from all
 <IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteBase /
   RewriteRule ^index\.php$ - [L]
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteRule . /index.php [L]
   RewriteRule ^/wp-content/uploads/.*\.php$ - [F,NC]
 </IfModule>
</Directory>
<Directory /home/https/example.com/www/wordpress/.git>
 Order deny,allow
 Deny from all
 Require all denied
</Directory>
ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9001/home/https/example.com/www/wordpress/$1

ErrorLog ${APACHE_LOG_DIR}/www.spherecube.io-error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/www.example.com-access.log combined

Include /etc/apache2/sites-available/common-ssl.inc
SSLCertificateFile /etc/ssl/certs/www.example.com.crt
SSLCertificateKeyFile /etc/ssl/private/www.example.com.key
SSLCACertificateFile /etc/ssl/certs/startcom-ca.pem
SSLCertificateChainFile /etc/ssl/certs/startcom-sub.class2.server.ca.pem


</VirtualHost>
</IfModule>

Yeah, I like verbose apache configs. The relevant lines are highlighted. You basically pull in the config defined above, and then define the SSL certificate and key on a per site basis. Could I have added the CA and chain files to the global config? Yes, but then I would only be able to support a single CA. This is still a tiny amount of configuration information compared to the whole mess in the previous section and provides some flexibility. That’s pretty much it, now go secure everything.

comments powered by Disqus