Skip to content

Instantly share code, notes, and snippets.

@shawnhank
Forked from ammgws/letsencrypt-acme-guide.md
Created February 1, 2023 03:35
Show Gist options
  • Save shawnhank/71799be2dac86017629deef84f5bf323 to your computer and use it in GitHub Desktop.
Save shawnhank/71799be2dac86017629deef84f5bf323 to your computer and use it in GitHub Desktop.
Using acme.sh script to renew LetsEncrypt certs using non-standard SSL port

1. Install acme.sh shell script

git clone https://github.com/Neilpang/acme.sh.git
cd acme.sh
./acme.sh --install

Optionally, set the home dir and/or account info (if already have one).
If was previously using LetsEncrypt's certbot, can probably get account info from /etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/*/regr.json.

./acme.sh --install \
--home ~/acme-letsencrypt \
--config-home ~/acme-letsencrypt \
--certhome  ~/acme-letsencrypt \
--accountemail  "[email protected]" \
--accountkey  ~/myaccount.key \

NOTE: For some reason acme.sh keeps creating certs in the default ~/.acme.sh/ directory... Can't figure out why.

2. Setup DNS API to work with FreeDNS

If not using FreeDNS check this page for others.

FreeDNS (https://freedns.afraid.org/) does not provide an API to update DNS records (other than IPv4 and IPv6 dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging into the FreeDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your userid and password for the FreeDNS website.

Fish Shell:

set -x FREEDNS_User 'youruser'
set -x FREEDNS_Password 'yourpassword'

NOTE: You may need to quote your password string if it contains special chars

Bash:

export FREEDNS_User="yourusername"
export FREEDNS_Password="yourpassword"

You need only provide this the first time you run the acme.sh client with FreeDNS validation and then again whenever you change your password at the FreeDNS site. The acme.sh FreeDNS plugin does not store your userid or password but rather saves an authentication token returned by FreeDNS in ~/.acme.sh/account.conf and reuses that when needed.

Note that you cannot use acme.sh automatic DNS validation for FreeDNS public domains or for a subdomain that you create under a FreeDNS public domain. You must own the top level domain in order to automatically validate with acme.sh at FreeDNS.

3. Run script

./acme.sh --issue --dns dns_freedns -d yourdomain.com

4. Remove the user/password env variables from step 2

Fish Shell:

set -e FREEDNS_User
set -e FREEDNS_Password

5. Install cert to nginx

acme.sh --install-cert -d example.com \
--key-file       /path/to/keyfile/in/nginxconfig/cert.key  \
--fullchain-file /path/to/fullchain/nginxconfig/fullchain.cer \

sudo service nginx force-reload

acme.sh has a flag to restart nginx itself but I couldn't get it to work, would get Domain is not valid:<readacted>.com:

acme.sh --install-cert -d example.com \
--key-file       /path/to/keyfile/in/nginxconfig/cert.key  \
--fullchain-file /path/to/fullchain/nginxconfig/fullchain.cer \
--reloadcmd     "service nginx force-reload"

6. Create a systemd unit for acme.sh

/etc/systemd/system/acme_letsencrypt.service

[Unit]
Description=Renew Let's Encrypt certificates using acme.sh
After=network-online.target

[Service]
Type=oneshot
ExecStart=/path/to/acme.sh --home /path/to/acmedir --cron --issue --dns dns_freedns -d yourdomain.com --log

7. Test that it works before creating the timer

sudo systemctl daemon-reload
sudo systemctl start acme_letsencrypt --now

8. Create systemd timer unit for the service above

/etc/systemd/system/acme_letsencrypt.timer

[Unit]
Description=Daily renewal of Let's Encrypt's certificates

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

9. Enable timer

sudo systemctl enable acme_letsencrypt.timer

For reference only

Setup crontab

Since we need to interact with nginx, we require root access, so must move acme.sh cron job to root crontab:

sudo crontab -e
ACME_DIR = /path/to/acme-letsencrypt
24 0 * * * $ACME_DIR/acme.sh --cron --home $ACME_DIR --renew-hook "systemctl force-reload nginx"

NOTE:

  1. Make sure that nginx user has access to /path/to/copykeyto/ folder.
  2. --cron will automatically install certs on successful renew info.
    It appears to base it on the last time you ran --install-cert, so make sure you run that manually at least once to be sure cron job will work as expected.

Example output (first run)

./acme.sh --issue --dns dns_freedns -d yourdomain.com
[Sun Jul 23 14:50:49 JST 2017] Registering account
[Sun Jul 23 14:50:53 JST 2017] Registered
[Sun Jul 23 14:50:56 JST 2017] Update account tos info success.
[Sun Jul 23 14:50:57 JST 2017] ACCOUNT_THUMBPRINT='REDACTED'
[Sun Jul 23 14:50:57 JST 2017] Creating domain key
[Sun Jul 23 14:51:11 JST 2017] The domain key is here: /path/to/.acme.sh/yourdomain.com/yourdomain.com.key
[Sun Jul 23 14:51:11 JST 2017] Single domain='yourdomain.com'
[Sun Jul 23 14:51:12 JST 2017] Getting domain auth token for each domain
[Sun Jul 23 14:51:12 JST 2017] Getting webroot for domain='yourdomain.com'
[Sun Jul 23 14:51:12 JST 2017] Getting new-authz for domain='yourdomain.com'
[Sun Jul 23 14:51:17 JST 2017] The new-authz request is ok.
[Sun Jul 23 14:51:18 JST 2017] Found domain api file: /path/to/acme-letsencrypt/dnsapi/dns_freedns.sh
[Sun Jul 23 14:51:18 JST 2017] Add TXT record using FreeDNS
[Sun Jul 23 14:55:19 JST 2017] Added acme challenge TXT record for _acme-challenge.yourdomain.com at FreeDNS
[Sun Jul 23 14:55:19 JST 2017] Sleep 120 seconds for the txt records to take effect
[Sun Jul 23 14:57:25 JST 2017] Verifying:yourdomain.com
[Sun Jul 23 14:57:32 JST 2017] Success
[Sun Jul 23 14:57:32 JST 2017] Delete TXT record using FreeDNS
[Sun Jul 23 14:57:37 JST 2017] Deleted acme challenge TXT record for _acme-challenge.yourdomain.com at FreeDNS
[Sun Jul 23 14:57:37 JST 2017] Verify finished, start to sign.
[Sun Jul 23 14:57:41 JST 2017] Cert success.
-----BEGIN CERTIFICATE-----
MIIE+TCCA+<REDACTED>=
-----END CERTIFICATE-----
[Sun Jul 23 14:57:41 JST 2017] Your cert is in  /path/to/.acme.sh/yourdomain.com/yourdomain.com.cer 
[Sun Jul 23 14:57:41 JST 2017] Your cert key is in  /path/to/.acme.sh/yourdomain.com/yourdomain.com.key 
[Sun Jul 23 14:57:43 JST 2017] The intermediate CA cert is in  /path/to/.acme.sh/yourdomain.com/ca.cer 
[Sun Jul 23 14:57:43 JST 2017] And the full chain certs is there:  /path/to/.acme.sh/yourdomain.com/fullchain.cer 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment