ACME Configuration#
The ACME module in Angie enables automatic certificate acquisition using the ACME protocol. The ACME protocol supports various domain verification methods (also called "validation"); this module implements HTTP validation, DNS validation, and hook-based validation through a custom external service.
Configuration Steps#
General steps to enable certificate requests in the configuration:
Configure an ACME client in the
http
block using the acme_client directive, which specifies a unique client name and other parameters. Multiple ACME clients can be configured.Specify the domains for which certificates are requested: A single certificate will be issued for all domain names listed in server_name directives within all
server
blocks that use acme, pointing to the same ACME client.-
Set up request handling and ACME callbacks: This is required to verify domain ownership. The setup depends on the chosen domain validation method:
Method
Requirements
Certificates
Requires responding to a specific request (callback) from the ACME server.
Port 80 must be open on the Angie server.
Does not support wildcard certificates.
Allows issuing multi-domain certificates.
Requires handling specific DNS queries (callbacks) from the ACME server.
Requires the ability to modify DNS records.
The Angie server must have a port open as specified by the acme_dns_port directive (default is 53).
Supports issuing both multi-domain and wildcard certificates.
Requires implementing an external service that Angie communicates with.
Requires an external DNS or HTTP server configured by the external service.
The requirements for the external DNS or HTTP server match the respective points above.
Supports issuing both multi-domain and wildcard certificates.
-
Configure SSL using the obtained certificate and key: Certificates and keys are made available as embedded variables that can be used in configuration to populate ssl_certificate and ssl_certificate_key.
For SSL setup instructions, refer to SSL Configuration.
Implementation Details#
Client keys and certificates are stored in PEM encoding within subdirectories of the
directory specified by the --http-acme-client-path
build option:
$ ls /var/lib/angie/acme/example/
account.key certificate.pem private.key
The ACME client requires an account on the CA server, managed using a private
key (account.key
). If no key exists, it is generated at startup. The
client registers the account with the CA server using this key.
Note
If you already have an account key, place it in the client's subdirectory
before starting to reuse the account. Alternatively, specify the key using
the account_key
parameter in acme_client.
The client also uses a separate key (private.key
) for Certificate
Signing Requests (CSRs). This key is automatically created if needed. At
startup, the client requests a certificate by signing and sending a CSR for all
domains under its management to the CA server.
The CA server verifies domain ownership using HTTP or
DNS validation and issues a certificate, which is saved
locally (certificate.pem
).
As mentioned earlier, a single certificate covers all domains managed by the same ACME client. The list of all covered names can be found in the Subject Alternative Name (SAN) field of the certificate. To check this in the terminal:
$ openssl x509 -in certificate.pem -noout -text | grep -A5 "Subject Alternative Name"
When a certificate is about to expire or domain changes occur, the client submits a new CSR, and the CA server re-verifies ownership before issuing a new certificate.
The obtained certificate and key are accessible via prefixed variables $acme_cert_<name> and $acme_cert_key_<name>, which refer to the respective files. Use these with ssl_certificate and ssl_certificate_key:
server {
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
HTTP Validation#
Validation is handled automatically. The process involves the ACME server, upon
receiving a request, retrieving a special file token via HTTP from the address
/.well-known/acme-challenge/<TOKEN>
. The ACME module intercepts and
processes such requests automatically. When the expected response with the
correct content is received, the ACME server confirms domain ownership.
Configuration Example#
In this example, the ACME client named example
manages the certificates
for example.com
and www.example.com
(note that wildcards aren't
supported with HTTP validation):
http {
resolver 127.0.0.53; # Required for the 'acme_client' directive
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80; # May reside in a different 'server' block
# with a different domain list
# or even without one
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
As noted earlier, port 80 must be open to handle HTTP callbacks from ACME. However, as shown in this example, the listen directive for this port can be placed in a separate server block. If no existing block with this directive is present, a new block limited to ACME callbacks can be created:
server {
listen 80;
return 444; # No response, connection closed
}
Why does this work? The module intercepts requests to
/.well-known/acme-challenge/<TOKEN>
after reading headers but before
selecting a virtual server or processing rewrite and location
directives. Such intercepted requests are processed if the value of
<TOKEN>
matches the expected value for the specific callback. No
directory access is performed; the module handles the request entirely.
DNS Validation#
Validation is handled automatically. When processing a certificate request, the
ACME server performs a special DNS query to the
_acme-challenge.
subdomain of the domain being verified. Once the
expected response is received, the ACME server confirms domain ownership.
The ACME module tracks and processes such requests automatically, provided that
your DNS records are configured properly to designate the Angie server as the
authoritative name server for the _acme-challenge.
subdomain.
For example, to verify the domain example.com
using an Angie server at
IP address 93.184.215.14
, your domain's DNS configuration should include
the following records:
_acme-challenge.example.com. 60 IN NS ns.example.com.
ns.example.com. 60 IN A 93.184.215.14
This configuration delegates DNS resolution for
_acme-challenge.example.com
to ns.example.com
, ensuring the
availability of ns.example.com
via its IP address
(93.184.215.14
).
This method allows requesting wildcard certificates, e.g. a certificate that
will include the entry *.example.com
in the Subject Alternative Name
(SAN) section. To explicitly request a certificate for a subdomain, such as
www.example.com
, you must separately verify that subdomain using the
method described above.
Attention
The applicability of this scenario largely depends on the capabilities of your DNS provider; some providers do not allow such configurations.
Configuration Example#
The configuration is generally similar to the example in the previous section.
There is no need for HTTP-specific settings; instead, set challenge=dns
in the acme_client directive.
In this example, the ACME client named example
manages the certificates
for example.com
and *.example.com
:
http {
resolver 127.0.0.53;
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
server {
server_name example.com *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
Hook-Based Validation#
Unlike the previous methods, this validation requires additional effort. The ACME server performs a standard HTTP validation or DNS validation, but instead of interacting directly with the Angie server, it communicates with an external service managed by the Angie server using hooks (acme_hook). This service configures a separate DNS or HTTP server where the ACME server sends its requests.
Once the ACME server receives the expected response from the configured DNS or HTTP server, it confirms domain ownership.
The external service is invoked by Angie when it needs to perform certificate renewal under the ACME protocol. Requests and data are proxied to FastCGI, SCGI, or similar servers using directives such as fastcgi_pass, scgi_pass, etc.
The service must return a 2xx
status code, which can be sent via the
Status
header. Any other code is treated as an error, and certificate
renewal is halted. Output from the service is ignored.
Configuration Example#
In this example, the ACME client example
is configured to use DNS-based
validation, indicated by the challenge=dns
parameter in the
acme_client
directive.
A server
block applies to all subdomains of example.com
(e.g.,
*.example.com
) and uses the ACME client example
to manage
certificates, as specified by the acme
directive.
A named location
block is set up to handle external service calls for
DNS validation. The acme_hook
directive links the server to the ACME
client example
. Requests are proxied to a local FastCGI server running
on port 9000 using the fastcgi_pass
directive. The fastcgi_param
directives pass data about the ACME client, hook, challenge type, domain, token,
and key authorization to the external service.
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
server {
listen 80;
server_name *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location @acme_hook_location {
acme_hook example;
fastcgi_pass localhost:9000;
fastcgi_param ACME_CLIENT $acme_hook_client;
fastcgi_param ACME_HOOK $acme_hook_name;
fastcgi_param ACME_CHALLENGE $acme_hook_challenge;
fastcgi_param ACME_DOMAIN $acme_hook_domain;
fastcgi_param ACME_TOKEN $acme_hook_token;
fastcgi_param ACME_KEYAUTH $acme_hook_keyauth;
include fastcgi.conf;
}
}
The following Perl script demonstrates a simple external FastCGI service:
#!/usr/bin/perl
use strict; use warnings;
use FCGI;
my $socket = FCGI::OpenSocket(":9000", 5);
my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket);
while ($request->Accept() >= 0) {
print "\r\n";
my $client = $ENV{ACME_CLIENT};
my $hook = $ENV{ACME_HOOK};
my $challenge = $ENV{ACME_CHALLENGE};
my $domain = $ENV{ACME_DOMAIN};
my $token = $ENV{ACME_TOKEN};
my $keyauth = $ENV{ACME_KEYAUTH};
if ($hook eq 'add') {
DNS_set_TXT_record("_acme-challenge.$domain.", $keyauth);
} elsif ($hook eq 'remove') {
DNS_clear_TXT_record("_acme-challenge.$domain.");
}
};
FCGI::CloseSocket($socket);
Here, DNS_set_TXT_record()
and DNS_clear_TXT_record()
are
functions assumed to add and remove TXT records in the configuration of an
external DNS server that the ACME server queries. These records must contain the
data provided by the Angie server to allow successful validation, similar to the
process described in DNS Validation. The implementation details of such
functions are beyond the scope of this guide.
Migrating from certbot#
If you previously used certbot to obtain and renew SSL certificates from Let's Encrypt while using nginx, follow these steps to transition to using the ACME module.
Suppose you initially configured certificates with the following command:
$ sudo certbot --nginx -d example.com -d www.example.com
The configuration automatically created by certbot is typically located in
/etc/nginx/sites-available/example.conf
and looks something like this:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
try_files $uri $uri/ =404;
}
}
In the example above, the highlighted lines must be modified. Depending on your circumstances and preferences, configure HTTP validation or DNS validation using the ACME module.
The resulting Angie configuration might look like this:
http {
resolver 127.0.0.53;
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80;
return 444;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location / {
try_files $uri $uri/ =404;
}
}
}
Remember to reload the configuration after making changes:
$ sudo angie -s reload
Once the new configuration is verified, you can delete the certbot certificates and optionally disable or remove certbot entirely, if it is no longer used elsewhere:
$ sudo rm -rf /etc/letsencrypt
$ sudo systemctl stop certbot.timer
$ sudo systemctl disable certbot.timer
$ # -- or --
$ sudo rm /etc/cron.d/certbot
$ sudo apt remove certbot
$ # -- or --
$ sudo dnf remove certbot