Installing and Updating SSL Certificates with Emergence

Updated reference

There is a newer version of this guide with a complete letsencrypt setup:

1. Generate SSL Certificate

If you do not already have a ssl certificate, you’ll need to purchase. To do so you’ll need to generate a certificate signing request (csr) using openssl. The following video has a tutorial on how to do so.

https://knowledge.geotrust.com/support/knowledge-base/index?page=content&id=AR876

This one-liner can be used to generate both a .key and .csr file at the same time:

openssl req -new -newkey rsa:2048 -nodes -keyout www.example.org.key -out www.example.org.csr

IMPORTANT: Do not use a passphrase when generating your private key

2. Add SSL Certificates to Server

Once you have the three ssl necessary files move them to:

/emergence/sites/SITENAME/ssl/
  • “www.example.com.crt”
  • “www.example.com.key”
  • “www.example.com.csr”

Ensure the permissions of these certs match the following:

-rw-r----- 1 root www-data 1679 Jul 20 20:34 www.example.com.crt
-rw-r----- 1 root www-data 1679 Jul 20 20:34 www.example.com.key
-rw-r----- 1 root www-data 1679 Jul 20 20:34 www.example.com.csr

3. Update config.json (optional)

NOTE: This step is only necessary if you’re operating a single site instance, ie mywebsite.com is the only public facing site in the kernel (excluding non public sites like staging sites). If there are multiple sites on the server skip to step 3.

The path to these certs needs to be added to config.json.

/emergence/config.json

{
     "user": "www-data",
     "group": "www-data",
     "server": {
         "host": "0.0.0.0",
         "sslKey": "/emergence/sites/SITENAME/ssl/www.example.com.key",
         "sslCert": "/emergence/sites/SITENAME/ssl/www.example.com.crt",
     }
     ...
}

4. Update site.json

Similar to config.json, you’ll need to add the ssl key and cert to site.json as well. Edit the following file to include the “ssl” parameter.

/emergence/sites/SITENAME/site.json

{
    "handle": "SITENAME",
    ...
    "ssl": {
         "certificate": "/emergence/sites/SITENAME/ssl/www.example.com.crt",
         "certificate_key": "/emergence/sites/SITENAME/ssl/www.example.com.key"
     }
 }

5. Append Intermediate Chain to .crt File

Next, you need to append the global chain to your .crt file. To do this:

  1. Find chain file for your providers
  1. Upload to server
  • Put file in ssl directory /emergence/sites/SITENAME/ssl/GeoTrust_Intermediate.txt
  1. Append file to .crt using the following command:

cat GeoTrust_Intermediate.txt >> www.example.com.crt

6. Restart Emergence and nginx

To restart emergence, ssl into the server and run:

restart emergence-kernel

To restart nginx, navigate to example.com:9083 (emergence hub) and stop then start “Web”

7. Verify Certificates are Installed

Hit your url and verify that your browser isn’t giving a warning and the lock is complete. Next, click the lock to get more information about the cert and verify it’s accurate. (The method of viewing this information may vary by browser)

8. Force SSL for Entire Site (optional)

If you’d like to force every page on your site to use https, add the follow configuration snippet at /php-config/Site.config.d/https.php

<?php

$onInitialized = Site::$onInitialized;

Site::$onInitialized = function () use ($onInitialized) {
    if (
        (array_key_exists('REQUEST_SCHEME', $_SERVER) && $_SERVER['REQUEST_SCHEME'] == 'http')
        || (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] != 'on')
    ) {
        Site::redirect('https://' . Site::getConfig('primary_hostname') . $_SERVER['REQUEST_URI']);
    }

    if (is_callable($onInitialized)) {
        call_user_func($onInitialized);
    }
};

Note: this code was recently switched to make use of $_SERVER['REQUEST_SCHEME'] to ensure that it does not interfere with either CLI requests or FPM requests that aren’t coming from nginx. Please tag @chris in this thread if you encounter any emergence environment that does not provide REQUEST_SCHEME consistently.

Troubleshooting

  1. If you run into problems restarting your ngnix (Web) do a full reboot using this tutorial: Recovering from service- and host-level failures

  2. If the emergence-kernel and nginx look fine but your sites still aren’t loading, check these two places for possible errors:

    /var/log/upstart/emergence-kernel.log
    /emergence/log/nginx

Also, as of this commit, the kernel supports a more advanced syntax for site-level SSL that let’s you utilize SNI to map different hostnames to specific certificates:

{
    "handle": "SITENAME",
    ...
    "ssl": {
        "hostnames": {
            "api.example.com": {
                "certificate": "/emergence/sites/SITENAME/ssl/api.example.com.crt",
                "certificate_key": "/emergence/sites/SITENAME/ssl/api.example.com.key"
            },
            "example.com": {
                "certificate": "/emergence/sites/SITENAME/ssl/example.com.crt",
                "certificate_key": "/emergence/sites/SITENAME/ssl/example.com.key"
            },
            "admin.example.com": {
                "certificate": "/emergence/sites/SITENAME/ssl/admin.example.com.crt",
                "certificate_key": "/emergence/sites/SITENAME/ssl/admin.example.com.key"
            }
        }
    }
}

@tyler I updated the top post to “wiki” mode, apply the correction there

Here’s a nice workflow I’ve been using lately to obtain certificates:

First, put the domain name in a variable so you don’t have to edit the commands you’ll paste:

export DOMAIN="example.org"

Then create "${DOMAIN}.csr-input" (I like to do this under /root as root to keep ownership of and access to these files locked down but accessible for admins):

[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[dn]
C = US
ST = Pennsylvania
L = Philadelphia
O = Example LLC
emailAddress = webmaster@example.org
CN = example.org

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = www.example.org

Once the form is in order, then just run:

openssl req -new -sha256 -nodes -out "${DOMAIN}.csr" -newkey rsa:2048 -keyout "${DOMAIN}.key" -config <( cat "${DOMAIN}.csr-input" )

And that’s it! You’ve got everything you need, now just submit "${DOMAIN}.csr" to your certificate authority and put the chain they send back into "${DOMAIN}.crt"