Friday, May 16, 2014

Easy When You Know How: Setting up SNI, Virtual Hosts, and Secure Socket Layer (SSL)

There must be a zillion links to someone discussing one or another of these topics. And pages and pages of Apache documentation. And Google fodder (stuff you can look up) galore.

So, what: yet another howto site?

Well no. 

I hate when people start out with "All you have to do is..." Because inevitably there is always a lot more to "do". That is because people are in a hurry, hurtling, and ASS-U-ME that others know what they know. Or they're out to make a quick buck and really don't care what the content of the blog is as long as people click on the ads.

The other thing I hate is Linux man pages (and many subsequent sites) because they never give an example.

Which is not the case here.

"All you have to do is":
1. Meet the Prerequisites
2. Set up Virtual Hosts
3. Set up the /etc/apache2/listen.conf
4. Configure the hosts
5. Import your organization's root certificate into your browser
6. Restart apache
7. Optional: Set up Local Hosts

Gosh, is that all there is to it? Piece of cake...

My intent here is to pull it all together (with ample Google fodder) to let a relatively knowledgeable newbie get from a blank sheet to an operating SSL-protected web presence in a single shot.

("Relatively knowledgeable" is here defined as a computer geek who just hasn't met SSL before, but has done HTML and programming and understands Linux, LAMP, and BASH. Otherwise, stop and go home before you hurt yourself.)

I have a server in Tulsa, Oklahoma (I'm in Rhode Island) that I'm using as a sandbox to refine my geek skills. I've got Drupal and a host of other CMS in addition to ages of straight-stick HTML experience, but haven't (until now) actually built something with SSL. So reasonably, I thought I'd first set it up in a sandbox on the localhost machine. Easy, right?

Well, no. At least not until you know how. We now know how, but that may last for only a while...

(Stuff changes. Suck it up... :-(

I'll offer up Google fodder in that format for stuff beyond the scope of this article, but have fun) and point out some real gotchas along the way:

First: understand the difference between versions of Apache
  • Sounds like a "duh", but when you go googling for stuff you are often served stuff based on version 2.2, while the current (May 2014) version is 2.4.x. 
    • Necessarily, a lot of stuff has changed: default directories, default filenames, NamedVirtualHost directives, parsing of httpd.conf into subordinate directories with Include or IncludeOptional directives, and so forth.
    • For example, many references talk about the "<VirtualHost> block" in httpd.conf. Well, on my system (openSUSE 13.1), there is no such thing. Instead there is /etc/apache2/vhost.d/ containing any number of desired .conf files, each constituting (and instantiating) a different VirtualHost.
    • Additionally NamedVirtualHost directives have been deprecated in Apache 2.4 and can raise errors from listen.conf.
     So make sure you are using references to the current version of your server.

Second: in a similar vein, recognize the differences between operating system distributions
  • openSUSE 13.1 puts stuff in the following tree:
                # where xxx = key, csr, crt, crl, prm

     YMMV with other distributions, e.g., Ubuntu.

Third: Understand the difference between Server and ServerName:
  • The former refers to the machine on which the website is mounted. In my case it is localhost. In other cases it may be 123.235.345.456 or some other IP address.
  • The latter refers to the CommonName (CN) of the website, e.g.,

    These conflicts are replete throughout the setup.
Fourth: Reeely understand certificates:
  • ca.crt is the PEM-encoded certificate for the Certiicate Authority (CA) who certifies (signs) your server and website certificates. The CA for a self-signed certificate is yourself in whatever fashion you wish to represent yourself. Or the CA can be one of a zillion commercial certification activities, e.g., Thawte, etc. The point is that the CA certificate must be cited in your browser Root Certificate cache to prevent your browser raising an error when you try to go to one of your SSL-protected sites.
  • <vhost>.key is the PEM-encoded encryption key to go with the associated certificate, whether for the CA, the server itself, or the various VirtualHosts.
  • <vhost>.csr is the PEM-encoded X.509 Certificate Signing Request (CSR) for each of the client entities (server, <vhost>). There is an openssl command command that uses the CSR and the ca.crt to create a signed certificate for the client (server, <vhost>)
  • <vhost>.crt is the resulting PEM-encoded X.509 certificate for the client.
  • Most importantly, the CA CN cited in the client .crt must match the CA CN in the corresponding ca.crt and the keys must match as well.
  • There are additional tricks, like Certificate Revocation Lists (CRL) and public DSA Parameter Files (PRM) that I haven't touched. Apparently, I do not need to, TBTG... :-)
  • There is a very good and comprehensively specific protocol to follow for creating your own ca.crt and the consequential client (server or <vhost>) .key.csr, and .crt files here.
Fifth: Tip your hat to SNI (Server Name Indication) . The details don't really matter since it's all automagic, at least in Apache 2.4, but you need to know the name of the horse you are riding:
  • SNI (Server Name Indication) is an extension to SSL that allows multiple SSL-enabled Web sites to be served from a single IP address and port (443).
  • The server relies on the client to report the hostname as part of the HTTP headers with name-based virtual hosting. Many different hosts can share the same IP address using this technique.
Sixth: Back into the "relatively knowledgeable newbie" domain: you do understand IP addresses, FQDN, ports, domains, and all that, don't you...

So, with all that foregoing and foresaid, let's get started:
You must create a block (in httpd.conf or the Included /etc/apache2/vhost.d/<vhost>.conf) for the existing host if you are adding virtual hosts to an existing web server. Otherwise you will block access to the existing non-virtual host.

The DocumentRoot included in the .conf MUST  be the same as the global DocumentRoot. Note: the same:
So if you have loaded all your websites on localhost at /srv/www/htdocs then use
     DocumentRoot /srv/www/htdocs
List this virtual host first in the configuration file so that it will act as the default host.
These are defined in httpd.conf. Except that actually, the line in that file is:
IncludeOptional /etc/apache2/vhosts.d/*.conf
So if you create a conf file in /etc/apache2/vhosts.d/ it is automatically included when the server is restarted.
We'll set up two sites with SSL: and Both sites will be served by the same IP address. We'll use separate SSL certificates for each site.

1. Meet the Prerequisites

     Enable SSL and Create Certificates.

  • There is a very good and comprehensively specific protocol to follow for creating your own ca.crt and the consequential client (server or <vhost>) .key.csr, and .crt files here.
2. Set up Virtual Hosts
     Create directories for the hosts (the basic server is implicitly at 
          # mkdir /srv/www/htdocs/
          # mkdir /srv/www/htdocs/

     Use your favorite HTML editor to create an index page, e.g., index.html

     Set their ownership and permissions
          # chown -R wwwrun:www /srv/www/htdocs/
          # chown -R wwwrun:www /srv/www/htdocs/

3. Set up the /etc/apache2/listen.conf:
     Change listen 80 to listen *:80
     Change listen 80 to listen *:80

               Apache may need to be told to listen on specific ports, or only on selected addresses, or a combination of both. This is often combined with the Virtual Host feature.
               The Listen directive tells the server to accept incoming requests only on the specified ports or address-and-port combinations.
               Multiple Listen directives may be used to specify a number of addresses and ports to listen on.
               The Listen directive does not implement Virtual Hosts - it only tells the main server what addresses and ports to listen on.

4. Configure the hosts
     a. The hosts are configured in
           which presently contains
               vhost-ssl.template vhost.template
     b. Copy vhost.template for a generic non-SSL host localhost.conf
          This is necessary because creating other virtual hosts will block normal http access to localhost. It will still work if you append a different port, e.g., 8080, 9000 (appending :80 does not help):
          # cp vhost.template ./localhost.conf
                         Main host goes away
                         Any request that doesn't match an existing  is handled by the global server configuration, regardless of the hostname or ServerName.
     c. Copy vhost-ssl.template for each of the intended SSL hosts <vhost-ssl>.conf, renaming them accordingly (these do not affect localhost:80)
     # cp vhost-ssl.template ./privustech-ssl.conf     # cp vhost-ssl.template ./privuscloud-ssl.conf
     The name does not need to match the CN as the CN is specified within the file.

     d. Edit each virtual host's <vhost-ssl>.conf. E.g.:
            ServerAlias *
            DocumentRoot "/srv/www/htdocs/" 
                #This is the parent of the site, not the site itself
               #/srv/www/htdocs/ returns a 404
            ScriptAlias /cgi-bin/ /srv/www/cgi-bin/         
     Change the server key and certificate to those for the server, not the website:
            SSLEngine On     SSLCertificateFile /etc/apache2/ssl.crt/server.crt 
               #This is the server not the website
            SSLCertificateKeyFile /etc/apache2/ssl.key/server.key
              #This is the server not the website
            SSLCACertificatePath /etc/apache2/ssl.crt     
            SSLCARevocationPath /etc/apache2/ssl.crl 

5. Import your organization's root certificate into your browser
     The browser has a cache of "root certificates", certificates from Certificate Authorities that it has been taught to trust. It will not trust a self-signed certificate if the CN of the CA cited in the certificate is not included in its "Root Certificates" cache.
Use Chrome → Settings → Show advanced settings → HTTPS/SSL → Manage certificates → Authorities → Import →
This will establish (or whatever CN you used in the ca.crt) as a "trusted" root authority

6. Restart apache
     service apache2 restart

7. Optional: Set up Local Hosts
     Edit the /etc/hosts

          It is in the form:
               IP-Address Full-Qualified-Hostname Short-Hostname
          and already has the entries
      tm2t tm2t
               plus a bunch of ip-v6 addresses.
     The discussion suggests adding an IP address for the various virtual hosts, but we leave this alone as we have already connected to

Hope this helps. Comments welcome, and we'll revisit to retweak as necessary.
But for now, it just works.
So, how long will all this take? About twice as long as it took you to read through this and understand it. 
But you had to have read every sentence and understood it. Nevermind the googling...

This has taken me a week to compose, and even after having composed and published it easily half a day more to debug and edit. And then, when published, easily a couple of hours to read through and reflect on the instructions, comments, and implied links.

So if you are a guru, maybe about three hours. If not, then tomorrow is another day.

There's no such thing as a free lunch. You can have it big, or you can have it quick, but you can't have it big and quick.
Except it's easier when you know how (and have kept a log...)

No comments: