Let's Encrypt For Domains

How to Enable Let's Encrypt and Install an SSL for a Domain

Let's Encrypt is a free SSL tool that lets you install a very basic free SSL Certificate with 1 click. It supports multiple domains, sub-domains, and wildcards, and will auto-renew automatically before it expires after it's ~90 day lifespan.

To enable this feature in DirectAdmin, ensure you have DirectAdmin 1.50.1 or newer.

echo "action=directadmin&value=restart" >> /usr/local/directadmin/data/task.queue; /usr/local/directadmin/dataskq d2000
  • Add the /.well-known Alias:
cd /usr/local/directadmin/custombuild
./build rewrite_confs

Users should then be able to see the Let's Encrypt option in their panel via User Level -> SSL Certificates and be able to use this option to enable SSLs for their domains.

Select the Let's Encrypt option, review the subdomain/wildcard selections, adjust as desired, then proceed to request the SSL to be installed.

To install a Let's Encrypt SSL for a domain via SSH, you can use the letsencrypt.sh scriptopen in new window located at /usr/local/directadmin/scripts/letsencrypt.sh:

usage:

./letsencrypt.sh request|renew|revoke domain.com 4096 (/path/to/csr-request-config-file)

but you shouldn't need to run it manually, as DA will call it automatically when the User triggers it through DA.

Note, when you run it through DA, the domain.com.san_config will have more details than if you run it from ssh (.san_config will be created, but with less info).

LEGO: Remote DNS Providers with Let's Encrypt

LEGO is a LetsEncrypt client and ACME library written in Go, hence the name LEGO. This useful library facilitates the use of 3rd-party, remote DNS providers with Let's Encrypt by utilizing those providers' APIs to complete domain validation checks via DNS, thus permitting the issuance of LE SSLs for domains using remote DNS.

Wildcard certificates for LetsEncrypt require DNS confirmation. If you're running at some remote DNS provider that is not currently supported by the Multi-Server Setup, then this tool lets you use wildcard certs with those DNS providers.

This document will use Cloudflare as the example remote DNS provider.

Related:

INSTALL

Ensure that wildcard domain SSL certificate support via Let's Encryptopen in new window is enabled via dns_ttl=1 in the directadmin.conf.

GUI USAGE

This feature is only available in the Evolution Skin as of September 2024. You may access it via the User Level » SSL Certificates » Manage ACME DNS Providers Sample URI for the Enhanced GUI: CMD_SSL?action=dnsprovider&domain=domain.com&type=domain

It can be used for both the Manually requested LetsEncrypt / ZeroSSL, as well as the Use the best match certificate (aka: Auto SSL and Use the server's certificate).

It's no longer nessecary to have this setup at the same time as the request of the certificate. Simply the existence of the created dnsprovider.conf file will be used anytime the letsencrypt.sh script is called.

You can then pick a new DNS provider, say "CloudFlare", and enter your user/key + extra bits, as specified by that provider.

We'll be adding fixes/improvements as we go, so ensure you have the updated script if you encounter any issues!

INHERIT DNS PROVIDER

An Admin/Creator can set up 2 possible inherit files:

  • Global: /usr/local/directadmin/data/admin/dnsprovider.conf
  • Creator: /usr/local/directadmin/data/users/resellerbob/dnsprovider.conf

Should either of these exist, where creator=resellerbob is in the given User's user.conf, they'll be included in the list of dnsproviders["data"] output as:

dnsproviders["data"]["inherit-creator"]
dnsproviders["data"]["inherit-global"]

where each would still have the correct "Name", but the type is prefixed, e.g., dnsproviders["data"]["inherit-creator"]["name"] = "Inherit Creator : Cloudflare"

"Inherit Creator : " or "Inherit Global" would be prefixed beside the name from the used provider, Cloudflare in this example, for that inherited dnsprovider.conf type.

The inherit-creator or inherit-global arrays will have an empty credentials array, and zero credentials are allowed to be passed if the master (including inherited configs) have zero creds.

If the "dnsprovider" is empty (nothing picked yet), check for:
dnsproviders["settings"]["default"]
to know which selection should be used by default. It should be either local, inherit-creator, or inherit-global.

The Global/Creator dnsprovider.conf files may contain one of the following:

default=inherit-creator
default=inherit-global
default=local

which is what specifies the default value for a User to have pre-selected.

JSON - API CALLS

Below will be variouls way to view and save the dnsprovider information. It was previously only done through the action=save method, however new GET and POST method have been added with action=dnsprovider which is standalone, so as to not overlap with saving SSL Certificates. When possible, we recomend using the aciton=dnsprovider to view/save this inforation.

VIEW

You can get all related providers, plus any saved information like this:

CMD_SSL | CMD_API_SSL
method: GET
action=dnsprovider
domain=domain.com
type=domain|creator|global
domains=yes
json=yes

The domain currently needs to be passed, even if the creator and global types are used (this may change in the future to not be required).

General layout of the json response:

dnsproviders["data"] - includes a 'version', plus the list of all available dnsproviders, including 'inherit-global' and/or 'inherit-creator' if available.
dnsproviders["dnsprovider"] - if present, it means this type has a dnsprovider.conf saved.
dnsproviders["type"] = "creator" | "global"
domains - array of allowed domains to control if 'domains=yes' is passed. Only applies to creator and global types.
type - current type passed in the  request
types - array of avaialble types

Example:

{
	"dnsproviders":
	{
		"data": 
		{
			"version": "4.5.3",
			"acme-dns": 
			{
				"name": "Joohoi's ACME-DNS",
				"credentials": 
				{
					"ACME_DNS_API_BASE": "The ACME-DNS API address",
					"ACME_DNS_STORAGE_PATH": "The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates."
				},
				"additional_configuration": {
					...
				}
			},
... large list of dnsproviders ...
			"inherit-global":
			{
				"credentials":
				{
				},
				"name": "Inherit Global : Cloudflare"
			}
		},
		"dnsprovider":
		{
			"CLOUDFLARE_API_KEY": "MYKEY",
			"CLOUDFLARE_DNS_API_TOKEN": "",
			"CLOUDFLARE_EMAIL": "some@email.com",
			"CLOUDFLARE_HTTP_TIMEOUT": "",
			"CLOUDFLARE_POLLING_INTERVAL": "",
			"CLOUDFLARE_PROPAGATION_TIMEOUT": "",
			"CLOUDFLARE_TTL": "",
			"CLOUDFLARE_ZONE_API_TOKEN": "",
			"dnsprovider": "cloudflare"
		}
	},
	"domains" :
	[
	],
	"type": "domain",
	"types":
	{
		"creator": "Creator",
		"domain": "Domain domain.com",
		"global": "Global"
	}
}

Note that the dnsproviders[dnsprovider][dnsprovider] will show the currently saved dnsprovider in the given dnsproviders.conf file. If the domain has inherited a higher-level provider, it might look like this:

{
	"dnsproviders":
	{
...
		"dnsprovider":
		{
			"dnsprovider": "inherit-global"
		}

where you'd do a lookup of inherit-global in the dnsproviders[data]["inherit-global"], to get the name, and any related credentials (which are more for an actual provider's set credentials)

If type=creator or type=global is set in this GET, there will be no inherit-global nor inherit-creator providers offered, but the domains array will be filled out accordingly when domains=yes is passed.

Also for these higher level types, if any Domain Restrictions are passed, the dnsproviders[dnsprovider] may include either domains_allow or domains_deny (never both), which are a list of domains that are or are not allowed to inherit this dnsprovider.

SAVE

Save the dnsprovider to any of the 3 possible type locations:

CMD_SSL | CMD_API_SSL
method: POST
action=dnsprovider
domain=domain.com
dnsprovider=cloudflare|inherit-creator|etc.
type=domain|global|creator
CLOUDFLARE_EMAIL=foo@bar.com
CLOUDFLARE_API_KEY=sdgsd7681afn

Optional items for type=creator|global:

Allow/Deny: One or more domains_allow[] OR one or more domains_deny[]
domains_allow[]=userdomain.com
domains_deny[]=userdomain.com

default=local|inherit-global|inherit-creator - used to specify a default for their Users (optional)

where the example CLOUDFLARE_ items are based on the crentials and/or additional_configuration for the given dnsprovider. At least one of the credentials must be provided. It's up to the provider's connection to decide which are required or set correctly. An inherited value (eg: dnsprovider=inherit-creator) can only be saved for type=domain.

The type=creator and type=global may pass domains_allow[]=clear or domains_deny[]=clear to remove the given restriction from the dnsprovider.conf. Note the [] array method of passing the varible.

Absense of either allow and denay allows all related domains to inherit this creator/global type. The creator dnsprovider is only available to Users below this Reseller/Admin. Presence of domains_allow with one or more domain only allows these domains. Presence of domains_deny with one or more domain allows all except these domains. You cannot use both domains_allow and domains_deny at the same time.

RESET

Fully remove the domain.com.dnsprovider or related dnsprovider.conf. Future letsencrypt.sh requests.

CMD_SSL
method: POST
action=dnsprovider
dnsprovider_reset=yes
domain=domain.com
type=domain|creator|global

which simply deletes the given dnsprovider.conf file, preventing future use by the letsencrypt.sh

VIEW with Certificate info

To get the list of supported DNS providers within the SSL Certificates pull, include &dnsproviders=yes in the request, e.g., CMD_SSL?domain=domain.com&dnsproviders=yes&json=yes

However, using the action=dnsproviders method above, will allow better control (recommended).

It will be loaded in a top-level array called: dnsproviders["data"] = { "version" : "3.7.0", "acme-dns": ...}

Similar to action=dnsproviders, see above.

Any info about the current domain settings will be in: dnsproviders["dnsprovider"] where the dnsprovider (singular) is a dump of the domain.com.dnsprovider file, used to auto-fill the pre-selected choice. If this file contains dnsprovider=inherit-creator or dnsprovider=inherit-global, it will use the respective dnsprovider.conf file.

USERS: POST PARAMETERS FOR SPECIFYING A DNSPROVIDER VS INHERITING

When saving data for a LetsEncrypt request for Users, include dnsprovider=NAME to activate the rest of the checks, e.g.,

CMD_SSL
method: POST
domain=domain.com
action=save
background=auto
type=create
request=letsencrypt
name=domain.com
wildcard=yes
keysize=secp384r1
encryption=sha256
le_wc_select0=domain.com
le_wc_select1=*.domain.com
submit=Save

dnsprovider=cloudflare
CLOUDFLARE_EMAIL=foo@bar.com
CLOUDFLARE_API_KEY=sdgsd7681afn

for example, assuming cloudflare is the desired remote dnsprovider.

Although this should work fine, as mentioned before, we recommend using a POST to the action=dnsprovider method with CMD_SSL for better control, insteada of this action=save method. Please report any bugs to DirectAdmin Support.

ONLY SAVE DNSPROVIDER INFO

If you wish to only save the dnsprovider info, use:

CMD_SSL
method: POST
domain=domain.com
action=save
type=dnsprovider
dnsprovider=cloudflare
CLOUDFLARE_EMAIL=foo@bar.com
CLOUDFLARE_API_KEY=sdgsd7681afn

Please use action=dnsprovider, above, if possible.

RESET

To remove the domain.com.dnsprovider file (thus resetting the dnsprovider to Local), include: dnsprovider_reset=yes to either of the above requests. Neither dnsprovider= nor its related fields are needed when dnsprovider_reset=yes is passed.

For example,

CMD_SSL
method: POST
domain=domain.com
action=save
type=dnsprovider
dnsprovider_reset=yes

Please use action=dnsprovider, above, if possible.

USER / DOMAIN DATA FILES AND CONTENT

When a selection is made by a User (or via a creator's default choice), the domain's DNS settings will be stored in: /usr/local/directadmin/data/users/USERNAME/domains/DOMAIN.COM.dnsprovider

Sample data:

dnsprovider=cloudflare
CLOUDFLARE_EMAIL=foo@bar.com
CLOUDFLARE_API_KEY=sdgsd7681afn

OR:

dnsprovider=inherit-creator

OR:

dnsprovider=inherit-global

The data from any of the 3 possible file locations is loaded into the ENV and passed onto the letsencrypt.sh script. Eg: If the domain.com.dnsprovider contains an inherit type, the letsencrypt.sh will go find the respective dnsprovider.conf.

Automatically redirect domain/pointer to SSL

Since a lot of domains use .htaccess for SSL redirection this feature is disabled by default to prevent loops. However, DirectAdmin SSL redirection comes directly from webserver so it will be faster than using .htaccess. Each user may individually enable redirection under:

User Level -> Domain Setup -> domain.com -> Force Redirect

Available redirection options:

  • www
  • non-www
  • none

Pointers will also use this setting. You may find more information about this feature hereopen in new window.

Automatically set up Let's Encrypt SSL for all domains that do not currently have a certificate

The new Let's Encrypt feature is a great way to easily secure your website connections at no cost. If you already have many websites that you want to secure all at once, you can use the autoletsencrypt.sh script to do this.

After you've enabled Let's Encrypt on the system, you can install certificates for all domains using the following script:

cd /root
wget -O autoletsencrypt.sh http://files.directadmin.com/services/all/letsencrypt/autoletsencrypt.sh
chmod 755 autoletsencrypt.sh
./autoletsencrypt.sh

Note that if you have too many domains, you might hit the Let's Encrypt Rate-Limitopen in new window so if that happens, you should be able to wait until the time window has passed, run it again, which should continue where it left off.

Swapping cPanel SSLs on Domains Transferred from cPanel for Let's Encrypt SSLs Managed by DirectAdmin

If you've recently migrated from cPanel to DirectAdmin, you will need to convert your cPanel/Sectigo SSLs to Let's Encrypt SSLs.

A script from a DirectAdmin forum postopen in new window will create all necessary files so that DirectAdmin can manage automatic Let's Encrypt SSL renewals for your migrated domains.

Running the script in the post will create all necessary files needed for DirectAdmin to manage the SSLs. You may also want to go ahead and proceed with ensuring all domains have valid SSLs by using the aforementioned autoletsencrypt.sh script from the above guide. You may also want to check that the hostname SSL is valid and will be autorenewed as well.

Combine every check into one single script that you can run following a migration from cPanel to DirectAdmin like so:

#!/bin/bash
#Convert cPanel SSLs to Let's Encrypt SSLs Managed by DirectAdmin
for i in `cat /etc/virtual/domainowners | cut -d: -f1`; do { 
    USER=`grep "^${i}:" /etc/virtual/domainowners | awk '{print $2}'`;
    CERT_PATH=/usr/local/directadmin/data/users/${USER}/domains/${i}.cert
    if [ -s ${CERT_PATH} ]; then
        if openssl x509 -issuer -in ${CERT_PATH} -noout | grep -m1 -q "cPanel"; then
            CERT_DATE="`openssl x509 -startdate -in ${CERT_PATH} -noout | cut -d= -f 2`"
            TIMESTAMP="`date --date=\"${CERT_DATE}\" +%s`"
            TIMESTAMP_LENGTH="`echo \"${TIMESTAMP}\" | wc -c`"
            if [ ! -s /usr/local/directadmin/data/users/${USER}/domains/${i}.cert.creation_time ]; then
                echo "Setting up ${i} (owned by ${USER}/) for autorenewal..."
                if [ ${TIMESTAMP_LENGTH} -gt 10 ]; then
                    echo "${TIMESTAMP}" > /usr/local/directadmin/data/users/${USER}/domains/${i}.cert.creation_time
                else
                    echo "0" > /usr/local/directadmin/data/users/${USER}/domains/${i}.cert.creation_time
                fi
            fi
            if [ ! -s /usr/local/directadmin/data/users/${USER}/domains/${i}.san_config ]; then
                SAN_CN="`openssl x509 -noout -subject -in ${CERT_PATH} | cut -d= -f3`"
                SAN_NAMES="`openssl x509 -noout -text -in ${CERT_PATH} | grep -m1 -A1 'Subject Alternative Name' | grep -o 'DNS:.*'`"
                cat <<< "
[ req ]
default_bits        = 4096
default_keyfile        = keyfile.pem
distinguished_name    = req_distinguished_name
attributes        = req_attributes
output_password        = bogus


[ req_distinguished_name ]
CN            = ${SAN_CN}
[ req_attributes ]
[ SAN ]
subjectAltName=${SAN_NAMES}" > /usr/local/directadmin/data/users/${USER}/domains/${i}.san_config
            fi
        fi
    fi
}; 
done


#run autoletsencrypt.sh for all domains to ensure any needed SSLs are installed as should be.
printf "Ensuring all migrated domains have valid SSLs installed...\n"
wget -O /root/autoletsencrypt.sh http://files.directadmin.com/services/all/letsencrypt/autoletsencrypt.sh
chmod 755 /root/autoletsencrypt.sh
sh /root/autoletsencrypt.sh


#enable hostname SSL if not present or expired
printf "Ensuring the hostname SSL is valid and will be autorenewed...\n"
HOSTCACERT=`/usr/local/directadmin/directadmin config | grep 'cacert.pem' | cut -d= -f2`

#request hostname SSL (note: doesn't affect self-signed SSLs, so another check would be needed for this)
if [[ `openssl x509 -checkend 0 -in $HOSTCACERT` != "Certificate will not expire" ]];then
  /usr/local/directadmin/scripts/letsencrypt.sh server_cert
fi

#ensure creation_time file exists for hostname autorenewal...
if [ ! -s $HOSTCACERT.creation_time ]; then
  echo "Setting up the hostname SSL for autorenewal..."
  echo "0" > $HOSTCACERT.creation_time
fi

exit 0

Save the script as ssl.sh, make executable and run it:

chmod 755 ssl.sh
./ssl.sh

Create new User with ssl on, but with ssl off for the new domain

In some cases, you might want to create a new User and allow that User the ability to use SSL and SSL certificates, but don't want it turned on by default for the domain, to keep the VirtualHost count down.

This can be done with the user_create_post_confirmed.sh scriptopen in new window.

So create /usr/local/directadmin/scripts/custom/user_create_post_confirmed.sh with code:

#!/bin/sh
CONF=/usr/local/directadmin/data/users/$username/domains/$domain.conf
if [ -s $CONF ]; then
   perl -pi -e 's/ssl=ON/ssl=OFF/' $CONF
   echo "action=rewrite&value=httpd&user=$username" >> /usr/local/directadmin/data/task.queue
fi
exit 0;

and make it executable:

chmod 755 /usr/local/directadmin/scripts/custom/user_create_post_confirmed.sh

This will shut off SSL for the new domain after the User is created, and rewrite their httpd.conf file up to 1 minute later via task.queue (in case you're wondering why the 443 VH might still be added immediately after User creation).

Enforcing ssl=ON for all Users, Resellers and their packages

For the modern age, websites should be using SSL for their websites. This guide provides a means to enforce enabling it for all Users/Resellers, their domains, and the packages used to create them.

First, we'll setup the enforcement for actions in the GUI.

Saving a package

Create the directory and script:

/usr/local/directadmin/scripts/custom/package_write_pre/enforce_ssl.sh

with code:

#!/bin/sh
if [ "${ssl}" = "OFF" ]; then
   echo "SSL must be enabled";
   exit 1;
fi
exit 0;

Account creation/modification

Create both:

/usr/local/directadmin/scripts/custom/user_create_pre/enforce_ssl.sh

and

/usr/local/directadmin/scripts/custom/user_modify_pre/enforce_ssl.sh

with the same code as above.
Chmod all scripts to 755:

chmod 755 /usr/local/directadmin/scripts/custom/*/enforce_ssl.sh

Change all Users, domains, and packages to use ssl=ON:

cd /usr/local/directadmin/data
perl -pi -e 's/^ssl=OFF/ssl=ON/' users/*/user.conf
perl -pi -e 's/^ssl=OFF/ssl=ON/' users/*/domains/*.conf
perl -pi -e 's/^ssl=OFF/ssl=ON/' users/*/reseller.conf
perl -pi -e 's/^ssl=OFF/ssl=ON/' admin/packages/*.pkg
perl -pi -e 's/^ssl=OFF/ssl=ON/' users/*/packages/*.pkg

Follow up with a rewrite of the webserver configs to use the values:

cd /usr/local/directadmin/custombuild
./build rewrite_confs

Let's Encrypt weekly domain rate limit

LetsEncrypt has a rate limit of 50 requests per week, per main domain. Read more hereopen in new window

DirectAdmin has this directadmin.conf option:

letsencrypt_max_requests_per_week=200

The 50 LE limit is only requests that make it that far. The letsencrypt.sh script has some prechecks which could fail before the LE limit, so the 200 limit is used to err on the side of caution to reach the actual LE 50 limits.

In effect, the letsencrypt_max_requests_per_week=200 value is mainly for a last-resort limit on "far too many requests", vs trying to predict the true LE 50 limit.

DISABLE

You can fully disable the DA limit enforcement by setting:

/usr/local/directadmin/directadmin set letsencrypt_max_requests_per_week 0
service directadmin restart

FILES

/usr/local/directadmin/data/admin/letsencrypt_rate_limits/DOMAIN.COM/weekly_domain_count

which will be a JSON file, storing each request attempt.

Future requests will read, reduce old entries, and add new ones as needed. If there are 200 or more entries after reduction, the request will not happen and the new request will not be added to the file.

NIGHTLY TALLY

Each tally will see if the age of the weekly_domain_count for each main domain is older than 1 week and will fully remove the domain.com directory if it is.

USERS

These are cross-User files, meaning, if

  • User bob has domain.com
  • User fred has sub.domain.com

both Users will have their counts stored in the same domain.com/weekly_domain_count file.

If you run many subdomains on the system, it would be highly advisable to request a wildcard for domain.com, so that sub.domain.com does not need to create any requests, as the limit would be reached quite quickly.

Last Updated: