Настройка ACME#

Шаги настройки#

Общие шаги для включения запроса сертификатов в конфигурации:

  • Настройте ACME-клиент в блоке http с помощью директивы acme_client, задающей уникальное имя клиента и другие параметры; можно настроить несколько клиентов ACME.

  • Укажите домены, для которых запрашиваются сертификаты: для доменных имен, перечисленных во всех директивах server_name всех блоков server с директивами acme, указывающими на один и тот же ACME-клиент, будет выдан единый сертификат.

    IP-адреса (IPv4 или IPv6), указанные в server_name, также являются допустимыми идентификаторами сертификата, за исключением DNS-проверки.

    Обратите внимание: публичные центры сертификации могут выдавать сертификаты для IP-адресов только в рамках определенных профилей сертификатов; например, Let's Encrypt в настоящее время требует профиль, допускающий IP-идентификаторы (см. документацию по профилям). Чтобы запросить такой профиль, задайте параметр profile= директивы acme_client.

  • Настройте обработку запросов и вызовов ACME: это нужно для проверки владения доменом. Способ настройки зависит от способа проверки доменных имен:

    Способ

    Требования к пользователю

    Мультидомены

    Домены со звездочкой

    HTTP-проверка

    Открыть порт 80 (или указанный в acme_http_port) для входящих соединений на сервере Angie.

    ✔

    DNS-проверка

    Открыть порт 53 (или указанный в acme_dns_port) для входящих соединений на сервере Angie.

    Настроить NS-запись для поддомена _acme-challenge., направив ее на свой сервер Angie.

    ✔

    ✔

    ALPN-проверка

    Открыть порт 443 (или TLS-порт, используемый сервером Angie) для входящих соединений.

    ✔

    Проверка хуками

    Реализовать внешний сервис (скрипт или приложение), который по команде Angie сможет внести изменения в DNS-зону или разместить специальный ответ на веб-сервере.

    ✔

    ✔

Подробности реализации#

Клиенту ACME требуется учетная запись на сервере CA. Для ее создания и управления ею клиент использует закрытый ключ (account.key); если ключа у него еще нет, ключ создается при запуске. Затем клиент использует его для регистрации учетной записи на сервере.

Примечание

Если у вас уже есть ключ учетной записи, поместите его в подкаталог клиента перед запуском для повторного использования учетной записи. Файл ключа также можно указать с помощью параметра account_key в acme_client.

Некоторые CA требуют привязать учетную запись ACME к уже существующей внешней учетной записи с помощью External Account Binding (EAB), прежде чем выпускать сертификаты. Для этого задайте параметр eab директивы acme_client, указав идентификатор ключа и MAC-ключ, предоставленные CA; точный синтаксис приведен в описании директивы.

Клиент ACME также использует отдельный ключ (private.key) для запросов на подпись сертификата (CSR); если нужно, этот ключ сертификата также создается автоматически при запуске.

При запуске клиент запрашивает сертификат, если его еще нет, подписывая и отправляя CSR для всех доменов, которыми он управляет, серверу CA. Сервер проверяет владение доменом путем HTTP- или DNS-проверки и выдает сертификат, который клиент сохраняет локально (certificate.pem).

Как сказано выше, сертификат будет единым для всех доменных имен, для которых используется один и тот же ACME-клиент, то есть потенциально может быть мультидоменным. Список всех имен, для которых выдан сертификат, см. в разделе Subject Alternative Name (SAN) полученного сертификата. Проверить его можно в командной строке, например:

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

Когда приближается завершение срока действия сертификата или изменяется список доменов, клиент подписывает и отправляет еще один CSR на сервер CA. Сервер снова проверяет владение и выдает новый сертификат, который клиент устанавливает локально, заменяя предыдущий.

В конфигурации полученный сертификат и соответствующий ключ доступны через префиксные переменные $acme_cert_<имя> и $acme_cert_key_<имя>. Их значения — содержимое соответствующих файлов, которое следует использовать с директивами ssl_certificate и 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;
}

Примечание

Клиент ACME определяет адрес сервера CA по его имени через resolver того же контекста, который по умолчанию обращается к DNS-серверам из /etc/resolv.conf; задавайте директиву явно, только если нужны другие серверы или параметры. На узле без поддержки IPv6 resolver может все равно отправлять AAAA-запросы (IPv6) для сервера CA и не суметь подключиться; отключите их параметром ipv6=off, например resolver conf ipv6=off;.

Сбор доменов и использование сертификата#

Директива acme служит только для сбора доменных имен для запросов сертификатов. Она не определяет, где можно использовать сертификат: любой блок server может ссылаться на полученный сертификат через переменную $acme_cert_<имя>, независимо от того, содержит ли блок директиву acme.

Например, если у вас есть блок server с маской, который уже охватывает все поддомены, дополнительные блоки server для конкретных поддоменов не нуждаются в директиве acme:

http {

    acme_client example https://acme-v02.api.letsencrypt.org/directory
        challenge=dns;

    # В этом блоке перечислены домены для запроса сертификата
    server {

        listen 443 ssl;

        server_name example.com *.example.com;
        acme example;

        ssl_certificate $acme_cert_example;
        ssl_certificate_key $acme_cert_key_example;
    }

    # Этот блок использует тот же сертификат, но не добавляет
    # свой server_name в запрос на сертификат
    server {

        listen 443 ssl;

        server_name app.example.com;

        ssl_certificate $acme_cert_example;
        ssl_certificate_key $acme_cert_key_example;
    }
}

Явный список доменов#

Чтобы точно контролировать набор доменных имен в сертификате, не полагаясь на автоматический сбор из всех блоков server, создайте отдельный блок server, содержащий только директивы server_name и acme. Чтобы этот блок не обрабатывал реальный трафик, привяжите его к Unix-сокету:

# Отдельный блок, определяющий список доменов сертификата
server {

    listen unix:/tmp/acme_example.sock;

    server_name example.com www.example.com;
    acme example;
}

Другие блоки server могут затем использовать сертификат через переменную $acme_cert_<имя>, не влияя на то, какие домены запрашиваются.

Отдельные сертификаты для разных доменов#

Каждый acme_client управляет одним сертификатом. Чтобы получить несколько независимых сертификатов (например, для несвязанных доменов, которым не следует использовать общий сертификат), настройте отдельный acme_client для каждого из них в блоке http и укажите в директиве acme каждого блока server соответствующий клиент по имени:

http {

    # Два независимых клиента, каждый управляет своим сертификатом
    acme_client shop https://acme-v02.api.letsencrypt.org/directory
        challenge=http;

    acme_client blog https://acme-v02.api.letsencrypt.org/directory
        challenge=http;

    server {

        listen 443 ssl;

        server_name shop.example.com www.shop.example.com;
        acme shop;

        ssl_certificate $acme_cert_shop;
        ssl_certificate_key $acme_cert_key_shop;
    }

    server {

        listen 443 ssl;

        server_name blog.example.com;
        acme blog;

        ssl_certificate $acme_cert_blog;
        ssl_certificate_key $acme_cert_key_blog;
    }
}

HTTP-проверка#

Проверка выполняется автоматически. Когда Angie заказывает сертификат, ACME-сервер запрашивает по HTTP файл-токен по адресу /.well-known/acme-challenge/<TOKEN>. Angie отвечает на такие запросы сам, поэтому отдавать этот путь вручную не требуется. Получив файл, ACME-сервер сверяет токен с выданным значением и, если они совпадают, засчитывает проверку домена.

Пример конфигурации#

Здесь ACME-клиент с именем example управляет сертификатами для example.com и www.example.com (учтите, что wildcard-сертификаты не поддерживаются при HTTP-проверке):

http {

    acme_client example https://acme-v02.api.letsencrypt.org/directory;

    server {

        listen 80; # Не обязательно: если порт HTTP-проверки
                   # никто не слушает, модуль откроет его сам
                   # (см. директиву 'acme_http_port')

        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. Если ни один сервер не слушает порт HTTP-проверки, модуль создает отдельный слушающий сокет на порту 80 (или указанном в acme_http_port). Отдельный блок server не требуется.

DNS-проверка#

Проверка выполняется автоматически, но требует настройки DNS с вашей стороны. Когда Angie заказывает сертификат, ACME-сервер отправляет DNS-запрос TXT-записи в поддомене _acme-challenge. проверяемого домена. Angie отвечает на такие запросы сам: он выступает авторитетным сервером имен для этого поддомена и возвращает TXT-запись с тем значением, которого ждет ACME-сервер. ACME-сервер сверяет полученную запись с выданным значением и, если они совпадают, засчитывает проверку домена.

Чтобы запрос дошел до Angie, DNS вашего домена должен делегировать поддомен _acme-challenge. серверу Angie; нужные записи приведены ниже. По умолчанию Angie отвечает с очень небольшим TTL, а некоторые DNS-провайдеры отфильтровывают ответы с таким низким TTL — если проверка не завершается, увеличьте его директивой acme_dns_ttl.

DNS-проверка подтверждает контроль над доменным именем, поэтому не может проверять идентификаторы в виде IP-адресов: для клиента с challenge=dns Angie игнорирует IP-адрес в server_name и выводит предупреждение при запуске. Блок server, в котором указан IP-адрес, должен по-прежнему содержать хотя бы одно доменное имя, которое клиент может проверить.

Примечание

Сервер Angie должен быть доступен из интернета на UDP-порту 53 (или указанном в acme_dns_port). Если сервер находится за межсетевым экраном, убедитесь, что этот порт открыт для входящих соединений.

Например, чтобы подтвердить домен example.com, используя сервер Angie с IP-адресом 203.0.113.10, DNS-конфигурация вашего домена должна включать следующие записи:

_acme-challenge.example.com. 60    IN      NS       ns.example.com.
             ns.example.com. 60    IN       A       203.0.113.10

Эта конфигурация делегирует разрешение DNS для _acme-challenge.example.com на ns.example.com, обеспечивая доступность ns.example.com путем сопоставления с IP-адресом (203.0.113.10).

Предупреждение

Распространение изменений NS-записей может занимать от нескольких минут до 48 часов в зависимости от TTL и DNS-провайдера. Рекомендуется проверить корректность настройки перед запросом сертификата.

Для проверки корректности настройки DNS можно использовать следующие команды:

$ dig NS _acme-challenge.example.com +short  # Проверка NS-записи для поддомена _acme-challenge

  ns.example.com.

$ dig A ns.example.com +short  # Проверка A-записи для сервера имен

  203.0.113.10

$ nc -zv 203.0.113.10 53  # Проверка доступности DNS-сервера на порту 53

Этот способ позволяет запрашивать wildcard-сертификаты, например сертификат, включающий запись *.example.com в разделе Subject Alternative Name (SAN). Чтобы в явной форме запросить сертификат для поддомена, например www.example.com, следует отдельно подтвердить этот поддомен описанным выше способом.

Предупреждение

Применимость данного сценария во многом зависит от возможностей, предоставляемых вашим DNS-провайдером; некоторые провайдеры не позволяют выполнять такие настройки.

Пример конфигурации#

В целом, конфигурация схожа с примером из предыдущего раздела. Нет необходимости в настройках, специфичных для HTTP; вместо этого достаточно установить challenge=dns для директивы acme_client.

Здесь ACME-клиент с именем example управляет сертификатами для example.com и *.example.com:

http {

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

ALPN-проверка#

Проверка работает автоматически. ACME‑сервер устанавливает TLS‑соединение и запрашивает через ALPN протокол acme-tls/1. Модуль отдает временный сертификат для валидационного запроса.

Чтобы использовать этот способ, задайте challenge=alpn в директиве acme_client и убедитесь, что ваш TLS‑слушатель доступен на порту 443 (или на используемом TLS‑порту).

Пример конфигурации#

Конфигурация аналогична предыдущим разделам; достаточно задать challenge=alpn для директивы acme_client и убедиться, что TLS-сервер доступен на порту 443.

Здесь ACME-клиент с именем example управляет сертификатом для example.com и www.example.com:

http {

    acme_client example https://acme-v02.api.letsencrypt.org/directory
        challenge=alpn;

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

Проверка с помощью хуков#

В отличие от предыдущих способов, эта проверка требует дополнительных усилий. Здесь ACME-сервер производит обычную HTTP-проверку или DNS-проверку, но обращается не к самому серверу Angie, а к внешнему сервису, которым сервер Angie управляет с помощью вызовов-хуков (acme_hook). В свою очередь, этот сервис настраивает отдельный DNS- или HTTP-сервер, куда и направляются запросы ACME-сервера.

Получив ожидаемый ответ от настроенного таким образом DNS- или HTTP-сервера, ACME-сервер подтверждает, что домен принадлежит клиенту.

Когда для выпуска или обновления сертификата требуется проверка домена, Angie формирует внутренний запрос к именованному location, содержащему директиву acme_hook. Способ обработки этого запроса полностью зависит от других директив, заданных в том же location.

Общая схема такова:

Обработчик должен возвращать код состояния 2xx, который можно передать через заголовок Status. Любой другой код от хука add прерывает попытку обновления сертификата; от хука remove тот же код только записывается в журнал как предупреждение, и обновление продолжается. Вывод обработчика игнорируется.

Минимальная конфигурация#

Независимо от используемого обработчика, location для хука имеет следующую структуру:

При DNS-проверке обработчик должен использовать ACME_HOOK для определения действия: если значение add, создать TXT-запись для _acme-challenge.ACME_DOMAIN со значением из ACME_KEYAUTH; если значение remove, удалить эту запись.

Пример с FastCGI#

Здесь настраивается ACME-клиент example для подтверждения домена при помощи DNS-вызова, на что указывает параметр challenge=dns директивы acme_client.

Блок server применяется ко всем поддоменам example.com (например, *.example.com) и использует ACME-клиент example для управления сертификатами, что указано в директиве acme.

Именованный блок location обрабатывает вызовы хуков. Директива acme_hook связывает его с ACME-клиентом example. Запросы хуков отправляются на локальный FastCGI-сервер на порту 9000 с помощью fastcgi_pass. Директивы 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;
    }
}

Пример соответствующего внешнего FastCGI-сервиса на Perl:

#!/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() — функции, предположительно добавляющие и удаляющие TXT-записи в конфигурации некого внешнего DNS-сервера, к которому и обратится 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#

Еще один пример, с использованием PHP-FPM:

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['...'].

ACME в потоковом модуле#

Потоковый модуль ACME позволяет автоматизировать выпуск и использование сертификатов для TCP-трафика. Для его корректной работы необходимо сначала настроить HTTP-аналог: ACME-клиент должен быть объявлен в контексте http, а сам блок stream должен располагаться после блока http в конфигурации.

Пример конфигурации#

По умолчанию для получения сертификатов используется режим HTTP-проверки. Для него, как упоминалось в разделе HTTP-проверка, нужен HTTP-сервер, слушающий на порту 80:

# HTTP-часть
http {

    # ACME-клиент для потоковой части
    acme_client example https://acme-v02.api.letsencrypt.org/directory;

    # Сервер для HTTP-проверки
    server {

        listen 80;
        return 444;
    }
}

# Потоковая часть
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; # ссылка на ACME-клиент, определенный в HTTP-части
    }

    upstream backend_upstream {

        server 127.0.0.1:54321;
    }
}

Также можно использовать DNS-проверку, настроив challenge=dns в директиве acme_client; тогда сервер будет не нужен.