ACME配置#

Angie中的 ACME 模块能够通过 ACME协议 自动获取证书。ACME协议支持各种域验证方法(也称为"验证");此模块实现了 HTTP验证DNS验证基于钩子的验证,通过自定义外部服务。

配置步骤#

启用证书请求的常规步骤:

  • 在 :samp:`http` 块中配置ACME客户端,使用 acme_client 指令,指定唯一的客户端名称和其他参数。可以配置多个ACME客户端。

  • 指定请求证书的域:对于在所有使用 acmeserver 块中的 server_name 指令列出的所有域名,将颁发一个单一的证书,指向同一个ACME客户端。

  • 设置请求处理和ACME回调:这对于验证域所有权是必需的。设置取决于选择的域验证方法:

    方法

    要求

    证书

    HTTP验证

    需要对ACME服务器的特定请求(回调)作出响应。

    Angie服务器的80端口必须开放。

    不支持通配符证书。

    允许颁发多域证书。

    DNS验证

    需要处理来自ACME服务器的特定DNS查询(回调)。

    需要能够修改DNS记录。

    Angie服务器必须开放 acme_dns_port 指令指定的端口(默认是53)。

    支持颁发多域和通配符证书。

    基于钩子的验证

    需要实现一个外部服务,Angie与之通信。

    需要由外部服务配置的外部DNS或HTTP服务器。

    外部DNS或HTTP服务器的要求与上述相应点匹配。

    支持颁发多域和通配符证书。

  • 使用获得的证书和密钥配置SSL:模块使证书和密钥作为 嵌入变量 可用,可以在 配置 中用于填充 ssl_certificatessl_certificate_key

    有关SSL设置说明,请参见 SSL配置

小技巧

证书获取和更新过程依赖于多个服务的运行,可能需要一些时间。请保持耐心,如有问题或疑问,请查阅 调试日志

实现细节#

客户端密钥和证书以 PEM编码 存储在由 --http-acme-client-path 构建选项 指定的目录的相应子目录中:

$ ls /var/lib/angie/acme/example/

  account.key  certificate.pem  private.key

ACME客户端需要在CA服务器上有一个账号。它使用私钥(account.key)创建和管理该账号;如果没有密钥,则在启动时生成。然后客户端使用此密钥在服务器上注册该账号。

备注

如果您已经有一个账号密钥,请在启动前将其放置在客户端的子目录中,以便重用该账号。也可以使用 acme_client 中的 account_key 参数指定该密钥文件。

ACME客户端还使用一个单独的密钥(private.key)用于证书签名请求(CSR);如果需要,此证书密钥也会在启动时自动创建。

启动时,如果尚无证书,客户端会为其管理的所有域签名并发送CSR到CA服务器请求证书。服务器通过 HTTPDNS验证 验证域所有权,并颁发证书,客户端将其保存在本地(certificate.pem)。

如前所述,使用同一个ACME客户端的所有域名将获得一个单一证书,因此可能是多域证书。所有包含的名称列表可以在证书的 主题备用名称 (SAN)部分中找到。您可以在命令行中检查此信息:

$ openssl x509 -in certificate.pem -noout -text | grep -A5 "Subject Alternative Name"

当证书即将到期或域列表发生变化时,客户端会签名并发送另一个CSR到CA服务器。服务器再次验证所有权并颁发新证书,客户端将其安装在本地,替换之前的证书。

配置 中,获取的证书及其对应的密钥可通过前缀变量 $acme_cert_<名称>$acme_cert_key_<名称> 访问。它们的值是相应文件 的内容,应与 ssl_certificatessl_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验证#

验证是自动处理的。此过程涉及ACME服务器,在收到请求后,通过HTTP 检索特殊文件令牌 ,地址为 /.well-known/acme-challenge/<TOKEN>。我们的ACME模块跟踪此类请求并自动处理它们。当收到带有正确内容的预期响应时,ACME服务器确认域所有权。

配置示例#

在此示例中,名为 example 的ACME客户端管理 example.comwww.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;
    }
}

如前所述,80端口必须开放以接收ACME的HTTP回调。然而,如本示例所示,针对此端口的 listen 指令可以放置在单独的 server 块中。如果没有现有的包含此指令的块,可以创建一个仅限于ACME回调的新块:

server {

    listen 80;
    return 444; # 无响应,连接关闭
}

为什么这样做有效? 模块在读取头信息后但在选择虚拟服务器和处理 rewritelocation 指令之前拦截对 /.well-known/acme-challenge/<TOKEN> 的请求。 如果 <TOKEN> 的值与特定回调的预期值匹配,则处理此类拦截请求。 不会访问目录;请求完全由模块处理。

DNS验证#

验证是自动处理的。其原理是,当收到证书请求时,ACME服务器对正在验证的域的 _acme-challenge. 子域执行 特殊DNS查询 。收到预期响应后,ACME服务器确认域所有权。

我们的ACME模块自动跟踪和处理此类请求,前提是您的DNS记录已正确配置,将Angie服务器指定为 _acme-challenge. 子域的权威名称服务器。

例如,要使用IP地址为 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 可访问。

此方法允许请求通配符证书,例如在 主题备用名称 (SAN)部分包含 *.example.com 的证书。要明确请求子域的证书,如 www.example.com,您需要使用上述方法单独验证该子域。

注意

此场景的适用性在很大程度上取决于您的DNS提供商提供的功能;某些提供商不允许此类配置。

配置示例#

总体而言,配置与前一节中的示例相似。无需HTTP特定设置;相反,只需在 acme_client 指令中设置 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;
    }
}

基于钩子的验证#

与前面的方法不同,此验证需要额外的工作。这里,ACME服务器执行常规的 HTTP验证DNS验证,但不是直接与Angie服务器通信,而是与由Angie服务器通过钩子调用(acme_hook)管理的外部服务通信。这个服务反过来配置一个单独的DNS或HTTP服务器,ACME服务器向其发送请求。

一旦ACME服务器从这样配置的DNS或HTTP服务器收到预期响应,它确认域所有权。

Angie在需要通过ACME协议更新证书时调用外部服务。调用通过使用 fastcgi_passscgi_pass 等指令将请求和数据代理到FastCGI、SCGI或类似服务器来传递。

该服务必须返回 2xx 代码,可以通过 Status 头完成。任何其他代码都被视为错误,证书更新将中止。服务的输出将被忽略。

配置示例#

此处配置了ACME客户端 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;
    }
}

以下是一个相应的Perl外部FastCGI服务示例:

#!/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;
}

另一个使用PHP-FPM配置FastCGI服务的示例:

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
# ...

传递的参数可以在PHP中通过 $_SERVER['...'] 访问。

certbot 迁移#

如果在 从nginx迁移到Angie 之前,您使用 certbot 从Let's Encrypt获取和续订SSL证书,请按照以下步骤迁移到使用我们的ACME模块。

假设您最初使用以下命令配置证书:

$ 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;
    }
}

在上面的示例中,突出显示的行需要修改。根据您的情况和偏好,使用ACME模块配置 HTTP验证DNS验证

最终的 Angie配置 可能如下所示:

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)

确认此配置正常工作后,您可以删除 certbot 证书,并且如果不再在其他地方使用,可以禁用或完全删除certbot,例如:

$ 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