How to create a Let’s Encrypt wildcard certificate on a Synology NAS

I love the Let’s Encrypt functionality on the Synology but the built-in solution will not allow you to create a wildcard certificate. From a security standpoint a good way to do certificates with SAN’s but if you are like me and run a home lab… The pain of doing it the SAN way with domain DNS validation and having a /29 ipv4 public subnet is a bitch…

I always need to change a few of my public DNS IP’s to point to the Synology or the auto-renewal will fail. Also, there is a stupid 255 character limit in the SAN text box on the Synology.. so having a few SAN’s is fine but there is a limit… Hence here comes the wildcard and for a lab, it makes life easy. Below is my guide to do this manually every 3 months. I will be working out on how to automate this renewal.

How to create a wildcard on a Synology

We are going to use the acme.sh script to accomplish this. For authentication of the domain name, we will use the DNS option. First login to your Synology with ssh as the admin user and then sudo -i to get root access. When you login into the Synology with ssh you will end up in the /root path. I assume for the rest of the guide we run everything from that path. Now we need to get the script and change the permissions so it is executable.

wget https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh
chmod a+x acme.sh
Next we issue the certificate:
Note: replace *.vdr.one with your own domain. To add top-level domain or multi-domain add “-d  yourdomain
./acme.sh --issue -d *.vdr.one --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please

You will get an output like below:

Registering account
Registered
ACCOUNT_THUMBPRINT='abjJHFIUTDGIBDYDDJBDJKkhHk'
Creating domain key
The domain key is here: /root/.acme.sh/*.vdr.one/*.vdr.one.key
Single domain='*.vdr.one'
Getting domain auth token for each domain
Getting webroot for domain='*.vdr.one'
You need to add the txt record manually.
Add the following TXT record:
Domain: '_acme-challenge.vdr.one'
TXT value: 'ThIsISNoTtHeReAlKeY'
Please be aware that you prepend _acme-challenge. before your domain
so the resulting subdomain will be: _acme-challenge.vdr.one
Please add the TXT records to the domains, and re-run with --renew.
Please add '--debug' or '--log' to check more details.

As you can see you will have to create a DNS text record with your domain name provider according to the output I marked. Needless to say that you need to use your own values.After you created it we will have to run the acme.sh script again with –renew.
Note: It can take some time for DNS records to get updated. Depends on your provider for my provider it is less than a minute. For renewal, after 3 months you can just run the renew command.

./acme.sh --renew -d *.vdr.one --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please

When all goes well you get an output like below:

Your cert is in /root/.acme.sh/*.vdr.one/*.vdr.one.cer
Your cert key is in /root/.acme.sh/*.vdr.one/*.vdr.one.key
The intermediate CA cert is in /root/.acme.sh/*.vdr.one/ca.cer
And the full chain certs is there: /root/.acme.sh/*.vdr.one/fullchain.cer

Now copy the files to an accessible share on your NAS. In my case a made a share called Certs with the subfolder of vdr.one in it.:

cp "/root/.acme.sh/*.vdr.one/*.vdr.one.cer" "/volume1/Certs/vdr.one/vdr.one.cer"
cp "/root/.acme.sh/*.vdr.one/*.vdr.one.key" "/volume1/Certs/vdr.one/vdr.one.key"
cp "/root/.acme.sh/*.vdr.one/ca.cer" "/volume1/Certs/vdr.one/ca.cer"
cp "/root/.acme.sh/*.vdr.one/fullchain.cer" "/volume1/Certs/vdr.one/fullchain.cer"

You will see that the files will be in that folder after running the cp actions.

You can close the SSH session now and for security reasons, you can also disable SSH.
All you need to do now is to import your certificate on the Synology if you want or just start using it where you need it!

To import it on your Synology

If you use your Synology as a reverse proxy and ssl ofloader like me this is pretty darn handy!
Go to the Control Panel, then Security and Certificate. Choose Add.

Then import

And put in the files requested.

Now you’re done and you have a Let’s Encrypt wildcard certificate.

13 thoughts on “How to create a Let’s Encrypt wildcard certificate on a Synology NAS

  1. Tone

    You’re a wizard, thank so much! I was looking for a single solution for synology and you give me!

    Reply
  2. Nick

    Thanks a mil for the wite up, learnt a lot about SSL today!

    If it helps I have automated mine by using Synology’s “Task Scheduler” to run a bash script every month.

    The only thing i had to change vs what you have is include the –force tag to force the renewal ahead of schedule.
    “`
    ./acme.sh –renew –force -d *.vdr.one –dns –yes-I-know-dns-manual-mode-enough-go-ahead-please
    “`
    Works like a charm on a test run we will soon see in June when it comes to renewals 🙂

    Reply
      1. Monten

        I followed your instructions but I receive error message :
        .
        dsemo.com:Challenge error: {
        “type”: “urn:ietf:params:acme:error:malformed”,
        “detail”: “Unable to update challenge :: authorization must be pending”,
        “status”: 400
        }

        See log hereunder. Any idea of what I’m doing wrong ?

        Thank you for your feedback.

        Erik

        root@NAS_ERIK:~# ./acme.sh –renew –debug –log -d *.dsemo.com –dns –yes-I-know-dns-manual-mode-enough-go-ahead-please
        [Fri May 17 15:58:11 CEST 2019] Wildcard domain
        [Fri May 17 15:58:11 CEST 2019] Lets find script dir.
        [Fri May 17 15:58:11 CEST 2019] _SCRIPT_=’./acme.sh’
        [Fri May 17 15:58:11 CEST 2019] _script=’/root/acme.sh’
        [Fri May 17 15:58:11 CEST 2019] _script_home=’/root’
        [Fri May 17 15:58:11 CEST 2019] Using default home:/root/.acme.sh
        [Fri May 17 15:58:11 CEST 2019] Using config home:/root/.acme.sh
        https://github.com/Neilpang/acme.sh
        v2.8.2
        [Fri May 17 15:58:11 CEST 2019] Using config home:/root/.acme.sh
        [Fri May 17 15:58:11 CEST 2019] ACME_DIRECTORY=’https://acme-v02.api.letsencrypt.org/directory’
        [Fri May 17 15:58:11 CEST 2019] DOMAIN_PATH=’/root/.acme.sh/*.dsemo.com’
        [Fri May 17 15:58:11 CEST 2019] Renew: ‘*.dsemo.com’
        [Fri May 17 15:58:11 CEST 2019] Le_API
        [Fri May 17 15:58:11 CEST 2019] _main_domain=’*.dsemo.com’
        [Fri May 17 15:58:11 CEST 2019] _alt_domains=’no’
        [Fri May 17 15:58:11 CEST 2019] Using ACME_DIRECTORY: https://acme-v02.api.letsencrypt.org/directory
        [Fri May 17 15:58:11 CEST 2019] _init api for server: https://acme-v02.api.letsencrypt.org/directory
        [Fri May 17 15:58:11 CEST 2019] GET
        [Fri May 17 15:58:11 CEST 2019] url=’https://acme-v02.api.letsencrypt.org/directory’
        [Fri May 17 15:58:11 CEST 2019] timeout=
        [Fri May 17 15:58:11 CEST 2019] _CURL=’curl -L –silent –dump-header /root/.acme.sh/http.header -g ‘
        [Fri May 17 15:58:11 CEST 2019] ret=’0′
        [Fri May 17 15:58:12 CEST 2019] ACME_KEY_CHANGE=’https://acme-v02.api.letsencrypt.org/acme/key-change’
        [Fri May 17 15:58:12 CEST 2019] ACME_NEW_AUTHZ
        [Fri May 17 15:58:12 CEST 2019] ACME_NEW_ORDER=’https://acme-v02.api.letsencrypt.org/acme/new-order’
        [Fri May 17 15:58:12 CEST 2019] ACME_NEW_ACCOUNT=’https://acme-v02.api.letsencrypt.org/acme/new-acct’
        [Fri May 17 15:58:12 CEST 2019] ACME_REVOKE_CERT=’https://acme-v02.api.letsencrypt.org/acme/revoke-cert’
        [Fri May 17 15:58:12 CEST 2019] ACME_AGREEMENT=’https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf’
        [Fri May 17 15:58:12 CEST 2019] ACME_NEW_NONCE=’https://acme-v02.api.letsencrypt.org/acme/new-nonce’
        [Fri May 17 15:58:12 CEST 2019] ACME_VERSION=’2′
        [Fri May 17 15:58:12 CEST 2019] Le_NextRenewTime
        [Fri May 17 15:58:12 CEST 2019] _on_before_issue
        [Fri May 17 15:58:12 CEST 2019] _chk_main_domain=’*.dsemo.com’
        [Fri May 17 15:58:12 CEST 2019] _chk_alt_domains
        [Fri May 17 15:58:12 CEST 2019] Le_LocalAddress
        [Fri May 17 15:58:12 CEST 2019] d=’*.dsemo.com’
        [Fri May 17 15:58:12 CEST 2019] Check for domain=’*.dsemo.com’
        [Fri May 17 15:58:12 CEST 2019] _currentRoot=’dns’
        [Fri May 17 15:58:12 CEST 2019] d
        [Fri May 17 15:58:12 CEST 2019] _saved_account_key_hash is not changed, skip register account.
        [Fri May 17 15:58:12 CEST 2019] Read key length:
        [Fri May 17 15:58:12 CEST 2019] _createcsr
        [Fri May 17 15:58:12 CEST 2019] Single domain=’*.dsemo.com’
        [Fri May 17 15:58:12 CEST 2019] Getting domain auth token for each domain
        [Fri May 17 15:58:12 CEST 2019] ok, let’s start to verify
        [Fri May 17 15:58:12 CEST 2019] Verifying: *.dsemo.com
        [Fri May 17 15:58:12 CEST 2019] d=’*.dsemo.com’
        [Fri May 17 15:58:12 CEST 2019] keyauthorization=’frb0-z-hQF_Eqmrdu7bKjhcHOTfZYLcTG6Szrj7tv3s.A_dkXhoIBF-KZMy1A9pG9BoaxF1fRNOkxlMu2bSPazg’
        [Fri May 17 15:58:12 CEST 2019] uri=’https://acme-v02.api.letsencrypt.org/acme/challenge/vFciD3wF5QkQH-w-78RkVl521N8gybKkmd1jibJLJPA/15965441778′
        [Fri May 17 15:58:12 CEST 2019] _currentRoot=’dns’
        [Fri May 17 15:58:12 CEST 2019] url=’https://acme-v02.api.letsencrypt.org/acme/challenge/vFciD3wF5QkQH-w-78RkVl521N8gybKkmd1jibJLJPA/15965441778′
        [Fri May 17 15:58:12 CEST 2019] payload='{}’
        [Fri May 17 15:58:13 CEST 2019] RSA key
        [Fri May 17 15:58:13 CEST 2019] HEAD
        [Fri May 17 15:58:13 CEST 2019] _post_url=’https://acme-v02.api.letsencrypt.org/acme/new-nonce’
        [Fri May 17 15:58:13 CEST 2019] _CURL=’curl -L –silent –dump-header /root/.acme.sh/http.header -g ‘
        [Fri May 17 15:58:13 CEST 2019] _ret=’0′
        [Fri May 17 15:58:13 CEST 2019] POST
        [Fri May 17 15:58:13 CEST 2019] _post_url=’https://acme-v02.api.letsencrypt.org/acme/challenge/vFciD3wF5QkQH-w-78RkVl521N8gybKkmd1jibJLJPA/15965441778’
        [Fri May 17 15:58:13 CEST 2019] _CURL=’curl -L –silent –dump-header /root/.acme.sh/http.header -g ‘
        [Fri May 17 15:58:13 CEST 2019] _ret=’0′
        [Fri May 17 15:58:13 CEST 2019] code=’400′
        [Fri May 17 15:58:14 CEST 2019] *.dsemo.com:Challenge error: {
        “type”: “urn:ietf:params:acme:error:malformed”,
        “detail”: “Unable to update challenge :: authorization must be pending”,
        “status”: 400
        }
        [Fri May 17 15:58:14 CEST 2019] Skip for removelevel:
        [Fri May 17 15:58:14 CEST 2019] pid
        [Fri May 17 15:58:14 CEST 2019] No need to restore nginx, skip.
        [Fri May 17 15:58:14 CEST 2019] _clearupdns
        [Fri May 17 15:58:14 CEST 2019] dns_entries
        [Fri May 17 15:58:14 CEST 2019] skip dns.
        [Fri May 17 15:58:14 CEST 2019] _on_issue_err
        [Fri May 17 15:58:14 CEST 2019] Please check log file for more details: /root/.acme.sh/acme.sh.log
        [Fri May 17 15:58:14 CEST 2019] url=’https://acme-v02.api.letsencrypt.org/acme/challenge/vFciD3wF5QkQH-w-78RkVl521N8gybKkmd1jibJLJPA/15965441778′
        [Fri May 17 15:58:14 CEST 2019] payload='{}’
        [Fri May 17 15:58:14 CEST 2019] POST
        [Fri May 17 15:58:14 CEST 2019] _post_url=’https://acme-v02.api.letsencrypt.org/acme/challenge/vFciD3wF5QkQH-w-78RkVl521N8gybKkmd1jibJLJPA/15965441778′
        [Fri May 17 15:58:14 CEST 2019] _CURL=’curl -L –silent –dump-header /root/.acme.sh/http.header -g ‘
        [Fri May 17 15:58:14 CEST 2019] _ret=’0′
        [Fri May 17 15:58:14 CEST 2019] code=’400’
        [Fri May 17 15:58:14 CEST 2019] The dns manual mode can not renew automatically, you must issue it again manually. You’d better use the other modes instead.
        [Fri May 17 15:58:14 CEST 2019] socat doesn’t exists.
        [Fri May 17 15:58:14 CEST 2019] Diagnosis versions:
        openssl:openssl
        OpenSSL 1.0.2r-fips 26 Feb 2019
        apache:
        apache doesn’t exists.
        nginx:
        nginx version: nginx/1.15.7
        TLS SNI support enabled
        socat:

        Reply
        1. LaurensvanDuijn Post author

          Your DNS challenge has extra quotes! ‘” it’s wrong 🙂 You can check if the record exists with:
          nslookup -type=txt _acme-challenge.dsemo.com and compare it with mine.

          _acme-challenge.dsemo.com text = “‘a9JdivAfGYtjFO64S_dmiM9logZ8T9PDnNwYrXXBb2k‘”

          Reply
          1. Monten

            Thank you very much !! Problem solved. Works perfectly. Very much appreciated.

  3. René

    Hello,

    thank you very much for this great instruction! Do you have any news in automate the renewal?

    Reply
  4. Pingback: Install Let’s Encrypt Certificates on Synology – The Knowledge Base

    1. LaurensvanDuijn Post author

      Not yet.. working on it but since the dns challenge changes every time.. you need a dns provider with api access.

      Reply
      1. René

        Can you provide more feedback what you mean with DNS provider with API access please?

        Reply
        1. LaurensvanDuijn Post author

          What i mean is that you need a dns provider where you can change dns records via scripting. (Api) instead of doing it manually every time.

          Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.