Skip to content

Instantly share code, notes, and snippets.

@ammgws
Last active July 23, 2023 17:19
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save ammgws/381b4d9104c4e2b43b9210f33f03a15a to your computer and use it in GitHub Desktop.
Save ammgws/381b4d9104c4e2b43b9210f33f03a15a 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  "your@email.com" \
--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 
@trex2000
Copy link

What about wildcard domains ? like *.domain.com ?
Can this be used ?

@belcerca
Copy link

@trex2000 Yes, just be sure to be using the last version from acme.sh

@kevdogg
Copy link

kevdogg commented Sep 13, 2020

Hey question about your script since I've had a lot of problems getting my systemd service unit file to work --

From the command line if I need to renew a certificate I use:

# acme.sh --renew -d <domain.com> --force

--force flag is optional
This command will automatically copy the certs to the specified directory according to the directories I specified when I issued the certs manually

The gist of your systemd script is this line:
ExecStart=/path/to/acme.sh --home /path/to/acmedir --cron --issue --dns dns_freedns -d yourdomain.com --log

When I run this command I have the follow questions:

  1. (--issue --cron) instead of (--renew)
  2. When I run the command I don't get the resulting certificates copied to the target directories -- such as /etc/ssl/....
  3. I've seen examples on other websites using:
    ExecStart=/path/to/acme.sh --cron --home /path/to/acmedir
    These don't use the --issue command

Is there something I'm missing

@rajaws2015
Copy link

rajaws2015 commented Oct 1, 2020

Hi,
Yesterday I tried to use the below command, but it is not renewed.
acme.sh --renew -d <domain.com> --force

Actuall I have installed the Acme as mentioned in the below. Issued the cert with nginx mode for the domains but autorenewal is not happening. It is really cumbersome. Do have to follow any document?
git clone https://github.com/Neilpang/acme.sh.git cd acme.sh ./acme.sh --install

@celsojr
Copy link

celsojr commented Oct 27, 2021

I think you also have to install the certificate after you renew it

  1. Copy certificate via cpanel
cat /home/YOUR_USER/.acme.sh/DOMAIN.COM/DOMAIN.COM.cer
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
  1. Copy the certificate key via cpanel
cat /home/YOUR_USER/.acme.sh/DOMAIN.COM/DOMAIN.COM.key
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

or your can try something like this command in the next time as a cronjob

cd /home/YOUR_USER/.acme.sh; \
acme.sh --force --issue -d DOMAIN.COM -d WWW.DOMAIN.COM -w ~/www > /dev/null 2>&1; \
acme.sh --deploy -d DOMAIN.COM -d WWW.DOMAIN.COM --deploy-hook cpanel > /dev/null 2>&1

@LeatheryStranger
Copy link

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

I think the command:

--certhome ~/acme-letsencrypt \

needs to be changed to:

--cert-home ~/acme-letsencrypt \

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment