Настройка ACME#

Модуль ACME в Angie обеспечивает автоматическое получение сертификатов с использованием протокола ACME. Протокол ACME предусматривает несколько способов проверки доменов (также используется термин верификация); модуль реализует HTTP-проверку, DNS-проверку, ALPN-проверку, а также проверку с помощью хуков через самостоятельно реализуемый внешний сервис.

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

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

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

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

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

    Способ

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

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

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

    HTTP-проверка

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

    DNS-проверка

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

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

    ALPN-проверка

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

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

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

  • Настройте SSL с использованием полученного сертификата и ключа: Модуль делает сертификаты и ключи доступными в виде встроенных переменных, которые можно использовать в конфигурации для заполнения ssl_certificate и ssl_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); если ключа у него еще нет, ключ создается при запуске. Затем клиент использует его для регистрации учетной записи на сервере.

Примечание

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

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

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

http {

    resolver 127.0.0.53;

    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_<имя>, не влияя на то, какие домены запрашиваются.

HTTP-проверка#

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

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

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

http {

    resolver 127.0.0.53; # требуется для директивы 'acme_client'

    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-проверка#

Проверка работает автоматически. Суть в том, что при получении запроса на сертификат ACME-сервер выполняет специальный DNS-запрос к поддомену _acme-challenge. проверяемого домена. После получения ожидаемого ответа ACME-сервер подтверждает, что домен принадлежит клиенту.

Наш модуль ACME отслеживает такие запросы и обрабатывает их автоматически, при условии, что ваши записи DNS настроены должным образом, чтобы указать сервер Angie в качестве авторитетного сервера имен для поддомена _acme-challenge..

Примечание

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

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

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

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

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

Распространение изменений 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-записи для сервера имен

  93.184.215.14

$ nc -zv 93.184.215.14 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 {

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

ALPN-проверка#

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

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

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

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

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

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

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

  1. Создайте именованный location с директивой acme_hook.

  2. Настройте обработчик запроса в том же location с помощью модуля, подходящего для вашей конфигурации: fastcgi_pass для FastCGI, proxy_pass для HTTP, cgi для CGI-скриптов и т. д.

  3. Передайте обработчику переменные ACME с помощью поддерживаемого им механизма, например, fastcgi_param для FastCGI или cgi_set_var для CGI.

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

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

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

location @acme_hook {

    acme_hook example;

    # Handler directive (fastcgi_pass, proxy_pass, cgi on, ...)
    # Pass ACME variables using the handler's mechanism:
    #   ACME_HOOK       — $acme_hook_name ("add" or "remove")
    #   ACME_CHALLENGE  — $acme_hook_challenge ("dns" or "http")
    #   ACME_DOMAIN     — $acme_hook_domain
    #   ACME_TOKEN      — $acme_hook_token
    #   ACME_KEYAUTH    — $acme_hook_keyauth
}

При 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 {

    resolver 127.0.0.53;

    # 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; тогда сервер будет не нужен.

Миграция с certbot#

Если до перехода с nginx на Angie вы использовали certbot для получения и продления SSL-сертификатов от центра сертификации Let's Encrypt, выполните следующие шаги, чтобы перейти к использованию нашего модуля 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;
}

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

Итоговая конфигурация 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;
    }
}

Не забудьте перезагрузить конфигурацию после изменения:

$ sudo kill -HUP $(cat /run/angie.pid)

Убедившись, что эта конфигурация работает, вы можете удалить сертификаты 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