2015-11-25 21:44:00 -08:00
|
|
|
# acme-tiny
|
2015-11-25 22:48:17 -08:00
|
|
|
|
2015-12-05 18:11:29 -08:00
|
|
|
[](https://travis-ci.org/diafygi/acme-tiny)
|
2015-12-06 02:36:32 -08:00
|
|
|
[](https://coveralls.io/github/diafygi/acme-tiny?branch=master)
|
2015-12-05 18:11:29 -08:00
|
|
|
|
2015-11-25 22:48:17 -08:00
|
|
|
This is a tiny, auditable script that you can throw on your server to issue
|
|
|
|
|
and renew [Let's Encrypt](https://letsencrypt.org/) certificates. Since it has
|
|
|
|
|
to be run on your server and have access to your private Let's Encrypt account
|
2015-11-25 22:54:57 -08:00
|
|
|
key, I tried to make it as tiny as possible (currently less than 200 lines).
|
2015-11-25 22:48:17 -08:00
|
|
|
The only prerequisites are python and openssl.
|
|
|
|
|
|
2016-04-21 09:05:48 +02:00
|
|
|
**PLEASE READ THE SOURCE CODE! YOU MUST TRUST IT WITH YOUR PRIVATE ACCOUNT KEY!**
|
2015-11-25 22:48:17 -08:00
|
|
|
|
2017-08-24 00:42:40 +02:00
|
|
|
## Donate
|
2015-11-25 22:48:17 -08:00
|
|
|
|
|
|
|
|
If this script is useful to you, please donate to the EFF. I don't work there,
|
|
|
|
|
but they do fantastic work.
|
|
|
|
|
|
|
|
|
|
[https://eff.org/donate/](https://eff.org/donate/)
|
|
|
|
|
|
|
|
|
|
## How to use this script
|
|
|
|
|
|
|
|
|
|
If you already have a Let's Encrypt issued certificate and just want to renew,
|
|
|
|
|
you should only have to do Steps 3 and 6.
|
|
|
|
|
|
|
|
|
|
### Step 1: Create a Let's Encrypt account private key (if you haven't already)
|
|
|
|
|
|
|
|
|
|
You must have a public key registered with Let's Encrypt and sign your requests
|
|
|
|
|
with the corresponding private key. If you don't understand what I just said,
|
2015-11-25 22:54:57 -08:00
|
|
|
this script likely isn't for you! Please use the official Let's Encrypt
|
|
|
|
|
[client](https://github.com/letsencrypt/letsencrypt).
|
2015-12-19 15:10:13 +01:00
|
|
|
To accomplish this you need to initially create a key, that can be used by
|
2018-03-17 16:56:34 -07:00
|
|
|
acme-tiny, to register an account for you and sign all following requests.
|
2015-11-25 22:48:17 -08:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
openssl genrsa 4096 > account.key
|
|
|
|
|
```
|
|
|
|
|
|
2015-12-07 14:00:33 +01:00
|
|
|
#### Use existing Let's Encrypt key
|
|
|
|
|
|
|
|
|
|
Alternatively you can convert your key, previously generated by the original
|
|
|
|
|
Let's Encrypt client.
|
|
|
|
|
|
|
|
|
|
The private account key from the Let's Encrypt client is saved in the
|
|
|
|
|
[JWK](https://tools.ietf.org/html/rfc7517) format. `acme-tiny` is using the PEM
|
|
|
|
|
key format. To convert the key, you can use the tool
|
2015-12-22 08:17:03 -08:00
|
|
|
[conversion script](https://gist.github.com/JonLundy/f25c99ee0770e19dc595) by JonLundy:
|
2015-12-07 14:00:33 +01:00
|
|
|
|
2015-12-22 08:17:03 -08:00
|
|
|
```sh
|
|
|
|
|
# Download the script
|
|
|
|
|
wget -O - "https://gist.githubusercontent.com/JonLundy/f25c99ee0770e19dc595/raw/6035c1c8938fae85810de6aad1ecf6e2db663e26/conv.py" > conv.py
|
|
|
|
|
|
|
|
|
|
# Copy your private key to your working directory
|
|
|
|
|
cp /etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/<id>/private_key.json private_key.json
|
|
|
|
|
|
|
|
|
|
# Create a DER encoded private key
|
2019-12-10 07:01:02 -06:00
|
|
|
openssl asn1parse -noout -out private_key.der -genconf <(python2 conv.py private_key.json)
|
2015-12-22 08:17:03 -08:00
|
|
|
|
|
|
|
|
# Convert to PEM
|
|
|
|
|
openssl rsa -in private_key.der -inform der > account.key
|
|
|
|
|
```
|
2015-12-07 14:00:33 +01:00
|
|
|
|
2015-11-25 22:48:17 -08:00
|
|
|
### Step 2: Create a certificate signing request (CSR) for your domains.
|
|
|
|
|
|
|
|
|
|
The ACME protocol (what Let's Encrypt uses) requires a CSR file to be submitted
|
|
|
|
|
to it, even for renewals. You can use the same CSR for multiple renewals. NOTE:
|
|
|
|
|
you can't use your account private key as your domain private key!
|
|
|
|
|
|
|
|
|
|
```
|
2017-08-24 00:42:40 +02:00
|
|
|
# Generate a domain private key (if you haven't already)
|
2015-11-25 22:48:17 -08:00
|
|
|
openssl genrsa 4096 > domain.key
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
2017-08-24 00:42:40 +02:00
|
|
|
# For a single domain
|
2015-11-25 22:48:17 -08:00
|
|
|
openssl req -new -sha256 -key domain.key -subj "/CN=yoursite.com" > domain.csr
|
|
|
|
|
|
2017-08-24 00:42:40 +02:00
|
|
|
# For multiple domains (use this one if you want both www.yoursite.com and yoursite.com)
|
2019-08-08 14:15:52 -05:00
|
|
|
openssl req -new -sha256 -key domain.key -subj "/" -addext "subjectAltName = DNS:yoursite.com, DNS:www.yoursite.com" > domain.csr
|
|
|
|
|
|
2019-12-10 06:50:56 -06:00
|
|
|
# For multiple domains (same as above but works with openssl < 1.1.1)
|
2015-11-25 22:48:17 -08:00
|
|
|
openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yoursite.com,DNS:www.yoursite.com")) > domain.csr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Step 3: Make your website host challenge files
|
|
|
|
|
|
|
|
|
|
You must prove you own the domains you want a certificate for, so Let's Encrypt
|
|
|
|
|
requires you host some files on them. This script will generate and write those
|
2015-12-04 23:03:03 +01:00
|
|
|
files in the folder you specify, so all you need to do is make sure that this
|
2016-03-16 01:49:21 -05:00
|
|
|
folder is served under the ".well-known/acme-challenge/" url path. NOTE: Let's
|
|
|
|
|
Encrypt will perform a plain HTTP request to port 80 on your server, so you
|
|
|
|
|
must serve the challenge files via HTTP (a redirect to HTTPS is fine too).
|
2015-11-25 22:48:17 -08:00
|
|
|
|
|
|
|
|
```
|
2017-08-24 00:42:40 +02:00
|
|
|
# Make some challenge folder (modify to suit your needs)
|
2015-11-25 22:48:17 -08:00
|
|
|
mkdir -p /var/www/challenges/
|
2015-12-05 15:41:15 +01:00
|
|
|
```
|
2015-11-25 22:48:17 -08:00
|
|
|
|
2015-12-05 15:41:15 +01:00
|
|
|
```nginx
|
2017-08-24 00:42:40 +02:00
|
|
|
# Example for nginx
|
2015-11-25 22:48:17 -08:00
|
|
|
server {
|
|
|
|
|
listen 80;
|
2015-12-09 07:14:00 +00:00
|
|
|
server_name yoursite.com www.yoursite.com;
|
2015-11-25 22:48:17 -08:00
|
|
|
|
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
|
|
|
alias /var/www/challenges/;
|
|
|
|
|
try_files $uri =404;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
...the rest of your config
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Step 4: Get a signed certificate!
|
|
|
|
|
|
|
|
|
|
Now that you have setup your server and generated all the needed files, run this
|
|
|
|
|
script on your server with the permissions needed to write to the above folder
|
|
|
|
|
and read your private account key and CSR.
|
|
|
|
|
|
|
|
|
|
```
|
2017-08-24 00:42:40 +02:00
|
|
|
# Run the script on your server
|
2018-03-17 12:10:07 -07:00
|
|
|
python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed_chain.crt
|
2015-11-25 22:48:17 -08:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Step 5: Install the certificate
|
|
|
|
|
|
2018-03-17 12:10:07 -07:00
|
|
|
The signed https certificate chain that is output by this script can be used along
|
2015-11-25 22:48:17 -08:00
|
|
|
with your private key to run an https server. You need to include them in the
|
|
|
|
|
https settings in your web server's configuration. Here's an example on how to
|
|
|
|
|
configure an nginx server:
|
|
|
|
|
|
|
|
|
|
```nginx
|
|
|
|
|
server {
|
2017-12-07 11:15:46 -06:00
|
|
|
listen 443 ssl;
|
2019-11-10 23:19:23 +01:00
|
|
|
server_name yoursite.com www.yoursite.com;
|
2015-11-25 22:48:17 -08:00
|
|
|
|
2018-03-17 12:10:07 -07:00
|
|
|
ssl_certificate /path/to/signed_chain.crt;
|
2015-11-25 22:48:17 -08:00
|
|
|
ssl_certificate_key /path/to/domain.key;
|
|
|
|
|
ssl_session_timeout 5m;
|
2021-08-12 21:55:26 -05:00
|
|
|
ssl_protocols TLSv1.2;
|
|
|
|
|
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
2015-11-25 22:48:17 -08:00
|
|
|
ssl_session_cache shared:SSL:50m;
|
|
|
|
|
ssl_dhparam /path/to/server.dhparam;
|
|
|
|
|
ssl_prefer_server_ciphers on;
|
|
|
|
|
|
|
|
|
|
...the rest of your config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server {
|
|
|
|
|
listen 80;
|
2019-11-10 23:19:23 +01:00
|
|
|
server_name yoursite.com www.yoursite.com;
|
2015-11-25 22:48:17 -08:00
|
|
|
|
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
|
|
|
alias /var/www/challenges/;
|
|
|
|
|
try_files $uri =404;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
...the rest of your config
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Step 6: Setup an auto-renew cronjob
|
|
|
|
|
|
|
|
|
|
Congrats! Your website is now using https! Unfortunately, Let's Encrypt
|
|
|
|
|
certificates only last for 90 days, so you need to renew them often. No worries!
|
|
|
|
|
It's automated! Just make a bash script and add it to your crontab (see below
|
|
|
|
|
for example script).
|
|
|
|
|
|
|
|
|
|
Example of a `renew_cert.sh`:
|
|
|
|
|
```sh
|
|
|
|
|
#!/usr/bin/sh
|
2019-07-06 20:47:11 -05:00
|
|
|
python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /path/to/signed_chain.crt.tmp || exit
|
|
|
|
|
mv /path/to/signed_chain.crt.tmp /path/to/signed_chain.crt
|
2015-11-25 22:48:17 -08:00
|
|
|
service nginx reload
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
2017-08-24 00:42:40 +02:00
|
|
|
# Example line in your crontab (runs once per month)
|
2015-11-25 22:48:17 -08:00
|
|
|
0 0 1 * * /path/to/renew_cert.sh 2>> /var/log/acme_tiny.log
|
|
|
|
|
```
|
|
|
|
|
|
2018-03-17 12:10:07 -07:00
|
|
|
NOTE: Since Let's Encrypt's ACME v2 release (acme-tiny 4.0.0+), the intermediate
|
|
|
|
|
certificate is included in the issued certificate download, so you no longer have
|
|
|
|
|
to independently download the intermediate certificate and concatenate it to your
|
|
|
|
|
signed certificate. If you have an bash script using acme-tiny <4.0 (e.g. before
|
|
|
|
|
2018-03-17) with acme-tiny 4.0.0+, then you may be adding the intermediate
|
|
|
|
|
certificate to your signed_chain.crt twice (not a big deal, it should still work fine,
|
|
|
|
|
but just makes the certificate slightly larger than it needs to be). To fix,
|
|
|
|
|
simply remove the bash code where you're downloading the intermediate and adding
|
|
|
|
|
it to the acme-tiny certificate output.
|
|
|
|
|
|
2015-11-25 22:48:17 -08:00
|
|
|
## Permissions
|
|
|
|
|
|
|
|
|
|
The biggest problem you'll likely come across while setting up and running this
|
|
|
|
|
script is permissions. You want to limit access to your account private key and
|
|
|
|
|
challenge web folder as much as possible. I'd recommend creating a user
|
|
|
|
|
specifically for handling this script, the account private key, and the
|
|
|
|
|
challenge folder. Then add the ability for that user to write to your installed
|
2018-03-17 12:10:07 -07:00
|
|
|
certificate file (e.g. `/path/to/signed_chain.crt`) and reload your webserver. That
|
2015-12-04 20:44:45 +01:00
|
|
|
way, the cron script will do its thing, overwrite your old certificate, and
|
2015-11-25 22:48:17 -08:00
|
|
|
reload your webserver without having permission to do anything else.
|
|
|
|
|
|
|
|
|
|
**BE SURE TO:**
|
|
|
|
|
* Backup your account private key (e.g. `account.key`)
|
|
|
|
|
* Don't allow this script to be able to read your domain private key!
|
|
|
|
|
* Don't allow this script to be run as root!
|
|
|
|
|
|
|
|
|
|
## Feedback/Contributing
|
|
|
|
|
|
|
|
|
|
This project has a very, very limited scope and codebase. I'm happy to receive
|
|
|
|
|
bug reports and pull requests, but please don't add any new features. This
|
|
|
|
|
script must stay under 200 lines of code to ensure it can be easily audited by
|
|
|
|
|
anyone who wants to run it.
|
|
|
|
|
|
|
|
|
|
If you want to add features for your own setup to make things easier for you,
|
|
|
|
|
please do! It's open source, so feel free to fork it and modify as necessary.
|