Настройка ACME#

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

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

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

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

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

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

    Способ

    Требования

    Сертификаты

    HTTP-проверка

    Требует ответа на специальный запрос (вызов) от ACME-сервера.

    Нужно открыть порт 80 на самом сервере Angie.

    Не поддерживает wildcard-сертификаты.

    Позволяет выпускать мультидоменные сертификаты.

    DNS-проверка

    Требует обработки специальных DNS-запросов (вызовов) от ACME-сервера.

    Необходима возможность изменять DNS-записи.

    До сервера Angie должен быть открыт порт, заданный директивой acme_dns_port (по умолчанию 53).

    Позволяет выпускать мультидоменные и wildcard-сертификаты.

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

    Требует реализации внешнего сервиса, к которому обращается сервер Angie.

    Требует наличия внешнего DNS- или HTTP-сервера, настраиваемого внешним сервисом.

    Для внешнего DNS- или HTTP-сервера действуют требования соответствующего пункта выше.

    Позволяет выпускать мультидоменные и wildcard-сертификаты.

  • Настройте 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;
}

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; # Может стоять в другом блоке '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; # Нет ответа, соединение закрыто
}

Почему это работает? Модуль перехватывает запросы к /.well-known/acme-challenge/<TOKEN> после чтения заголовков, но до выбора виртуального сервера и обработки директив rewrite и location. Такие перехваченные запросы обрабатываются, если значение <TOKEN> соответствует ожидаемому для конкретного вызова. Обращения к каталогу не будет; запрос полностью обрабатывается модулем.

DNS-проверка#

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

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

Например, чтобы подтвердить домен 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).

Этот способ позволяет запрашивать 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;
    }
}

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

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

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

Angie вызывает внешний сервис в моменты, когда требуется обновление сертификатов по протоколу ACME. Вызовы передаются путем проксирования запросов и данных на серверы FastCGI, SCGI и аналогичные с использованием директив fastcgi_pass, scgi_pass и т.д.

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

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

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

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

Здесь также настроен именованный блок location, через который и производятся вызовы внешнего сервиса для DNS-верификации. Директива 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-проверка. Подробности реализации таких функций выходят за рамки этого руководства.

Миграция с 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;

    location / {

        try_files $uri $uri/ =404;
    }
}

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

Итоговая конфигурация Angie может выглядеть приблизительно так:

http {

    resolver 127.0.0.53;

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

    server {
        listen 80;
        return 444;
    }

    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 angie -s reload

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