Sudo-less TLS with Let's Encrypt

`acme.sh` brings a straighforward setup.

I've been using Let's Encrypt since they opened to the public for every site I control.

I was not happy with Certbot because of the complexity in getting it to work without root access (which I really don't want to hand out to everything that asks for it, even if they are trustworthy). Out of the box, Certbox seemed easy and automatic. If you didn't want to use it in exactly that way... less so.

This is actually my 3rd attempt at writing a blog post of my Let's Encrypt setup, as I was never happy with the bulk I ended up with. Now that we have acme.sh, lets try again!


acme.sh is a shell script ACME protocol client. At nearly 6k lines of code, it isn't the most lightweight, but you can read enough of it to get what is going on. If you want the bare protocol, acme-tiny (which I believe was an inspiration for acme.sh) is the way to go at ~200 lines of Python. acme.sh gives you the whole package, however.

First, lets create a user/group pair, and put the others into it:

1
2
3
4
5
# Make our user (and group).
useradd -m -d /usr/local/letsencrypt --system letsencrypt

# Put Nginx into our user (so it can use the certificates).
usermod -aG letsencrypt www-data

We need to allow the letsencrypt user to reload the Nginx config whenever there is a certificate change, so add to /etc/sudoers via visudo:

letsencrypt ALL = NOPASSWD: /bin/systemctl reload nginx

Prepare an Nginx "params" file for the ACME challenge, which serves requests to /.well-known/acme-challenge from ~letsencrypt/httpdocs:

1
2
3
4
5
6
cat > /etc/nginx/letsencrypt_params <<EOF
location /.well-known/acme-challenge {
    allow all;
    root /usr/local/letsencrypt/httpdocs/;
}
EOF

Switch over to the user, and setup the directory structure:

1
2
3
4
5
6
7
su letsencrypt
cd

mkdir httpdocs     # For the ACME challenges.
mkdir tls          # For the final keys/certificates.

chmod -R g=rX,o= . # Allow group members (e.g. Nginx) to read, but nobody else.

Lets install the client, and set it up with our email:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Install the latest acme.sh.
# This creates ~/.acme.sh, which contains the shell script, keys, and the config
# for auto-renewal. It also modifies ~/.bashrc to find the command.
curl https://get.acme.sh | sh

# Activate the new changes.
source .bashrc

# Update our "account" (so we get emails about expiry, etc.):
# NOTE: Insert your own email in here:
acme.sh --updateaccount --accountemail "you+letsencrypt-$(hostname)@example.com"

For each domain you must first edit its config to respond to the challenge (the files are typically in /etc/nginx/sites-enabled/$DOMAIN):

server {

    listen 80;
    server_name example.com;

    # Respond to ACME challenges.
    include letsencrypt_params;

    ### NORMAL CONFIG HERE ###

}

A sudo systemctl reload nginx later to pick up the changes, and we can issue/install the certificates!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# NOTE: Edit these variables for your main/secondary domains.
# You can have as many domains in one certificate as you want.
DOMAIN=example.com
FLAGS="-d $DOMAIN -d www.$DOMAIN" # Add more if you want.

# Assert ownership of the domain(s), and get the first certificate.
acme.sh --issue $FLAGS -w ~/httpdocs

# Install the cert, and configure the CRON job.
acme.sh --install-cert $FLAGS \
    --key-file ~/tls/$DOMAIN.key \
    --fullchain-file ~/tls/$DOMAIN.pem \
    --reloadCmd 'sudo systemctl reload nginx'

Finally, configure the site to use the certificates (again in /etc/nginx/sites-enabled/$DOMAIN):

server {

    listen 80;
    server_name example.com;

    # Respond to ACME challenges.
    include letsencrypt_params;

    location / {
        # Redirect normal traffic to secure site.
        return 301 https://$host$request_uri;
    }

}

server {

    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /usr/local/letsencrypt/tls/example.com.pem;    
    ssl_certificate_key /usr/local/letsencrypt/tls/example.com.key;

    ### NORMAL CONFIG HERE ###

}

And that is it. You now have free TLS/SSL certificates for your site(s) that auto-renew!

Posted . Categories: .