Easily secure Meteor with free, automatically issued SSL certificates from Let's Encrypt without needing Galaxy or Meteor Up.
Without some help, Meteor does not play nice with the Let's Encrypt Certbot. This is because Meteor manages the public
directory and the certbot
CLI cannot write ACME challenges to that directory and have it immediately readable by the challenge issuer. Thus it fails to prove domain name control. You can work around this by using the manual cert issuing workflow, copy and paste filenames and contents, and rebuild your Meteor app.
But that sucks. So with this package you can simply run letsencrypt certonly --webroot
and it just works.
Add the package to your Meteor project:
$ meteor add noland:lets-encrypt
Follow the Let's Encrypt Certbot installation instructions.
This package requires a settings file to specify where to find the challenge files the certbot
creates.
If you're not already using one, read about how to create and use a settings file.
Either of these keys will work, your preference:
{
"letsEncryptChallengesDir": "/etc/letsencrypt/challenges/",
"private": {
"letsEncryptChallengesDir": "/etc/letsencrypt/challenges/"
}
}
The path must match the --webroot
path you give to the Certbot.
Don't forget to actually use your settings file.
The package automatically responds to challenges from the Let's Encrypt validation server using challenge responses saved in the directory you specified above. You don't need to do anything more on the Meteor end of things.
To issue or renew an SSL certificate, use the Let's Encrypt cerbot
.
Specifically, you'll need the certonly --webroot
options. See the Certbot webroot documenation.
A typical command might look like.
sudo letsencrypt certonly \
--webroot --webroot-path /etc/letsencrypt/challenges/ \ # same as your settings file
--cert-path /etc/letsencrypt/live/mydomain.ca \
--domain mydomain.ca \
--domain subdomain.mydomain.ca
Try it without sudo
to get instructions on how to do that if that's your thing. You can also just run:
letsencrypt certonly --webroot
to use a GUI if you like pointy clicky.
Whatever you're using to serve your Meteor app might need to be restarted to use the new cert and key. I use PM2 and Redbird Reverse Proxy in production and this combo rocks. I serve multiple secured domains from the same VPS and same IP address using SNI with a Redbird config like so:
webProxy.register('mydomain.ca', 'http://localhost:7010', { // my Meteor is configured to listen on 7010
ssl: {
key: '/etc/letsencrypt/live/mydomain.ca/privkey.pem',
cert: '/etc/letsencrypt/live/mydomain.ca/fullchain.pem',
ca: '/etc/letsencrypt/live/mydomain.ca/fullchain.pem',
}
});
So to tell Redbird to use the new cert and key, I run:
sudo pm2 restart redbird-reverse-proxy
TL;DR Don't put private files in the letsEncryptChallengesDir
directory.
In theory, this packages introduces a somewhat obscure security risk because it serves files from outside the Meteor project root folder which is generally unexpected. The files it serves though are tightly controlled and their names are strictly sanitized with a whitelist of characters.
Any file immediately in the directory named in your Meteor settings under the key letsEncryptChallengesDir
or private.letsEncryptChallengesDir
will be served to the public. No deeper/nested files or parent folders will be served.
Let's Encrypt certs are only issued for 3 months. It would be fairly easy to completely automate the renewal process by making a cron job to check expiry date and re-running the above commands every 3 months.
PRs and co-maintainer welcome.