ACME client Lego - WildCard SSL
A detailed guide for deploying a star WildCard SSL certificate via the ACME client Lego and API DNS validation with the VEDOS web hosting. The procedure is intended for certificates of the type example.com and *.example.com, where renewal should be automatic without manually entering TXT records. The guide uses an ACME certificate from the Certum certificate authority. The certificate used serves only as an example – the operating principle and the ACME deployment procedure are the same for all certificate authorities.
The guide uses syntax verified on Lego 5.2.2. Lego v5 changed some parameters compared to older versions, so in case of an error such as flag provided but not defined verify the correct syntax using lego accounts register --help, lego run --help or lego --help.
Article contents
- Lego installation
- DNS API provider
- Lego configuration files
- Certificate issuance
- Deployment to Apache
- Automatic renewal
- Common errors
Basic concepts
- ACME – protocol for automated issuance and renewal of SSL/TLS certificates.
- Lego – an ACME client written in Go. It can perform DNS validation through many DNS providers (list of supported DNS providers).
- DNS-01 – validation via the DNS TXT record
_acme-challenge. It is required for WildCard certificates. - EAB kid + hmac – External Account Binding (EAB) details from the certificate authority. They link ACME client to an account or product.
- VEDOS WAPI – the VEDOS API interface through which Lego creates and deletes DNS TXT records.
- Systemd service - a configuration file that tells the Linux system how to start an application and keep it running even after a server restart.
In all the examples shown, replace the domain example.com with your own domain.
Lego installation
apt update
apt install -y curl tar
cd /tmp
LEGO_URL=$(curl -s https://api.github.com/repos/go-acme/lego/releases/latest | sed -n 's/.*"browser_download_url": "\(.*linux_amd64.tar.gz\)".*/\1/p' | head -n1)
echo "$LEGO_URL"
curl -L -o lego.tar.gz "$LEGO_URL"
tar -xzf lego.tar.gz
install -m 0755 lego /usr/local/bin/lego
lego --version
After a successful installation, we recommend removing the temporary files.
rm -f /tmp/lego /tmp/lego.tar.gz /tmp/LICENSE /tmp/CHANGELOG.md
| Command / value | What it does / what to replace |
|---|---|
apt update |
Updates the package list. |
apt install -y curl tar |
Installs the tools for downloading and extracting Lego. |
LEGO_URL=... |
Finds the URL of the latest Linux amd64 release package. |
curl -L -o lego.tar.gz |
Downloads the Lego archive. |
tar -xzf lego.tar.gz |
Extracts the archive. |
install -m 0755 lego /usr/local/bin/lego |
Installs Lego as an executable system command. |
lego --version |
Verifies the installed version of Lego. |
DNS API provider
This guide uses the DNS API from the domain registrar Vedos, which offers an API for managing the DNS of registered domains. For Vedos web hosting, you need to activate WAPI and also fill in the allowed IP addresses and the WAPI password.
The LEGO client supports hundreds of other DNS providers.
You can find their list on the LEGO website - list of supported DNS providers.
IP addresses of the VPS server
curl -4 ifconfig.me
curl -6 ifconfig.me
| Command / value | What it does / what to replace |
|---|---|
curl -4 ifconfig.me |
Shows the server's public IPv4 address, which needs to be allowed in VEDOS WAPI. |
curl -6 ifconfig.me |
Shows the server's public IPv6 address, if the VPS uses one. It is advisable to allow this address in VEDOS WAPI as well. |
In the Allowed IP addresses field, enter all outgoing IP addresses of your server, typically both IPv4 and IPv6. The values are separated by a space. VEDOS allows API requests only from the listed IP addresses.
Important: If you allow only IPv4 and some API request goes out over IPv6, the certificate issuance may succeed, but the cleanup of TXT records will fail with the error Access not allowed from this IP address.
Recommended values for the VEDOS DNS provider
| Field | Recommended value |
|---|---|
| Activate WAPI | On |
| Allowed IP addresses | The VPS's public IPv4 and possibly IPv6 address |
| Notification method | POLL queue |
| Preferred protocol | JSON |
| Password | The generated WAPI password, not the ordinary administration password |
Apache, webroot
The basic Apache setup is a supporting part. DNS validation runs through the DNS API, not over HTTP, but the Apache vhost is needed to serve the website after the certificate is issued.
›› Show/Hide sectionBefore running, replace the value example.com in the line DOMAIN="example.com" with your own domain without the asterisk. The variable $DOMAIN is then used in the following commands for paths, the Apache vhost and the test page.
cd /var/www
apt update
apt install -y apache2
systemctl enable --now apache2
a2enmod rewrite headers ssl
systemctl reload apache2
DOMAIN="example.com"
mkdir -p /var/www/$DOMAIN/public
chown -R www-data:www-data /var/www/$DOMAIN
chmod -R 755 /var/www/$DOMAIN
echo "OK $DOMAIN" > /var/www/$DOMAIN/public/index.html
| Command / value | What it does / what to replace |
|---|---|
cd /var/www |
Changes to the directory where web files are usually stored. |
apt update |
Updates the package list. |
apt install -y apache2 |
Installs Apache; -y automatically confirms the installation. |
systemctl enable --now apache2 |
Enables Apache at server startup and starts it at the same time. |
a2enmod rewrite headers ssl |
Enables modules for redirects, headers and HTTPS. |
DOMAIN="example.com" |
Sets the domain variable. Replace example.com with your own domain. |
mkdir/chown/chmod/echo |
Creates the webroot, sets permissions for Apache and saves a simple test page. |
HTTP vhost for both the apex and subdomains:
cat > /etc/apache2/sites-available/$DOMAIN.conf <<EOF
<VirtualHost *:80>
ServerName $DOMAIN
ServerAlias *.$DOMAIN
DocumentRoot /var/www/$DOMAIN/public
<Directory /var/www/$DOMAIN/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/${DOMAIN}_error.log
CustomLog \${APACHE_LOG_DIR}/${DOMAIN}_access.log combined
</VirtualHost>
EOF
a2ensite $DOMAIN.conf
apache2ctl configtest
systemctl reload apache2
curl -I http://$DOMAIN
| Command / value | What it does / what to replace |
|---|---|
cat > ... <<EOF |
Writes a new Apache HTTP vhost to a file in sites-available. |
ServerName $DOMAIN |
The main domain of the virtual host. |
ServerAlias *.$DOMAIN |
Allows handling of any first-level subdomain. |
DocumentRoot |
The directory from which Apache serves content. |
a2ensite $DOMAIN.conf |
Enables the vhost. |
apache2ctl configtest |
Verifies the Apache configuration syntax. |
curl -I http://$DOMAIN |
Verifies the domain's HTTP response. |
Lego configuration files
The recommended approach for Lego v5 is to store the settings in a configuration file. The systemd service then does not need to contain a long command with domains, the DNS provider and hooks.
Configuration file .env
The .env file is a text configuration file in which environment variables are stored, for example access credentials, API keys or application settings. For clarity, you can name the file provider-domain.env. The vedos-example.com.env file will contain the VEDOS WAPI login credentials, so we store it in /etc/lego and set restricted permissions on it.
DOMAIN="example.com"
mkdir -p /etc/lego/$DOMAIN
nano /etc/lego/vedos-$DOMAIN.env
| Command / value | What it does / what to replace |
|---|---|
DOMAIN="example.com" |
Sets the domain for the following commands. Replace with your own domain. |
mkdir -p /etc/lego/$DOMAIN |
Creates the directory for the Lego data and configuration of the given domain. |
nano /etc/lego/vedos-$DOMAIN.env |
Opens the file for the VEDOS API variables. |
In the configuration below, replace WEDOS_LOGIN with your VEDOS login and WEDOS_WAPI_PASSWORD with the password generated in VEDOS WAPI. You can leave the timeout and interval values as they are.
WEDOS_USERNAME='WEDOS_LOGIN'
WEDOS_WAPI_PASSWORD='WEDOS_WAPI_PASSWORD'
WEDOS_PROPAGATION_TIMEOUT=3600
WEDOS_POLLING_INTERVAL=30
WEDOS_TTL=300
| Command / value | What it does / what to replace |
|---|---|
WEDOS_USERNAME |
The VEDOS login of the account that manages the DNS zone. |
WEDOS_WAPI_PASSWORD |
The WAPI password generated in the VEDOS administration. |
WEDOS_PROPAGATION_TIMEOUT |
The maximum wait time for DNS propagation in seconds. |
WEDOS_POLLING_INTERVAL |
The interval between DNS propagation checks. |
WEDOS_TTL |
The TTL of the TXT records created for the ACME challenge. |
chmod 600 /etc/lego/vedos-$DOMAIN.env
Configuration file lego.yml
The .yml file is a text configuration file in YAML format, used for a clear notation of settings, parameters and structured data. Before saving the YAML configuration, replace example.com with your own domain, *.example.com with the wildcard name, your@email.com with your contact e-mail and the KID / HMAC values with the details from your ACME certificate order. Names such as certum-example or example-com-wildcard are internal labels; you can leave them, but with multiple domains it is advisable to rename them according to the domain.
mkdir /etc/lego/$DOMAIN
nano /etc/lego/$DOMAIN/lego.yml
storage: /etc/lego/example.com
accounts:
certum-example:
server: certum
email: your@email.com
acceptsTermsOfService: true
eab:
kid: KID
hmacKey: HMAC
servers:
certum:
url: https://acme.certum.pl/directory
challenges:
vedos-dns:
dns:
provider: vedos
envFile: /etc/lego/vedos-example-com.env
resolvers:
- 1.1.1.1:53
certificates:
example-com-wildcard:
account: certum-example
challenge: vedos-dns
domains:
- example.com
- "*.example.com"
renew:
days: 30
hooks:
deploy:
command: systemctl reload apache2
| Command / value | What it does / what to replace |
|---|---|
storage |
Directory for the Lego account, certificates and metadata. |
accounts |
Definition of the ACME account including the e-mail and EAB details. |
servers.certum.url |
The Certum ACME endpoint. |
challenges.vedos-dns |
DNS-01 validation through the VEDOS provider. |
envFile |
The file with the VEDOS API login credentials. |
certificates |
List of certificates that Lego should manage. |
domains |
The apex domain and the wildcard domain in the certificate. |
renew.days |
How many days before expiration Lego should renew. |
hooks.deploy.command |
Command after a successful issuance or renewal, here reloading Apache. |
chmod 600 /etc/lego/$DOMAIN/lego.yml
The lego.yml file contains the EAB HMAC, so it must have restricted permissions. In customer documentation, use only placeholders.
Certificate issuance
Before running, replace example.com in the path with the domain you used when creating the directory. The first run creates the ACME account, sets the DNS TXT records through the DNS API, performs DNS-01 validation and saves the certificate.
lego --config /etc/lego/$DOMAIN/lego.yml
While waiting, Lego may print:
dns01: waiting for record propagation timeout=1h0m0s interval=30s
| Command / value | What it does / what to replace |
|---|---|
lego --config |
Runs Lego according to the configuration file. On the first run it issues the certificate, on subsequent runs it handles renewal. |
dns01: waiting for record propagation |
Lego has created the TXT record and is waiting until it is visible in DNS. |
timeout=1h0m0s |
Waits at most one hour. |
interval=30s |
Checks DNS every 30 seconds. |
This means that Lego checks DNS every 30 seconds and waits at most 1 hour. After success, verify the files:
ls -la /etc/lego/$DOMAIN/certificates/
The certificates/ directory contains the issued .crt, .key, intermediate certificates of the certificate authority and metadata.
Alternative CLI procedure for Lego v5
›› Show/Hide sectionIf you do not use a configuration file, in Lego v5 the EAB is entered during account registration. Before running, replace example.com with your own domain, your@email.com with your own e-mail and KID / HMAC with the values from your order.
lego accounts register \
--path /etc/lego/example.com \
--server https://acme.certum.pl/directory \
--email your@email.com \
--accept-tos \
--eab \
--eab.kid 'KID' \
--eab.hmac 'HMAC'
| Command / value | What it does / what to replace |
|---|---|
lego accounts register |
Registers the ACME account manually via the CLI without lego.yml. |
--path |
Directory for the account and certificates. |
--server |
Certum ACME endpoint. |
--email |
Contact e-mail. |
--accept-tos |
Agreement with the terms of service. |
--eab |
Enables External Account Binding. |
--eab.kid / --eab.hmac |
EAB details from CertManager. |
Listing of accounts. In the path, use the same domain as in the previous command again:
lego accounts list --path /etc/lego/example.com
Certificate issuance now without EAB parameters. Replace example.com with your own domain and *.example.com with the wildcard name.
set -a
. /etc/lego/vedos-example.com.env
set +a
lego run \
--path /etc/lego/example.com \
--server https://acme.certum.pl/directory \
--email your@email.com \
--dns vedos \
--dns.resolvers 1.1.1.1:53 \
--domains example.com \
--domains '*.example.com'
| Command / value | What it does / what to replace |
|---|---|
set -a |
Automatically exports the variables loaded from the file. |
. /etc/lego/vedos-example.com.env |
Loads the VEDOS API variables into the current shell. |
set +a |
Turns off automatic export of variables. |
lego run |
Issues or renews the certificate without a configuration file. |
--dns vedos |
Uses the DNS API. |
--domains |
The domains that will be in the certificate. |
Deploying the certificate to Apache
Before creating the HTTPS vhost, replace example.com with your own domain in the file name, the ServerName and ServerAlias values, the webroot paths and the certificate paths. These paths must match the domain used in the Lego configuration.
cat > /etc/apache2/sites-available/example.com-le-ssl.conf <<'EOF'
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName example.com
ServerAlias *.example.com
DocumentRoot /var/www/example.com/public
<Directory /var/www/example.com/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/lego/example.com/certificates/example.com.crt
SSLCertificateKeyFile /etc/lego/example.com/certificates/example.com.key
ErrorLog ${APACHE_LOG_DIR}/example.com_ssl_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_ssl_access.log combined
</VirtualHost>
</IfModule>
EOF
a2ensite example.com-le-ssl.conf
apache2ctl configtest
systemctl reload apache2
curl -I https://example.com
curl -I https://test.example.com
| Command / value | What it does / what to replace |
|---|---|
cat > ...-le-ssl.conf |
Creates the Apache HTTPS vhost. |
ServerName / ServerAlias |
Specifies the apex domain and the wildcard subdomains. |
SSLCertificateFile |
Path to the certificate from Lego. |
SSLCertificateKeyFile |
Path to the private key from Lego. |
a2ensite |
Enables the HTTPS vhost. |
systemctl reload apache2 |
Reloads the new Apache configuration. |
curl -I https://... |
Verifies the HTTPS response. |
Automatic renewal
Lego can renew the certificate, but after installation it does not create a systemd timer on its own. Regular execution is set up via a custom service and timer. Before inserting, replace example-com in the service/timer name with your own safe name without dots, for example mojedomena-cz, and replace example.com in the configuration path with your own domain.
cat > /etc/systemd/system/lego-example-com-renew.service <<'EOF'
[Unit]
Description=Renew Certum WildCard SSL for example.com using Lego and VEDOS DNS
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/lego --config /etc/lego/example.com/lego.yml
EOF
cat > /etc/systemd/system/lego-example-com-renew.timer <<'EOF'
[Unit]
Description=Daily Lego renewal check for example.com
[Timer]
OnCalendar=*-*-* 03:20:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now lego-example-com-renew.timer
systemctl list-timers | grep lego
| Command / value | What it does / what to replace |
|---|---|
lego-example-com-renew.service |
Systemd service for a one-time run of Lego renew/run. |
Type=oneshot |
The service starts, does its work and finishes. |
ExecStart |
Runs Lego according to lego.yml. |
lego-example-com-renew.timer |
Systemd timer that runs the service regularly. |
OnCalendar |
Time of the daily check. |
RandomizedDelaySec |
Random delay so that the requests do not all start at exactly the same time. |
Persistent=true |
Runs a missed execution after the server starts. |
systemctl enable --now |
Enables the timer and activates it immediately. |
Safe test of the service:
systemctl start lego-example-com-renew.service
journalctl -u lego-example-com-renew.service -n 100 --no-pager
| Command / value | What it does / what to replace |
|---|---|
systemctl start ...service |
Manually runs the renewal service for a test. |
journalctl -u ... |
Shows the service's latest logs. |
If the certificate is not close to expiration, Lego may report that renewal is not needed. This is correct behavior.
Common errors
Unknown parameter in Lego
In Lego v5 the EAB parameters are --eab.kid and --eab.hmac. The parameters always belong to a specific subcommand.
lego accounts register --help
lego accounts list --help
lego run --help
Cleanup of TXT records fails on a disallowed IP
Cleaning up failed ... Access not allowed from this IP address (2a02:...)
Also add the server's IPv6 address to the allowed IP addresses in VEDOS WAPI. The certificate may be issued correctly, but the TXT records will remain in DNS after validation.
Verification checklist
dig TXT _acme-challenge.example.com +short
lego --config /etc/lego/example.com/lego.yml
systemctl status lego-example-com-renew.timer
apache2ctl configtest
curl -I https://example.com
| Command / value | What it does / what to replace |
|---|---|
dig TXT |
Verifies the TXT records in DNS. |
lego --config |
Runs the Lego configuration. |
systemctl status |
Shows the timer status. |
apache2ctl configtest |
Verifies the Apache configuration. |
curl -I |
Verifies the HTTPS response. |
Where to next?
Back to Help
Found an error or don't understand something? Write us!
