How to issue Let’s Encrypt wildcard certificate with acme.sh and Cloudflare DNS

I read your Let’s Encrypt TLS/SSL certificate acme.sh tutorial. But, I need to set up and use Cloudflare DNS with acme.sh. How do I secure my Nginx web server with Let’s Encrypt TLS/SSL certificate issue with Cloudflare DNS?

For wildcard TLS/SSL certificates, the only challenge method Let’s Encrypt accepts is the DNS challenge to authenticate the domain ownership. Therefore, we need to Cloudflare DNS API to add/modify DNS for our domain. This tutorial explains how to generate a wildcard TLS/SSL certificate using Let’s Encrypt client called acme.sh running on Linux or Unix-like systems.
Tutorial requirements
Operating system/appLinux or Unix with Nginx and Cloudflare API key
Root privileges required Yes
Difficulty Intermediate (rss)
Estimated completion time 10m
Table of contents

ADVERTISEMENTS

Prerequisite to get Let’s Encrypt wildcard certificate

You need the Nginx server installed and running. For example:
$ sudo apt install nginx
$ sudo yum install nginx

See the following tutorials:

Step 1 – Getting Cloudflare API key

Head over to Cloudflare control panel and obtain API key:
Cloudflar API Tokents
Click on the “Create Token” > “Edit zone DNS” > Use template :
Cloudflare Edit Zone DNS use template for Acme.sh
Make sure you set up DNS Permissions to Edit and include zone to your DNS domain name such as cyberciti.biz:
DNS Cloudflare New API Tokens
Finally click on the “Continue to summary” to see your “Edit zone DNS API token summary” as follows:
Get DNS to issue a Lets Encrypt Wildcard SSL certificate with Acme.sh
Finally, copy token displayed on the screen to access the Cloudflare API. Do not share this token with anyone. Keep it secure and secret.

Step 2 – Installing acme.sh client

After getting Cloudflare DNS API key, now set up the acme.sh client. Hence, clone the acme.sh repo using the git command:
$ cd /tmp/
$ git clone https://github.com/Neilpang/acme.sh.git

Install the client but first log in as root user using the su command/sudo command:
$ sudo -i
# touch /root/.bashrc
# cd /tmp/acme.sh/
# acme.sh --install --accountemail your-email-id@domain-here

Installing acme.sh Let’s Encrypt client

Installing acme.sh Let’s Encrypt client

Step 3 – Issuing Let’s Encrypt wildcard certificate

So far we set up Nginx, obtained Cloudflare DNS API key, and now it is time to use acme.sh to get a wildcard certificate for cyberciti.biz domain. First set up the CF_Token using export command as follows:
# Export single variable for the CloudFlare DNS challenge to work #
# export CF_Token="Your_Cloudflare_DNS_API_Key_Goes_here"

No need to define shell variable CF_Account_ID and CF_Zone_ID as those will be automatically pulled by the acme.sh. Then request the certificate. Make sure you replace cyberciti.biz with your domain name:
# acme.sh --issue --dns dns_cf --ocsp-must-staple --keylength 4096 -d cyberciti.biz -d '*.cyberciti.biz'
If you prefer Elliptic-curve cryptography (ECC/ECDSA) instead of RSA, try:

Although it is possible to configure Nginx to use RSA and ECDSA certificates, I will use RSA here as my LB only supports RSA. But you can serve a dual-cert config too which offers an RSA certificate by default, and a (much smaller) ECDSA certificate to those clients that indicate support.

# acme.sh --issue --dns dns_cf --ocsp-must-staple --keylength ec-384 -d cyberciti.biz -d '*.cyberciti.biz'

[Tue 21 Jul 2020 09:41:22 AM UTC] Creating domain key
[Tue 21 Jul 2020 09:41:22 AM UTC] The domain key is here: /root/.acme.sh/cyberciti.biz_ecc/cyberciti.biz.key
[Tue 21 Jul 2020 09:41:22 AM UTC] Multi domain='DNS:cyberciti.biz,DNS:*.cyberciti.biz'
[Tue 21 Jul 2020 09:41:22 AM UTC] Getting domain auth token for each domain
[Tue 21 Jul 2020 09:41:23 AM UTC] Getting webroot for domain='cyberciti.biz'
[Tue 21 Jul 2020 09:41:23 AM UTC] Getting webroot for domain='*.cyberciti.biz'
[Tue 21 Jul 2020 09:41:23 AM UTC] cyberciti.biz is already verified, skip dns-01.
[Tue 21 Jul 2020 09:41:23 AM UTC] *.cyberciti.biz is already verified, skip dns-01.
[Tue 21 Jul 2020 09:41:23 AM UTC] Verify finished, start to sign.
[Tue 21 Jul 2020 09:41:23 AM UTC] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/91904709/4305779370
[Tue 21 Jul 2020 09:41:24 AM UTC] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/0436c4ebf2a5784d099b29e0debb715b9b21
[Tue 21 Jul 2020 09:41:24 AM UTC] Cert success.
-----BEGIN CERTIFICATE-----
MIIEyDCCA7CgAwIBAgISBDbE6/KleE0Jmyng3rtxW5shMA0GCSqGSIb3DQEBCwUA
MDEwMTkwODQxMjNaMBgxFjAUBgNVBAMTDWN5YmVyY2l0aS5iaXowdjAQBgcqhkjO
PQIBBgUrgQQAIgNiAATgsEKKKKZQBxND706ETdaZUwapb8439jksX4P4eJldG03A
.....
..
RuA8fFmhcftOGL2FZmQibR0m2ReMiAAf4m4dmU4uJ1UNp6AabA6Fj+BTbaLRFQqJ
SFGM3REOsgJxWt4ee+oDwZXkpDwUHNqiA7ldyw==
-----END CERTIFICATE-----
[Tue 21 Jul 2020 09:41:24 AM UTC] Your cert is in  /root/.acme.sh/cyberciti.biz_ecc/cyberciti.biz.cer 
[Tue 21 Jul 2020 09:41:24 AM UTC] Your cert key is in  /root/.acme.sh/cyberciti.biz_ecc/cyberciti.biz.key 
[Tue 21 Jul 2020 09:41:24 AM UTC] The intermediate CA cert is in  /root/.acme.sh/cyberciti.biz_ecc/ca.cer 
[Tue 21 Jul 2020 09:41:24 AM UTC] And the full chain certs is there:  /root/.acme.sh/cyberciti.biz_ecc/fullchain.cer

Your Cloudflare DNS API key is sotred in /root/.acme.sh/account.conf file and we can see it using the cat command or grep command:
# cat /root/.acme.sh/account.conf
# grep '_CF_' /root/.acme.sh/account.conf

Step 4 – Configuring Nginx web server

Make sure you create a Diffie-Hellman key exchange file as follows using the openssl command:
# mkdir -pv /etc/nginx/letsencrypt/cyberciti.biz/
# cd /etc/nginx/letsencrypt/cyberciti.biz/
# openssl dhparam -out /etc/nginx/ssl/letsencrypt/cyberciti.biz/dhparams.pem -dsaparam 4096

Then edit your Nginx config file:
# vi /etc/nginx/nginx.conf
Edit/update as follows:

# Port 80 config
server {
 listen      80 default_server; # IPv4
 listen [::]:80 default_server; # IPv6
 server_name www.cyberciti.biz;
 access_log  off;
 error_log   off;
 root        /var/www/html;
 return 301 https://$host$request_uri;
}
 
# Port 443 config
server {
 listen 443 ssl http2;                # IPv4
 listen [::]:443 ssl http2;           # HTTP/2 TLS IPv6
 server_name www.cyberciti.biz;  # domain name 
 # Set document root 
 location / {
 root   /var/www/html;
 index  index.html;
 }
 
 # Set access and error log for this vhos
 access_log /var/log/nginx/cyberciti.biz_access.log;
 error_log  /var/log/nginx/cyberciti.biz_error.log;  
 # TLS/SSL CONFIG 
 ssl_certificate /etc/nginx/ssl/cyberciti.biz/cyberciti.biz.fullchain.cer;
 ssl_certificate_key /etc/nginx/ssl/cyberciti.biz/cyberciti.biz.key;
 
 # ECC/ECDSA certificates (dual config)
 #ssl_certificate /etc/nginx/ssl/cyberciti.biz/cyberciti.biz.fullchain.cer.ecc;
 #ssl_certificate_key /etc/nginx/ssl/cyberciti.biz/cyberciti.biz.key.ecc;
 ssl_dhparam  /etc/nginx/ssl/cyberciti.biz/dhparams.pem;
 
 # A little bit of optimization 
 ssl_session_timeout 1d;
 ssl_session_cache shared:NixCraftSSL:10m;
 
 # TLS version 1.2 and 1.3 only
 ssl_session_tickets off;  
 ssl_protocols TLSv1.2 TLSv1.3;
 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_prefer_server_ciphers off;  
 
 # HSTS (ngx_http_headers_module is required)
 # *************************************************************************
 # WARNING - Wrong headers can create problems. Read docs otherwise
 #           all 3rd party scripts/ads won't load and in some case 
 #           browser won't work. Read docs @ https://developer.mozilla.org
 # ************************************************************************* 
 add_header Strict-Transport-Security "max-age=63072000" always;
 add_header X-Content-Type-Options "nosniff" always;
 add_header X-Frame-Options "SAMEORIGIN" always;
 add_header X-Xss-Protection "1; mode=block" always;
 add_header Referrer-Policy  strict-origin-when-cross-origin always;
 add_header Feature-policy "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'" always;
 # ***************************************************************************************************
 # WARNING: The HTTP Content-Security-Policy response header allows sysadmin/developers 
 # to control resources the user agent is allowed to load for a given page. 
 # Wrong config can create problems for third party scripts/ad networks. Hence read the following url: 
 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
 # ****************************************************************************************************
 add_header content-security-policy "default-src https://www.cyberciti.biz:443" always;  # OCSP stapling
 
 # Verify chain of trust of OCSP response using Root CA and Intermediate certs
 ssl_stapling on;
 ssl_stapling_verify on;  
 ssl_trusted_certificate /etc/nginx/ssl/cyberciti.biz/cyberciti.biz.fullchain.cer;  
 
 # Replace with the IP address of your resolver
 resolver 1.1.1.1;
}

Save and close the file in vim.

Step 5 – Installing Let’s Encrypt wildcard certificate

Install your certificate (replace the systemctl reload nginx command as per your Linux/Unix distro)::
# DOMAIN="cyberciti.biz"
# CONFIG_ROOT="/etc/nginx/ssl/${DOMAIN}"
# acme.sh -d "$DOMAIN" \
--install-cert \
--reloadcmd "systemctl reload nginx" \
--fullchain-file "${CONFIG_ROOT}/$DOMAIN.fullchain.cer" \
--key-file "${CONFIG_ROOT}/$DOMAIN.key" \
--cert-file "${CONFIG_ROOT}/$DOMAIN.cer"

Install ECC/ECDSA if you need them too (again, replace the systemctl reload nginx command as per your Linux/Unix distro):
# acme.sh -d "$DOMAIN" \
--ecc \
--install-cert \
--reloadcmd "systemctl reload nginx" \
--fullchain-file "${CONFIG_ROOT}/$DOMAIN.fullchain.cer.ecc" \
--key-file "${CONFIG_ROOT}/$DOMAIN.key.ecc" \
--cert-file "${CONFIG_ROOT}/$DOMAIN.cer.ecc"

Step 6 – Testing

Make sure you open Nginx server tcp port # 443 if not already opened. For example, here is how we can open it on Ubuntu or Debian Linux:
$ sudo ufw allow https comment 'Open all to access Nginx port 443'
Fire a web browser and type the url:
https://www.cyberciti.biz/
Of course, we can visit SSL labs to test our TLS/SSL config page. Another option is to run the testssl.sh command as follows:
$ testssl.sh --fast --parallel https://www.cyberciti.biz/
Let's Encrypt wildcard certificate with A+ grade

Conclusion

You created a wildcard TLS/SSL certificate for your domain using acme.sh and Cloudflare DNS API for domain verification. Please note that acme.sh automatically configure a cron jobs to renew our wildcard based certificate. You can now install certificates to ISP load balancer or even use on LAN that are not open from the internet.

This entry is 11 of 13 in the Secure Web Server with Let's Encrypt Tutorial series. Keep reading the rest of the series:
  1. Set up Lets Encrypt on Debian/Ubuntu Linux
  2. Secure Lighttpd with Lets Encrypt certificate on Debian/Ubuntu
  3. Configure Nginx with Lets Encrypt certificate on Alpine Linux
  4. Nginx with Lets Encrypt on CentOS 7
  5. Apache with Lets Encrypt Certificates on RHEL 8
  6. CentOS 8 and Apache with Lets Encrypt Certificates
  7. Install Lets Encrypt certificates on CentOS 8 for Nginx
  8. Forcefully renew Let's Encrypt certificate
  9. OpenSUSE Linux and Nginx with Let's Encrypt Certificates
  10. Configure Nginx to use TLS 1.2 / 1.3 only
  11. Let's Encrypt wildcard certificate with acme.sh and Cloudflare DNS
  12. Nginx with Let's Encrypt on Ubuntu 18.04 with DNS Validation
  13. AWS Route 53 Let's Encrypt wildcard certificate with acme.sh

🐧 If you liked this page, please support my work on Patreon or with a donation.
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04

ADVERTISEMENTS
3 comments… add one

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.