ACME配置#
Angie中的 ACME 模块能够通过 ACME协议 自动获取证书。ACME协议支持各种域验证方法(也称为"验证");此模块实现了 HTTP验证、DNS验证 和 基于钩子的验证,通过自定义外部服务。 启用证书请求的常规步骤: 在 :samp:`http` 块中配置ACME客户端,使用 acme_client 指令,指定唯一的客户端名称和其他参数。可以配置多个ACME客户端。 指定请求证书的域:对于在所有使用指向同一个ACME客户端的 acme 指令的 设置请求处理和ACME回调:这对于验证域所有权是必需的。设置取决于选择的域验证方法: 方法 用户要求 多域 通配符域 在Angie服务器上开放80端口以接收连接。 在Angie服务器上开放53端口(或 acme_dns_port 指定的端口)
以接收连接。 为 创建一个外部服务(脚本或应用程序),
可以根据Angie的请求更新DNS记录
或通过Web服务器提供特殊响应。 使用获得的证书和密钥配置SSL:模块使证书和密钥作为 嵌入变量 可用,可以在 配置 中用于填充 ssl_certificate 和 ssl_certificate_key。 有关SSL设置说明,请参见 SSL配置。 小技巧 证书获取和更新过程依赖于多个服务,可能需要一些时间。请保持耐心,如有问题或疑问,请查阅 调试日志。 客户端密钥和证书以 PEM编码 存储在由 ACME客户端需要在CA服务器上有一个账号。为了创建和管理该账号,客户端使用私钥( 备注 如果您已经有一个账号密钥,请在启动前将其放置在客户端的子目录中,以便重用该账号。也可以使用 acme_client 中的 ACME客户端还使用一个单独的密钥( 启动时,如果证书不存在,客户端会请求证书,为其管理的所有域签名并发送CSR到CA服务器。服务器通过 HTTP 或 DNS验证 验证域所有权,并颁发证书,客户端将其保存在本地( 如前所述,单个证书涵盖同一个ACME客户端管理的所有域名,可能产生多域证书。证书涵盖的所有名称列表可以在获得的证书的 主题备用名称 (SAN)部分中找到。要从命令行检查此信息: 当证书即将到期或域列表发生变化时,客户端会签名并发送另一个CSR到CA服务器。服务器重新验证所有权并颁发新证书,客户端将其安装在本地,替换之前的证书。 在 配置 中,获得的证书及其对应的密钥可通过前缀变量
$acme_cert_<名称> 和 $acme_cert_key_<名称> 访问。它们的值是相应文件
的内容,应与 ssl_certificate 和 ssl_certificate_key 指令一起使用: 验证是自动处理的。此过程涉及ACME服务器,在收到请求后,通过HTTP检索特殊令牌文件 ,地址为 在此示例中,名为 如前所述,80端口必须开放以处理HTTP ACME调用。然而,如本示例所示,针对此端口的 listen 指令可以放置在单独的 server 块中。如果没有现有的包含此指令的块,您可以将新块限制为仅处理ACME调用: 为什么这样做有效?模块在读取头信息后但在选择虚拟服务器和处理 rewrite 和 location 指令之前拦截对 验证是自动处理的。当处理证书请求时,ACME服务器对正在验证的域的 我们的ACME模块自动跟踪和处理此类请求,前提是您的DNS记录已正确配置,将Angie服务器指定为 例如,要使用IP地址为 此配置将 此方法允许请求通配符证书,例如在 主题备用名称 (SAN)部分包含 警告 此场景的适用性在很大程度上取决于您的DNS提供商提供的功能;某些提供商不允许此类配置。 总体而言,配置与前一节中的示例相似。无需HTTP特定设置;相反,为 acme_client 指令设置 在此示例中,名为 与前面的方法不同,此验证需要额外的工作。ACME服务器执行标准的 HTTP验证 或 DNS验证,但不是直接与Angie服务器交互,而是与由Angie服务器通过钩子调用(acme_hook)管理的外部服务通信。该服务配置一个单独的DNS或HTTP服务器,ACME服务器向其发送请求。 一旦ACME服务器从配置的DNS或HTTP服务器收到预期响应,它就确认域所有权。 Angie在需要通过ACME协议更新证书时调用外部服务。调用通过使用 fastcgi_pass、scgi_pass 等指令将请求和数据代理到FastCGI、SCGI和类似服务器来进行。 该服务必须返回 在此示例中,ACME客户端 配置了一个命名的 以下Perl脚本演示了相应的外部FastCGI服务: 这里, 另一个使用PHP-FPM的示例: 传递的参数可以在PHP中通过 流模块 ACME 支持为TCP流量自动颁发和使用证书。为了正常运行,必须首先配置HTTP等效项:ACME客户端必须在 默认情况下,使用HTTP验证来获取证书。如 HTTP验证 中所述,这需要一个监听80端口的HTTP服务器: 也可以通过在 acme_client 指令中配置 如果您之前在 从nginx迁移到Angie 之前使用 certbot 从Let's Encrypt获取和续订SSL证书,请按照以下步骤迁移到使用ACME模块。 假设您最初使用以下命令配置证书: certbot自动创建的配置通常位于 在上面的示例中,突出显示的行必须修改。根据您的情况和偏好,使用ACME模块配置 HTTP验证 或 DNS验证。 最终的 Angie配置 可能如下所示: 记住在 更改后重新加载配置: 一旦您验证此配置正常工作,您可以删除 certbot 证书,并且如果不再在服务器的其他地方使用,可以禁用或完全删除certbot,例如:配置步骤#
server
块中的 server_name 指令列出的所有域名,将颁发一个单一的证书。_acme-challenge.
子域设置指向您Angie服务器的NS记录。实现细节#
--http-acme-client-path
构建选项 指定的目录的子目录中:$ ls /var/lib/angie/acme/example/
account.key certificate.pem private.key
account.key
)。如果没有密钥,则在启动时生成。然后客户端使用此密钥在服务器上注册该账号。account_key
参数指定该密钥文件。private.key
)用于证书签名请求(CSR)。如果需要,此证书密钥会在启动时自动创建。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验证#
/.well-known/acme-challenge/<TOKEN>
。我们的ACME模块自动跟踪和处理此类请求。当收到带有正确内容的预期响应时,ACME服务器确认域属于客户端。配置示例#
example
的ACME客户端管理 example.com
和 www.example.com
的证书(请注意,HTTP验证不支持通配符证书):http {
resolver 127.0.0.53; # 'acme_client'指令所需
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80; # 可能位于不同的'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;
}
}
server {
listen 80;
return 444; # 无响应,连接关闭
}
/.well-known/acme-challenge/<TOKEN>
的请求。如果 <TOKEN>
的值与特定调用的预期值匹配,则处理此类拦截请求。不会进行目录访问;请求完全由模块处理。DNS验证#
_acme-challenge.
子域执行 特殊DNS查询 。收到预期响应后,ACME服务器确认域属于客户端。_acme-challenge.
子域的权威名称服务器。93.184.215.14
的Angie服务器验证域 example.com
,您的域的DNS配置应包含以下记录:_acme-challenge.example.com. 60 IN NS ns.example.com.
ns.example.com. 60 IN A 93.184.215.14
_acme-challenge.example.com
的DNS解析委托给 ns.example.com
,通过将其映射到IP地址(93.184.215.14
)确保 ns.example.com
可访问。*.example.com
条目的证书。要明确请求子域的证书,如 www.example.com
,您必须使用上述方法单独验证该子域。配置示例#
challenge=dns
即可。example
的ACME客户端管理 example.com
和 *.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;
}
}
基于钩子的验证#
2xx
状态码,可以通过 Status
头发送。任何其他代码都被视为错误,证书续订将停止。服务的输出将被忽略。配置示例#
example
配置为使用DNS回调进行域验证,这由 acme_client
指令中的 challenge=dns
参数指示。server
块适用于 example.com
的所有子域(例如 *.example.com
),并使用ACME客户端 example
管理证书,如 acme
指令所指定。location
块来处理DNS验证的外部服务调用。acme_hook
指令将服务器链接到ACME客户端 example
。请求通过 fastcgi_pass
指令代理到在9000端口运行的本地FastCGI服务器。fastcgi_param
指令将有关ACME客户端、钩子、挑战类型、域、令牌和密钥授权的数据传递给外部服务。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()
和 DNS_clear_TXT_record()
是假定用于在外部DNS服务器配置中添加和删除TXT记录的函数,ACME服务器会查询该服务器。这些记录必须包含Angie服务器提供的数据,以允许外部DNS服务器成功通过验证,类似于 DNS验证 中描述的过程。此类函数的实现细节超出了本指南的范围;例如,参数也可以通过请求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#
http
上下文中声明,并且 stream
块必须放置在配置中的 http
块*之后*。配置示例#
# HTTP部分
http {
resolver 127.0.0.53;
# 用于stream部分的ACME客户端
acme_client example https://acme-v02.api.letsencrypt.org/directory;
# 用于HTTP验证的服务器
server {
listen 80;
return 444;
}
}
# Stream部分
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; # 引用HTTP部分中定义的ACME客户端
}
upstream backend_upstream {
server 127.0.0.1:54321;
}
}
challenge=dns
来使用DNS验证;在这种情况下,不需要服务器。从 certbot 迁移#
$ sudo certbot --nginx -d example.com -d www.example.com
/etc/nginx/sites-available/example.conf
,看起来像这样: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
$ # -- 或 --
$ sudo rm /etc/cron.d/certbot
$ sudo apt remove certbot
$ # -- 或 --
$ sudo dnf remove certbot