# Why use acme.sh instead of certbot? certbot does not behave nicely with anything non webserver, anything that does not use root permissions to load certificates. E.g. mumble-server, etc. This is because certbot certificates and keys were stored with perms root:root, in `/etc/letsencrypt/live/`. The seemingly only way to install certificates elsewhere with different permissions was a custom deployment hook script... acme.sh does not make any assumptions about location, and while it has *modes* to verify in nginx or apache2, the **most standard** way of verifying is using a webroot specific to `.well-known/acme-challenge`. Finally, because this is not a fancy all-in-one tool, and it makes the process of acme understandable to the system administrator, without modifying files without consent, it allows the admin to set it up and understand why it breaks if it does. This means: **less outages, because things work!** ## TL;DR, how do I use it? ### Setup steps ```bash apt install ssl-cert acme.sh # Register your account on the CA of your choice acme.sh --set-default-ca --server letsencrypt # Setup cronjob acme.sh --install-cronjob # Create ACME webroot mkdir /var/www/le_root # Create TLS cert directory mkdir /etc/ssl/acme # Create nginx snippet cat > /etc/nginx/snippets/tls-wellknown.conf < include snippets/tls-wellknown.conf;" ``` ### Obtain a certificate ```bash dom=example.org webroot="/var/www/le_root" acme.sh --issue -w "$webroot" -d "$dom" ``` More than 1 `-d` in the command -> the cert will have multiple (secondary) domains (SAN). ### Install a certificate ```bash dom=example.org echo "Installing \"$dom\"..." domdir="/etc/ssl/acme/$dom" mkdir -pv "$domdir" acme.sh --install-cert -d $dom \ --cert-file "$domdir/cert.pem" \ --key-file "$domdir/key.pem" \ --fullchain-file "$domdir/fullchain.pem" \ --reloadcmd "systemctl reload nginx" ``` nginx, as well as many other services support loading certs with `root:root 600` permissions. If yours does not, feel free to modify the permissions (ownership, mode), the renewal will not change these. # How to use acme.sh properly? ## Configure CA Configure the the CA you wish you use: ```bash acme.sh --set-default-ca --server letsencrypt ``` Note that **the default is ZeroSSL**, which requires an email: ```bash # ZeroSSL requires email - LE does not acme.sh --register-account -m MyEmail@example.org ``` ## Configure your webserver ACME verification is done through HTTP(S), as such, there should always be a default HTTP and HTTPS site w/ `.well-known/acme-challenge/` setup. All sites which are enabled should also have this snippet, otherwise, they cannot get a renewal on their TLS certificate. No matter which web server you are using, you must have a common web root for the acme-challenges. In my case, I use `/var/www/le_root`. As such, you must create the directory: ```bash mkdir /var/www/le_root ``` ### nginx For nginx, I recommend setting up `/etc/nginx/snippets/tls-wellknown.conf`: ```nginx location /.well-known/acme-challenge/ { alias /var/www/le_root/.well-known/acme-challenge/; } ``` Now, in every site you configure, add this **include** snippet. For example: ```nginx server { listen 443 ssl default_server; listen [::]:443 ssl default_server; include snippets/snakeoil.conf; include snippets/tls-wellknown.conf; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } } ``` Don't forget to ***reload nginx afterwards***! ### apache2 I'm not as well-versed with apache2, but the core idea is similar. Create a `/etc/apache2/conf-available/tls-wellknown.conf`: ```apache2 Alias /.well-known/acme-challenge/ /var/www/le_root/.well-known/acme-challenge/ Options None AllowOverride None DirectoryIndex disabled Require all granted ```` This now needs to be enabled by running: ```bash a2enconf tls-wellknown ``` A massive difference with nginx, is that you must also configure your sites correctly. Following are a few examples of misconfiguration. Avoid `RedirectMatch` and merely use a `Redirect` for 80 -> 443 redirect; note that the important part here is that the redirection to `.well-known/acme-challenge` must be there, and the request **cannot** be redirected to `/` on HTTPS. If you use `ScriptAlias / (...)`, you must include the snippet before it, otherwise it will automatically match on the script, and forward it to FCGI or other. Add `Include conf-enabled/tls-wellknown.conf` before the `ScriptAlias`. If you use `ProxyPass / (...)`, you must exclude the directory before it, otherwise it will ProxyPass the acme-challenge. Add `ProxyPass /.well-known/acme-challenge/ !` before the `ProxyPass`. Don't forget to reload apache2 afterwards! ## Issue a certificate Unlike certbot's default `run` command, certificate issuance and installation are **separate steps**! Issuing a certificate is merely proving you own the domain by ACME challenge, and getting a certificate. To issue a certificate, we must issue the following command: ```bash dom=example.org webroot="/var/www/le_root" acme.sh --issue -w "$webroot" -d "$dom" ``` If for some reason, you need multiple domains in a single certificate (e.g. mail server w/ mail. imap. smtp.example.org), do the following: ```bash dom=example.org webroot="/var/www/le_root" acme.sh --issue -w "$webroot" \ -d "mail.$dom" -d "imap.$dom" -d "smtp.$dom" ``` ## Install a certificate Now that you have an issued certificate, you must install it with acme.sh before you can use it in the desired service. The installation process works by **copying the files** to a location of your choice and issuing a custom **reload command**. This is better than certbot because the copying doesn't overwrite any manually set permissions (mode, ownership), as such they'll be kept across renewals. The reload command allows you to specify custom commands, for custom services, such as mumble, gemini, or others. Here's an example for a certificate used *only* by nginx: ```bash dom=example.org echo "Installing \"$dom\"..." domdir="/etc/ssl/acme/$dom" mkdir -pv "$domdir" acme.sh --install-cert -d $dom \ --cert-file "$domdir/cert.pem" \ --key-file "$domdir/key.pem" \ --fullchain-file "$domdir/fullchain.pem" \ --reloadcmd "systemctl reload nginx" ``` If the certificate is used by multiple services, simply chain it: ```bash --reloadcmd "systemctl reload nginx && systemctl reload mumble-server" ``` This also works fine with init.d or simply sending kill signals, as this is a free-field command. ## How do renewals work? acme.sh itself has a cronjob which runs every day and renews certificates when needed. Set it up with: ```bash acme.sh --install-cronjob ``` ## Appendix: bulk install When moving away from something like cerbot, it can be useful to bulk-install certificates after having issued them all with acme.sh: ```bash doms=$(acme.sh --list | cut -d" " -f1 | tail -n +2) for dom in $doms; do echo "Installing \"$dom\"..." domdir="/etc/ssl/acme/$dom" mkdir -pv "$domdir" acme.sh --install-cert -d $dom \ --cert-file "$domdir/cert.pem" \ --key-file "$domdir/key.pem" \ --fullchain-file "$domdir/fullchain.pem" \ --reloadcmd "systemctl reload nginx" done ```