auto-ssl: Регистрация и продление SSL в реальном времени (бесплатно) внутри nginx-module-lua/nginx с Let's Encrypt
Установка
Если вы еще не настроили подписку на RPM репозиторий, зарегистрируйтесь. После этого вы можете продолжить с следующими шагами.
CentOS/RHEL 7 или Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-auto-ssl
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-auto-ssl
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-auto-ssl v0.13.1, выпущенную 1 октября 2019 года.
Регистрация и продление SSL в реальном времени (бесплатно) внутри OpenResty/nginx с Let's Encrypt.
Этот плагин OpenResty автоматически и прозрачно выдает SSL-сертификаты от Let's Encrypt (бесплатного удостоверяющего центра) по мере поступления запросов. Он работает следующим образом:
- Получен запрос SSL для SNI имени хоста.
- Если система уже имеет SSL-сертификат для этого домена, он немедленно возвращается (с OCSP stapling).
- Если система еще не имеет SSL-сертификата для этого домена, она выдает новый SSL-сертификат от Let's Encrypt. Проверка домена осуществляется автоматически. После получения нового сертификата (обычно в течение нескольких секунд) новый сертификат сохраняется, кэшируется и возвращается клиенту (без прерывания оригинального запроса).
Это использует функциональность ssl_certificate_by_lua в OpenResty 1.9.7.2+.
Используя lua-resty-auto-ssl для регистрации SSL-сертификатов с Let's Encrypt, вы соглашаетесь с Соглашением подписчика Let's Encrypt.
Создайте /etc/resty-auto-ssl и убедитесь, что он доступен для записи пользователю, от имени которого работают ваши nginx workers (в этом примере "www-data").
$ sudo mkdir /etc/resty-auto-ssl $ sudo chown www-data /etc/resty-auto-ssl
Реализуйте необходимую конфигурацию внутри вашего конфигурационного файла nginx. Вот минимальный пример:
```nginx
events {
worker_connections 1024;
}
http {
# Общий словарь "auto_ssl" должен быть определен с достаточным объемом памяти для
# хранения данных вашего сертификата. 1 МБ памяти вмещает сертификаты для
# примерно 100 отдельных доменов.
lua_shared_dict auto_ssl 1m;
# Общий словарь "auto_ssl_settings" используется для временного хранения различных настроек
# таких как секрет, используемый сервером хуков на порту 8999. Не изменяйте и
# не пропускайте его.
lua_shared_dict auto_ssl_settings 64k;
# DNS-резолвер должен быть определен для работы OCSP stapling.
#
# В этом примере используется DNS-сервер Google. Вы можете использовать
# стандартные DNS-серверы вашей системы, которые можно найти в /etc/resolv.conf. Если ваша сеть
# не поддерживает IPv6, вы можете отключить результаты IPv6, используя
# флаг "ipv6=off" (например, "resolver 8.8.8.8 ipv6=off").
resolver 8.8.8.8;
# Начальные задачи настройки.
init_by_lua_block {
auto_ssl = (require "resty.auto-ssl").new()
-- Определите функцию, чтобы определить, какие SNI домены автоматически обрабатывать
-- и регистрировать новые сертификаты. По умолчанию не разрешает никакие домены,
-- поэтому это необходимо настроить.
auto_ssl:set("allow_domain", function(domain)
return true
end)
auto_ssl:init()
}
init_worker_by_lua_block {
auto_ssl:init_worker()
}
# HTTPS сервер
server {
listen 443 ssl;
# Динамический обработчик для выдачи или возврата сертификатов для SNI доменов.
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
# Вы все равно должны определить статический файл ssl_certificate, чтобы nginx мог запуститься.
#
# Вы можете сгенерировать самоподписанный запасной сертификат с помощью:
#
# openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
# -subj '/CN=sni-support-required-for-valid-ssl' \
# -keyout /etc/ssl/resty-auto-ssl-fallback.key \
# -out /etc/ssl/resty-auto-ssl-fallback.crt
ssl_certificate /etc/ssl/resty-auto-ssl-fallback.crt;
ssl_certificate_key /etc/ssl/resty-auto-ssl-fallback.key;
}
# HTTP сервер
server {
listen 80;
# Конечная точка, используемая для выполнения проверки домена с Let's Encrypt.
location /.well-known/acme-challenge/ {
content_by_lua_block {
auto_ssl:challenge_server()
}
}
}
# Внутренний сервер, работающий на порту 8999 для обработки задач сертификатов.
server {
listen 127.0.0.1:8999;
# Увеличьте размер буфера тела, чтобы гарантировать, что внутренние POST-запросы всегда
# могут разобрать полное содержимое POST в память.
client_body_buffer_size 128k;
client_max_body_size 128k;
location / {
content_by_lua_block {
auto_ssl:hook_server()
}
}
}
}
Конфигурация
Дополнительные параметры конфигурации могут быть установлены для экземпляра auto_ssl, который создается:
allow_domain
По умолчанию: function(domain, auto_ssl, ssl_options, renewal) return false end
Функция, которая определяет, должен ли входящий домен автоматически получить новый SSL-сертификат.
По умолчанию resty-auto-ssl не будет выполнять никакие регистрации SSL, пока вы не определите функцию allow_domain. Вы можете вернуть true, чтобы обрабатывать все возможные домены, но имейте в виду, что поддельные SNI имена хостов могут быть использованы для инициирования бесконечного числа попыток регистрации SSL (которые будут отклонены). Более разумным подходом может быть создание белого списка разрешенных доменов.
Аргументы обратного вызова:
domain: Домен входящего запроса.auto_ssl: Текущий экземпляр auto-ssl.ssl_options: Таблица дополнительных параметров конфигурации, которые были переданы в функциюssl_certificate. Это может быть использовано для настройки поведения на уровне каждого nginxserver(см. пример вrequest_domain). Обратите внимание, что этот параметр не передается, когда эта функция вызывается для продлений, поэтому ваша функция должна обрабатывать это соответствующим образом.renewal: Логическое значение, указывающее, вызывается ли эта функция во время продления сертификата или нет. Когдаtrue, аргументssl_optionsне будет присутствовать.
При использовании адаптера хранения Redis вы можете получить доступ к текущему подключению Redis внутри обратного вызова allow_domain, обратившись к auto_ssl.storage.adapter:get_connection().
Пример:
auto_ssl:set("allow_domain", function(domain, auto_ssl, ssl_options, renewal)
return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)
dir
По умолчанию: /etc/resty-auto-ssl
Базовый каталог, используемый для хранения конфигурации, временных файлов и файлов сертификатов (если используется адаптер хранения file). Этот каталог должен быть доступен для записи пользователю, от имени которого работают nginx workers.
Пример:
auto_ssl:set("dir", "/some/other/location")
renew_check_interval
По умолчанию: 86400
Как часто (в секундах) все домены должны проверяться на предмет продления сертификатов. По умолчанию проверка осуществляется раз в день. Сертификаты будут автоматически продлены, если срок их действия истекает менее чем через 30 дней.
Пример:
auto_ssl:set("renew_check_interval", 172800)
storage_adapter
По умолчанию: resty.auto-ssl.storage_adapters.file
Опции: resty.auto-ssl.storage_adapters.file, resty.auto-ssl.storage_adapters.redis
Механизм хранения, используемый для постоянного хранения SSL-сертификатов. Поставляются адаптеры для файлового и redis-хранения, но также могут быть указаны пользовательские внешние адаптеры (значение просто должно находиться в lua_package_path).
По умолчанию адаптер хранения сохраняет сертификаты в локальные файлы. Однако вы можете рассмотреть возможность использования другого адаптера хранения (например, redis) по нескольким причинам: - Файловый ввод-вывод вызывает блокировку в OpenResty, что следует избегать для оптимальной производительности. Однако файлы читаются и записываются только в первый раз, когда сертификат виден, а затем данные кэшируются в памяти, поэтому фактическое количество операций ввода-вывода должно быть довольно минимальным. - Локальные файлы не будут работать, если сертификаты должны быть общими между несколькими серверами (для среды с балансировкой нагрузки).
Пример:
auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")
redis
По умолчанию: { host = "127.0.0.1", port = 6379 }
Если используется адаптер хранения redis, то дополнительные параметры подключения могут быть указаны в этой таблице. Принимает следующие параметры:
host: Хост для подключения (по умолчанию127.0.0.1).port: Порт для подключения (по умолчанию6379).socket: Вместо указанияhostиportдля подключения, можно указать путь к unix-сокету (в формате"unix:/path/to/unix.sock").connect_options: Дополнительные параметры подключения, которые передаются функции Redisconnect.auth: Значение, передаваемое командеAUTH.db: Номер базы данных Redis, используемой lua-resty-auto-ssl для сохранения сертификатов.prefix: Префикс для всех ключей, хранящихся в Redis, с этой строкой.
Пример:
auto_ssl:set("redis", {
host = "10.10.10.1"
})
request_domain
По умолчанию: function(ssl, ssl_options) return ssl.server_name() end
Функция, которая определяет имя хоста запроса. По умолчанию используется SNI домен, но можно реализовать пользовательскую функцию для определения имени домена для не-SNI запросов (основывая домен на чем-то, что можно определить вне SSL, например, на порту или IP-адресе, который получил запрос).
Аргументы обратного вызова:
ssl: Экземпляр модуляngx.ssl.ssl_options: Таблица дополнительных параметров конфигурации, которые были переданы в функциюssl_certificate. Это может быть использовано для настройки поведения на уровне каждого nginxserver.
Пример:
Этот пример, вместе с сопутствующими блоками server nginx, будет по умолчанию использовать имена доменов SNI, но для клиентов без SNI будет отвечать предопределенными хостами на основе порта подключения. Подключения к порту 9000 будут регистрировать и возвращать сертификат для foo.example.com, в то время как подключения к порту 9001 будут регистрировать и возвращать сертификат для bar.example.com. Любые другие порты будут возвращать запасной сертификат по умолчанию nginx.
auto_ssl:set("request_domain", function(ssl, ssl_options)
local domain, err = ssl.server_name()
if (not domain or err) and ssl_options and ssl_options["port"] then
if ssl_options["port"] == 9000 then
domain = "foo.example.com"
elseif ssl_options["port"] == 9001 then
domain = "bar.example.com"
end
end
return domain, err
end)
server {
listen 9000 ssl;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate({ port = 9000 })
}
}
server {
listen 9001 ssl;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate({ port = 9001 })
}
}
ca
По умолчанию: стандартный CA Let's Encrypt
URL окружения Let's Encrypt, который следует использовать. Обычно вы не должны это устанавливать, если не хотите использовать тестовую среду Let's Encrypt.
Пример:
auto_ssl:set("ca", "https://some-other-letsencrypt.org/directory")
hook_server_port
По умолчанию: 8999
Внутри мы используем специальный сервер, работающий на порту 8999 для обработки задач сертификатов. Порт, используемый для этой службы, может быть изменен здесь. Обратите внимание, что вам также нужно будет изменить его в вашей конфигурации nginx.
Пример:
auto_ssl:set("hook_server_port", 90)
json_adapter
По умолчанию: resty.auto-ssl.json_adapters.cjson
Опции: resty.auto-ssl.json_adapters.cjson, resty.auto-ssl.json_adapters.dkjson
Адаптер JSON, используемый для кодирования и декодирования JSON. По умолчанию используется cjson, который входит в состав установок OpenResty и, вероятно, должен использоваться в большинстве случаев. Однако адаптер, использующий чистый Lua dkjson, может быть использован для сред, где cjson может быть недоступен (вам нужно будет вручную установить зависимость dkjson через luarocks, чтобы использовать этот адаптер).
Адаптеры json cjson и dkjson поставляются, но также могут быть указаны пользовательские внешние адаптеры (значение просто должно находиться в lua_package_path).
Пример:
auto_ssl:set("json_adapter", "resty.auto-ssl.json_adapters.dkjson")
http_proxy_options
По умолчанию: nil
Настройте HTTP-прокси для использования при выполнении запросов OCSP stapling. Принимает таблицу параметров для lua-resty-http's set_proxy_options.
Пример:
auto_ssl:set("http_proxy_options", {
http_proxy = "http://localhost:3128",
})
Конфигурация ssl_certificate
Функция ssl_certificate принимает необязательную таблицу параметров конфигурации. Эти параметры могут быть использованы для настройки и управления поведением SSL на уровне каждого nginx server. Некоторые встроенные параметры могут контролировать поведение по умолчанию lua-resty-auto-ssl, но любые другие пользовательские данные могут быть переданы в функции обратного вызова allow_domain и request_domain.
Встроенные параметры конфигурации:
generate_certs
По умолчанию: true
Эта переменная может быть использована для отключения генерации сертификатов для конкретного блока сервера.
Пример:
server {
listen 8443 ssl;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate({ generate_certs = false })
}
}
Расширенная конфигурация Let's Encrypt
Внутри lua-resty-auto-ssl используется dehydrated в качестве клиента Let's Encrypt. Если вы хотите настроить более низкие параметры, такие как размер закрытого ключа, алгоритм открытого ключа или ваш регистрационный адрес электронной почты, эти параметры могут быть настроены в пользовательском конфигурационном файле dehydrated.
- Для полного списка поддерживаемых параметров смотрите пример конфигурации dehydrated.
- Пользовательские конфигурационные файлы dehydrated могут быть размещены по умолчанию в каталоге
/etc/resty-auto-ssl/letsencrypt/conf.d(или измените путь, если вы изменили стандартную настройкуdirlua-resty-auto-ssl).
Пример /etc/resty-auto-ssl/letsencrypt/conf.d/custom.sh:
KEYSIZE="4096"
KEY_ALGO="rsa"
CONTACT_EMAIL="[email protected]"
Предостережения
- Разрешенные хосты: По умолчанию resty-auto-ssl не будет выполнять никакие регистрации SSL, пока вы не определите функцию
allow_domain. Вы можете вернутьtrue, чтобы обрабатывать все возможные домены, но имейте в виду, что поддельные SNI имена хостов могут быть использованы для инициирования бесконечного числа попыток регистрации SSL (которые будут отклонены). Более разумным подходом может быть создание белого списка разрешенных доменов. - Ненадежный код: Убедитесь, что ваш сервер OpenResty, на котором это установлено, не может выполнять ненадежный код. Сертификаты и закрытые ключи должны быть доступны для чтения пользователю веб-сервера, поэтому важно, чтобы эти данные не были скомпрометированы.
- Файловое хранилище: Стандартный адаптер хранения сохраняет сертификаты в локальные файлы. Однако вы можете рассмотреть возможность использования другого адаптера хранения (например, redis) по нескольким причинам:
- Файловый ввод-вывод вызывает блокировку в OpenResty, что следует избегать для оптимальной производительности. Однако файлы читаются и записываются только в первый раз, когда сертификат виден, а затем данные кэшируются в памяти, поэтому фактическое количество операций ввода-вывода должно быть довольно минимальным.
- Локальные файлы не будут работать, если сертификаты должны быть общими между несколькими серверами (для среды с балансировкой нагрузки).
Разработка
После клонирования репозитория можно использовать Docker для запуска тестового набора:
$ docker-compose run --rm app make test
Тесты можно найти в директории spec, а тестовый набор реализован с использованием busted.
Процесс выпуска
Чтобы выпустить новую версию в LuaRocks:
- Убедитесь, что
CHANGELOG.mdактуален. - Переместите файл rockspec на новый номер версии (
git mv lua-resty-auto-ssl-X.X.X-1.rockspec lua-resty-auto-ssl-X.X.X-1.rockspec), и обновите переменныеversionиtagв файле rockspec. - Зафиксируйте и создайте тег для релиза (
git tag -a vX.X.X -m "Tagging vX.X.X" && git push origin vX.X.X). - Запустите
make release VERSION=X.X.X. - Скопируйте заметки CHANGELOG в новый релиз на GitHub.
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-auto-ssl.