Super Easy SSL Certs for Mojolicious Apps
I’m assuming that by now you’ve probably heard of Let’s Encrypt. If you haven’t, they are a brand new Certificate Authority that issues SSL certificates for free via an automated system!
There has to be a catch right? Well kinda, but it’s a small one. The certificate is only valid for 90 days. They mention two reasons for this in a blog post: to encourage automation and to contain the damage of a compromised cert.
If you need to renew every 90 days, you don’t want to be doing that by hand right? By encouraging automation, they can effectively force you to investigate how to make security easier for yourself over the long term. You may have read the famous Ten Immutable Laws Of Security but the related Ten Immutable Laws of Security Administration tells us in Law #2 that
Security only works if the secure way also happens to be the easy way
Once you have automated your SSL cert generation then the easy way will be the standard way.
Also, the shorter the duration of the cert, the less time that a compromised cert can be used to wreak havoc. But once we all have automation, they can actually tighten that time eventually to even shorter times, which would further contain the damage of compromised certs.
So let that sink in: all you have to do to have a secure website for free is to setup the automation to issue and renew the certificate.
The ACME Workflow
Let’s Encrypt’s automated process for issuing certificates is a series of requests and responses made between your client, their server and your server. They have even requested that the process be standardized so that other CAs can use it too. They call it the Automatic Certificate Management Environment (ACME Protocol). The protocol is explained in slightly more visual form on their website, but I’ll summarize here too.
First you have to generate or have an existing key available that will represent you. This key is used to sign all your ACME messages and is proof of your identity for future actions (like renewing or revoking).
Then you tell the ACME server which domain(s) you would like to issue a certificate for. It responds telling you how it would like to challenge the server at that domain to prove that you own it. Once you prepare your server to handle the challenge, you tell the ACME server you are ready for it to begin. You then wait until you see the challenge attempted and then you ask the ACME server if it is satisfied.
Assuming it is, you finally can ask it to issue the final certificate. Once it is installed into your server, you can start accepting SSL encrypted traffic.
Rising to the Challenge
The hardest part of the certificate issuance process is proving that you are who you say you are. A certificate is tied to a domain name and therefore you have to prove that you control that domain name.
As outlined above, to test this the Let’s Encrypt system issues a challenge to your server. Your site must be able to serve a particular string from a particular url on your site. By design, the url isn’t likely to clash, but how do you make it appear?
In most cases it is difficult for an automated client to set up the response. A possible way is to write a file containing the response to the site’s static files path, but this assumes that the static file host is local and that your client has write permissions to it. After all the ACME client is a different process than the web server, probably running as a different user, possibly on a different host. Another way is to temporarily bring down the site to serve the response via a built-in server, but this assumes that the client can bring down the server, and even if it can you probably don’t want it to. None of these are truly viable in a production system.
In response to these difficulties, other ostensibly automated clients simply tell you what to serve and wait for you to manually set it up. This means we still are one major step away from complying with Security Administration Law #2: the challenge response still is manual and therefore it still isn’t easy.
The fundamental problem is that the client doesn’t really interact with the web application and cannot know how to seamlessly integrate with it. That is unless your ACME client is actually integrated into your web application.
Mojolicious, for example, has just such a mechanism.
The Mojolicious Command System
With Mojolicious, your application is more that just a web server, it can define commands that let you do all kinds of ancillary interaction. You can use the built-in eval to run ad-hoc commands against your application. You can build application specific commands that would have been external scripts in older days, like database deployment or administration using the same code that your application already defines.
Importantly for this case, commands can come as part of CPAN-released plugins. For example, the Minion system bundles several commands for monitoring job status and running worker processes. The minion architecture is used both in your application to defined and enqueue jobs but also provides commands for use in conjunction with your web app.
$ ./myapp minion worker # start a worker
$ ./myapp minion job # show the list of jobs and statuses
$ ./myapp minion job <job id> # show individual job details
Introducing Mojo::ACME
I have recently released Mojo::ACME which comes with such a plugin. Upon loading the plugin, the application is modified in two ways, first a route is added which handles the challenge requests, and second a command is added that acts as the ACME client. When the client is informed of the challenge by the ACME server, it starts its own tiny REST-like HTTP server. The primary web server then relays the challenge request to the client’s challenge-server. In that way it can know how to respond to the challenge without disrupting any service.
The primary server and the client don’t need to be on the same host, the client doesn’t need any permissions. And in case you were concerned, these internal messages are HMAC-signed in both directions to prevent tampering.
Automation is now a piece of cake.
Example
As mentioned before, the only modification you need to make is to include the plugin in your app. To demonstrate, here’s a simple “Hello World” app with the plugin added.
use Mojolicious::Lite;
plugin 'ACME';
get '/' => {text => 'Hello World'};
app->start;
You can also set options on the plugin but the defaults are sane for most cases.
Now that your application has the plugin loaded, its time to issue your certificate.
Start your application, ensuring it is available on port 80 (I use hypnotoad and nginx to reverse proxy to it).
Now unless you already have registered a key as your account, make one by running (with -t
to test against the test server):
$ ./myapp.pl acme account register
Writing account.key
Just remember to save this key in a safe place because it is how you will identify yourself to the service in the future, say for renewals. Now you can issue your cert by running (again use -t
to test, use -a <path>
to specify a different account key)
$ ./myapp.pl acme cert generate mydomain.com
Writing myapp.key
Writing myapp.crt
Now simply install your cert and restart/reload your server per server instructions.
Conclusion
Let’s Encrypt is a great step forward for small (and even not so small) website owners; we can finally have the security that we need for free. It does require automation but web frameworks can fill the gap and make automation easy. Mojo::ACME makes issuing and renewing certificates just that easy for Mojolicious apps.
Leave a comment