Kais DevBlog

More or less structred thoughts

Automatic Renewal of LetsEncrypt SSL Certificates with Structr
Posted in Structr by Kai on Jan 28, 2021

LetsEncrypt provides free SSL certificates which are valid for a 3-month period (after which you can create a new certificate). This is awesome! But scheduling to create a new certificate every 90 days is annoying.

Luckily Structr allows us to automate this...


The basic process is quite easy. Structr has a built-in maintenance command to order a LetsEncrypt certificate. It also has a task scheduler (think CRON). Now we only have to put those pieces together and do a little configuration.

Step 1: Configure the domain in the Structr settings in structr.conf

letsencrypt.domains = kai.news

Step 2: Create a global schema method renewSSLCertificate

{
  let config = {
      server: "production",
      challenge: "http",
wait: "10",
reload: true
};

  $.maintenance('letsencrypt', config);
}

This will order a new certificate for the configured domain and verify it with LetsEncrypt. reload: true tells Structr to reload the SSL certificate.

Step 3: Configure cron job for this global method in structr.conf

cronservice.tasks = renewSSLCertificate
renewSSLCertificate.cronExpression = 0 0 0 1 * *

This will run the global method at midnight on the first of every month. I would highly recommend using any other moment than midnight at the first of the month!

Step 4: Run the method once by clicking the "Run method" button and then the "Run" button. This works either in the "Code" area or in the "Dashboard".

If everything goes to plan and your instance is reachable at the domain you configured in step 1 you should see a success message in the server log in the "Dashboard"

Step 5: Now you can enable HTTPS by making the following settings in structr.conf

application.https.port = 443
application.https.enabled = true

Optionally you can also enable redirecting from HTTP to HTTPS.

application.https.enabled = true

Step 6: If you configured HTTPS for the first time you will need to restart structr. Once it has been started with HTTPS enabled, the reload: true parameter from step 1 will reload the renewed certificate automatically.

Step 7: The icing on the cake. Sometimes there is a connection issue with letsencrypt and the update process fails because of network instability. Then we get this output in the server log.

Unable to get certificate from Let's Encrypt: Network error
Unable to execute maintenance command letsencrypt: Unable to get certificate from Let's Encrypt: Network error

To be aware of this, I like to automatically send myself a mail with the last 20 lines of the log file after the renewal process. That way I can be sure everything works.

{
  let config = {
      server: "production",
      challenge: "http",
wait: "10",
reload: true
};

  $.maintenance('letsencrypt', config);

  let log   = $.serverlog(20);
  let subject  = 'SSL Certificate Updated';
  let html = '<p>Please check:</p>' + log.split('\n').join('<br />');
  let text = 'Please check:\n\n' + log;
    
  $.mailBegin('SENDER_ADDRESS', 'SENDER_NAME', subject, html, text);
  $.mailAddTo('RECIPIENT_ADDRESS', 'RECIPIENT_NAME');
  $.mailSend();
}

If you do not have access to a mailserver on the instance you can simply use your own account (Google Mail in my case). You can either add this as a global configuration setting in structr.conf or you can configure this in the above script after mailBegin and before mailSend like so:

// read password from structr.conf
let accountPassword = $.config('my.mail.password');

// configure mail server for this mail only
let senderAddress = 'YOUR_GMAIL_ACCOUNT@googlemail.com';
$.mailSetManualConfig('smtp.gmail.com', 587, senderAddress, accountPassword, true, true);

With this we have ensured that our SSL certificate is always up to date.