-
-
Save Greelan/28a46a33140b65c9a045573ca460f044 to your computer and use it in GitHub Desktop.
# How to use acme.sh to set up Let's Encrypt, with the script being run | |
# mostly without root permissions | |
# See https://github.com/Neilpang/acme.sh for more | |
# These instructions use the domain "EXAMPLE.COM" as an example | |
# These instructions: | |
# - work on Ubuntu 18.04 and 20.04 with nginx | |
# - use CloudFlare DNS validation | |
# - set up a wildcard certificate for the "EXAMPLE.COM" domain | |
# - use a systemd service, rather than cron job, to renew the certificate | |
# When this is done, there will be an "acme" user that handles issuing, | |
# updating, and installing certificates. This user will have the following | |
# (fairly minimal) permissions: | |
# - Copy certificates and key to /etc/letsencrypt/EXAMPLE.COM | |
# - Reload your nginx server | |
# First things first - create a system user account and group for acme | |
sudo useradd -m -d /var/lib/acme -s /usr/sbin/nologin -r -U acme | |
sudo chmod 700 /var/lib/acme | |
# Create a directory for the acme account to save certs in | |
MYDOMAIN="EXAMPLE.COM" | |
sudo mkdir -m 710 /etc/letsencrypt/"$MYDOMAIN" | |
sudo chown acme.www-data /etc/letsencrypt/"$MYDOMAIN" | |
# Edit your sudoers file to allow the acme user to reload (not restart) nginx | |
sudo visudo | |
# Add the following line at the end: | |
acme ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx.service | |
# Now change to the "acme" user - you'll do most of the rest of this guide as them | |
sudo MYDOMAIN="$MYDOMAIN" -s -u acme bash | |
export HOME=/var/lib/acme | |
cd ~ | |
# Install acme.sh | |
git clone https://github.com/Neilpang/acme.sh.git | |
cd acme.sh | |
./acme.sh --install | |
# Export your CloudFlare API token and account ID so that acme.sh can use them | |
# See https://github.com/Neilpang/acme.sh/wiki/dnsapi for more about API tokens | |
# You can find your account ID in the URL of any page within the Cloudflare Dashboard | |
# after selecting the appropriate account | |
export CF_Token="[insert]" | |
export CF_Account_ID="[insert]" | |
# Create your certificate | |
# Note that the "force" flag is needed for the below commands as otherwise | |
# the acme.sh script complains about being run as sudo | |
cd ~ | |
.acme.sh/acme.sh --issue -d "$MYDOMAIN" -d *."$MYDOMAIN" --dns dns_cf --force | |
# If everything went well, install your certificate | |
.acme.sh/acme.sh --install-cert --domain "$MYDOMAIN" \ | |
--ca-file /etc/letsencrypt/"$MYDOMAIN"/chain.pem \ | |
--key-file /etc/letsencrypt/"$MYDOMAIN"/key.pem \ | |
--fullchain-file /etc/letsencrypt/"$MYDOMAIN"/fullchain.pem \ | |
--reloadcmd "sudo systemctl reload nginx.service" --force | |
# Disable default cron job as a systemd service will be created instead for renewal | |
.acme.sh/acme.sh --uninstall-cronjob --force | |
# Drop back to your own user | |
exit | |
# Now modify your nginx config to work with the new certs | |
sudo nano /etc/nginx/sites-enabled/"$MYDOMAIN" | |
# Example SSL config section | |
#server { | |
# ... | |
# ssl_certificate /etc/letsencrypt/EXAMPLE.COM/fullchain.pem; | |
# ssl_certificate_key /etc/letsencrypt/EXAMPLE.COM/key.pem; | |
# ssl_trusted_certificate /etc/letsencrypt/EXAMPLE.COM/chain.pem; | |
# include /etc/nginx/ssl/ssl_params; # Change to whatever SSL settings you use - see further below | |
# ... | |
#} | |
# acme.sh does not create its own suggested SSL settings for you to use with nginx, | |
# so you will need to create your own (if you haven't already) | |
# The following commands set up SSL parameters of a reasonable level of security - | |
# relax or harden as you see fit (eg to include OCSP stapling), or skip if you | |
# already have your own. See https://ssl-config.mozilla.org/ for suggestions | |
sudo mkdir /etc/nginx/ssl | |
sudo tee /etc/nginx/ssl/ssl_params >/dev/null <<EOF | |
ssl_session_cache shared:SSL:10m; | |
ssl_session_timeout 1d; | |
ssl_protocols TLSv1.2 TLSv1.3; | |
ssl_prefer_server_ciphers off; | |
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ | |
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ | |
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ | |
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; | |
ssl_ecdh_curve X25519:secp384r1; | |
ssl_session_tickets off; | |
ssl_dhparam /etc/nginx/ssl/dhparam.pem; | |
EOF | |
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 | |
# Now test nginx to see if everything is working | |
sudo nginx -t | |
# And reload if it worked | |
sudo systemctl reload nginx.service | |
# Create a systemd service and timer to renew the certificate and reload nginx | |
sudo tee /etc/systemd/system/acme_letsencrypt.service >/dev/null <<EOF | |
[Unit] | |
Description=Renew Let's Encrypt certificates using acme.sh | |
After=network-online.target | |
[Service] | |
Type=oneshot | |
User=acme | |
Group=acme | |
Environment="HOME=/var/lib/acme" | |
ExecStart=/var/lib/acme/.acme.sh/acme.sh --cron | |
SuccessExitStatus=0 2 | |
EOF | |
sudo tee /etc/systemd/system/acme_letsencrypt.timer >/dev/null <<EOF | |
[Unit] | |
Description=Daily renewal of Let's Encrypt's certificates | |
[Timer] | |
OnCalendar=daily | |
RandomizedDelaySec=1h | |
Persistent=true | |
[Install] | |
WantedBy=timers.target | |
EOF | |
# Now start and enable | |
sudo systemctl start acme_letsencrypt.timer | |
sudo systemctl enable acme_letsencrypt.timer | |
# Congrats, you have a Let's Encrypt wildcard certificate set up on your box | |
# and it is configured to automatically renew, all by running the acme.sh script mostly | |
# without root permissions (other than to reload nginx on renewal). |
Ah, I see I should have followed Neil's instructions more closely on this one. Will update the gist (and my installation!).
Gist has been updated, including to add the cert-file
parameter, which will enable OCSP stapling to be used. Thanks again for the input.
Excellent
Correction: should use ca-file
parameter for OCSP stapling, although given ssl_certificate
already uses fullchain.pem
it is probably not even necessary now to use ssl_trusted_certificate
. Gist updated to reflect modified parameter, as well as to update the SSL parameters to reflect that support for TLSv1.1 will be dropped by most browsers in March 2020.
Thank you! <3
Back after over 2 years because of a fresh install that I have done.
I see that things have changed because of the underlying changes that have happened in acme.sh
Couple of extra steps that I had to do as a result.
acme.sh now using ZeroSSL by default (rather than LetsEncrypt) ... so a step is needed to set-up the ZeroSSL environment.
The approach taken depends on whether or not the user has a ZeroSSL account.
See https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA
Also - now using CloudFlare but probably worth mentioning that if using something else (I use Dynu) then the opening steps are different and user should follow the appropriate sequence from https://github.com/acmesh-official/acme.sh/wiki/dnsapi
remembering to also change the "--issue" command to use the correct "--dns" setting.
Thanks for the feedback. So is this issue effectively what is the problem you found, ie you need a key file that is the combined domain cert and key? acme.sh doesn't appear to generate that in the first instance (ie, what is stores in
/var/lib/acme/.acme.sh/EXAMPLE.COM
is ca.cer, fullchain.cer, EXAMPLE.COM.cer, and EXAMPLE.COM.key), but is the effect of the modified--install-cert
command you ran that it does that work for you, ie concatenates the cert and key and installs them as a pem?