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. General steps to enable certificate requests in the configuration: Configure an ACME client in the 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 Set up request handling and ACME callbacks: This is required to verify
domain ownership. The setup depends on the chosen domain validation method: Method User Requirements Multi-domain Wildcard Domains Open port 80 for incoming connections on the Angie server. Open port 53 (or the one specified in acme_dns_port)
for incoming connections on the Angie server. Set an NS record for the Create an external service (script or application)
that can, on request from Angie, update DNS records
or serve a special response via the web server. Configure SSL using the obtained certificate and key: The module makes
certificates and keys 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. Tip The certificate acquisition and renewal process depends on many services
and may take some time. Be patient, and if you encounter problems or have
doubts, check the debug log. Client keys and certificates are stored in PEM encoding within subdirectories of the
directory specified by the The ACME client requires an account on the CA server. To create and manage this
account, the client uses a private 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 file
using the The ACME client also uses a separate key ( At startup, the client requests a certificate if one doesn't exist, signing and
sending a CSR for all domains under its management to the CA server. The server
verifies domain ownership using HTTP or DNS
validation and issues a certificate, which the client saves
locally ( As mentioned earlier, a single certificate covers all domain names managed by
the same ACME client, potentially resulting in a multi-domain certificate. The
list of all names covered by the certificate can be found in the Subject
Alternative Name (SAN) section of the obtained certificate. To check this from
the command line: When a certificate is about to expire or the domain list changes, the client
signs and sends another CSR to the CA server. The server re-verifies ownership
and issues a new certificate, which the client installs locally, replacing the
previous one. In the configuration, the obtained certificate and its
corresponding key are available through the prefix variables
$acme_cert_<name> and $acme_cert_key_<name>. Their values are the
contents of the respective files, which should be used with the
ssl_certificate and ssl_certificate_key directives: Validation is handled automatically. The process involves the ACME server, upon
receiving a request, retrieving a special token file via HTTP from the address
In this example, the ACME client named As noted earlier, port 80 must be open to handle HTTP ACME callbacks. 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, you can create a new block limited to ACME callbacks: Why does this work? The module intercepts requests to
Validation is handled automatically. When processing a certificate request, the
ACME server performs a special DNS query to the
Our 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 For example, to verify the domain This configuration delegates DNS resolution for
This method allows requesting wildcard certificates, e.g., a certificate that
includes the entry Attention The applicability of this scenario largely depends on the capabilities of
your DNS provider; some providers do not allow such configurations. The configuration is generally similar to the example in the previous section.
There is no need for HTTP-specific settings; instead, set In this example, the ACME client named Unlike the previous methods, this validation requires additional effort. The
ACME server performs 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 hook calls (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. Angie invokes the external service when certificate updates are needed via the
ACME protocol. Calls are made by proxying requests and data to FastCGI, SCGI,
and similar servers using directives such as fastcgi_pass,
scgi_pass, etc. The service must return a In this example, the ACME client The A named The following Perl script demonstrates a corresponding external FastCGI service: Here, Another example using PHP-FPM: Parameters passed can be accessed in PHP via The stream module ACME enables automated certificate issuance and
usage for TCP traffic. For proper operation, the HTTP equivalent must be
configured first: the ACME client must be declared in the By default, HTTP validation is used to obtain certificates. As mentioned in
HTTP Validation, this requires an HTTP server listening on port 80: DNS validation can also be used by configuring If you previously used certbot to obtain and renew
SSL certificates from Let's Encrypt before migrating from nginx to Angie,
follow these steps to transition to using the ACME module. Suppose you initially configured certificates with the following command: The configuration automatically created by certbot is typically located in
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: Remember to reload the configuration after making changes: 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:Configuration Steps#
http
block using the
acme_client directive, which specifies a unique client name and other
parameters. Multiple ACME clients can be configured.server
blocks that use acme directives
pointing to the same ACME client._acme-challenge.
subdomain
pointing to your Angie server.Implementation Details#
--http-acme-client-path
build option:$ ls /var/lib/angie/acme/example/
account.key certificate.pem private.key
account.key
). If no key exists,
it is generated at startup. The client then uses this key to register the
account with the server.account_key
parameter in acme_client.private.key
) for Certificate
Signing Requests (CSRs). This certificate key is automatically created at
startup if needed.certificate.pem
).$ openssl x509 -in certificate.pem -noout -text | grep -A5 "Subject Alternative Name"
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#
/.well-known/acme-challenge/<TOKEN>
. Our 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#
example
manages certificates for
example.com
and www.example.com
(note that wildcard certificates
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;
}
}
server {
listen 80;
return 444; # No response, connection closed
}
/.well-known/acme-challenge/<TOKEN>
after reading headers but before
selecting a virtual server and processing rewrite and location
directives. Such intercepted requests are processed if the <TOKEN>
value
matches the expected value for the specific callback. No directory access is
performed; the module handles the request entirely.DNS Validation#
_acme-challenge.
subdomain of the domain being verified. Once the
expected response is received, the ACME server confirms domain ownership._acme-challenge.
subdomain.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
_acme-challenge.example.com
to ns.example.com
, ensuring
ns.example.com
is accessible via its IP address
(93.184.215.14
).*.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.Configuration Example#
challenge=dns
in the acme_client directive.example
manages 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#
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#
example
is configured for domain
verification using DNS callbacks, indicated by the challenge=dns
parameter in the acme_client
directive.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.location
block is configured to handle external service calls
for DNS verification. 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;
}
}
#!/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);
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 the external DNS server to
successfully pass validation, similar to the process described in
DNS Validation. The implementation details of such functions are beyond
the scope of this guide; for example, parameters can also be passed through the
request URI:# ...
location @acme_hook_location {
acme_hook example uri=/acme_hook/$acme_hook_name?domain=$acme_hook_domain&key=$acme_hook_keyauth;
fastcgi_pass localhost:9000;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param ACME_CLIENT $acme_hook_client;
fastcgi_param ACME_CHALLENGE $acme_hook_challenge;
fastcgi_param ACME_TOKEN $acme_hook_token;
include fastcgi.conf;
}
location @acme_hook_location {
acme_hook example;
root /var/www/dns;
fastcgi_pass unix:/run/php-fpm/php-dns.sock;
fastcgi_index hook.php;
fastcgi_param SCRIPT_FILENAME /var/www/dns/hook.php;
include fastcgi_params;
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;
}
[dns]
listen = /run/php-fpm/php-dns.sock
listen.mode = 0666
user = angie
group = angie
chdir = /var/www/dns
# ...
$_SERVER['...']
.ACME in Stream Module#
http
context,
and the stream
block must be placed after the http
block in
the configuration.Configuration Example#
# HTTP section
http {
resolver 127.0.0.53;
# ACME client for stream section
acme_client example https://acme-v02.api.letsencrypt.org/directory;
# Server for HTTP validation
server {
listen 80;
return 444;
}
}
# Stream section
stream {
server {
listen 12345 ssl;
proxy_pass backend_upstream;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
server_name example.com www.example.com;
acme example; # reference to ACME client defined in HTTP section
}
upstream backend_upstream {
server 127.0.0.1:54321;
}
}
challenge=dns
in the
acme_client directive; in this case, the server is not needed.Migrating from certbot#
$ sudo certbot --nginx -d example.com -d www.example.com
/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;
}
}
http {
resolver 127.0.0.53;
acme_client example https://acme-v02.api.letsencrypt.org/directory;
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;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location / {
try_files $uri $uri/ =404;
}
}
}
$ sudo kill -HUP $(cat /run/angie.pid)
$ 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