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.
- Enable the letsencrypt=1 option in the directadmin.conf
- If you want wildcard domain SSL certificate support via Let's Encrypt, ensure dns_ttl=1 is set in the directadmin.conf.
- Remote DNS providers are now supported via LEGO.
- Restart DirectAdmin:
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 script 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:
- 1.66.8: Addition of GUI to Enhanced
- 1.66.4: DNS Provider for Hostname
- 1.62.1: Backup/Restore of dnsprovider
- 1.61.4: First addition of dnsproviders
INSTALL
Ensure that wildcard domain SSL certificate support via Let's Encrypt 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 here.
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-Limit 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 post 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 script.
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 here
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.