auto-ssl: Registro y renovación de SSL en tiempo real (y gratis) dentro de nginx-module-lua/nginx con Let's Encrypt
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-auto-ssl v0.13.1 lanzado el 01 de octubre de 2019.
Registro y renovación de SSL en tiempo real (y gratis) dentro de OpenResty/nginx con Let's Encrypt.
Este plugin de OpenResty emite automáticamente y de manera transparente certificados SSL de Let's Encrypt (una autoridad certificadora gratuita) a medida que se reciben las solicitudes. Funciona de la siguiente manera:
- Se recibe una solicitud SSL para un nombre de host SNI.
- Si el sistema ya tiene un certificado SSL para ese dominio, se devuelve inmediatamente (con OCSP stapling).
- Si el sistema aún no tiene un certificado SSL para este dominio, se emite un nuevo certificado SSL de Let's Encrypt. La validación del dominio se maneja por ti. Después de recibir el nuevo certificado (generalmente dentro de unos segundos), el nuevo certificado se guarda, se almacena en caché y se devuelve al cliente (sin interrumpir la solicitud original).
Esto utiliza la funcionalidad ssl_certificate_by_lua en OpenResty 1.9.7.2+.
Al usar lua-resty-auto-ssl para registrar certificados SSL con Let's Encrypt, aceptas el Acuerdo de Suscriptor de Let's Encrypt.
Crea /etc/resty-auto-ssl y asegúrate de que sea escribible por el usuario con el que
se ejecutan los trabajadores de nginx (en este ejemplo, "www-data").
$ sudo mkdir /etc/resty-auto-ssl $ sudo chown www-data /etc/resty-auto-ssl
Implementa la configuración necesaria dentro de tu configuración de nginx. Aquí hay un ejemplo mínimo:
```nginx
events {
worker_connections 1024;
}
http {
# El diccionario compartido "auto_ssl" debe definirse con suficiente espacio de almacenamiento para
# contener tus datos de certificado. 1MB de almacenamiento sostiene certificados para
# aproximadamente 100 dominios separados.
lua_shared_dict auto_ssl 1m;
# El diccionario compartido "auto_ssl_settings" se utiliza para almacenar temporalmente varias configuraciones
# como el secreto utilizado por el servidor de ganchos en el puerto 8999. No lo cambies ni
# lo omitas.
lua_shared_dict auto_ssl_settings 64k;
# Se debe definir un resolver DNS para que funcione el OCSP stapling.
#
# Este ejemplo utiliza el servidor DNS de Google. Puede que desees usar los servidores DNS
# predeterminados de tu sistema, que se pueden encontrar en /etc/resolv.conf. Si tu red
# no es compatible con IPv6, es posible que desees desactivar los resultados de IPv6 utilizando el
# flag "ipv6=off" (como "resolver 8.8.8.8 ipv6=off").
resolver 8.8.8.8;
# Tareas de configuración inicial.
init_by_lua_block {
auto_ssl = (require "resty.auto-ssl").new()
-- Define una función para determinar qué dominios SNI manejar automáticamente
-- y registrar nuevos certificados. Por defecto no se permite ningún dominio,
-- por lo que esto debe configurarse.
auto_ssl:set("allow_domain", function(domain)
return true
end)
auto_ssl:init()
}
init_worker_by_lua_block {
auto_ssl:init_worker()
}
# Servidor HTTPS
server {
listen 443 ssl;
# Manejador dinámico para emitir o devolver certificados para dominios SNI.
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
# Aún debes definir un archivo ssl_certificate estático para que nginx inicie.
#
# Puedes generar un respaldo autofirmado con:
#
# 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;
}
# Servidor HTTP
server {
listen 80;
# Endpoint utilizado para realizar la verificación del dominio con Let's Encrypt.
location /.well-known/acme-challenge/ {
content_by_lua_block {
auto_ssl:challenge_server()
}
}
}
# Servidor interno que se ejecuta en el puerto 8999 para manejar tareas de certificados.
server {
listen 127.0.0.1:8999;
# Aumenta el tamaño del búfer del cuerpo, para asegurar que los POST internos puedan siempre
# analizar el contenido completo del POST en memoria.
client_body_buffer_size 128k;
client_max_body_size 128k;
location / {
content_by_lua_block {
auto_ssl:hook_server()
}
}
}
}
Configuración
Se pueden establecer opciones de configuración adicionales en la instancia auto_ssl que se crea:
allow_domain
Por defecto: function(domain, auto_ssl, ssl_options, renewal) return false end
Una función que determina si el dominio entrante debe emitir automáticamente un nuevo certificado SSL.
Por defecto, resty-auto-ssl no realizará ningún registro SSL hasta que definas la función allow_domain. Puedes devolver true para manejar todos los dominios posibles, pero ten en cuenta que nombres de host SNI falsos pueden ser utilizados para desencadenar un número indefinido de intentos de registro SSL (los cuales serán rechazados). Un enfoque mejor puede ser permitir solo los dominios permitidos de alguna manera.
Los argumentos de la función de devolución de llamada son:
domain: El dominio de la solicitud entrante.auto_ssl: La instancia actual de auto-ssl.ssl_options: Una tabla de opciones de configuración opcionales que se pasaron a la funciónssl_certificate. Esto se puede usar para personalizar el comportamiento en función de cadaserverde nginx (ver ejemplo enrequest_domain). Ten en cuenta que esta opción no se pasa cuando se llama a esta función para renovaciones, por lo que tu función debe manejar eso en consecuencia.renewal: Valor booleano que indica si esta función se está llamando durante la renovación del certificado o no. Cuando estrue, el argumentossl_optionsno estará presente.
Al usar el adaptador de almacenamiento Redis, puedes acceder a la conexión Redis actual dentro de la devolución de llamada allow_domain accediendo a auto_ssl.storage.adapter:get_connection().
Ejemplo:
auto_ssl:set("allow_domain", function(domain, auto_ssl, ssl_options, renewal)
return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)
dir
Por defecto: /etc/resty-auto-ssl
El directorio base utilizado para almacenar la configuración, archivos temporales y archivos de certificados (si se utiliza el adaptador de almacenamiento file). Este directorio debe ser escribible por el usuario con el que se ejecutan los trabajadores de nginx.
Ejemplo:
auto_ssl:set("dir", "/some/other/location")
renew_check_interval
Por defecto: 86400
Con qué frecuencia (en segundos) se deben verificar todos los dominios para renovaciones de certificados. Por defecto, se verifica cada 1 día. Los certificados se renovarán automáticamente si expiran en menos de 30 días.
Ejemplo:
auto_ssl:set("renew_check_interval", 172800)
storage_adapter
Por defecto: resty.auto-ssl.storage_adapters.file
Opciones: resty.auto-ssl.storage_adapters.file, resty.auto-ssl.storage_adapters.redis
El mecanismo de almacenamiento utilizado para el almacenamiento persistente de los certificados SSL. Se proporcionan adaptadores de almacenamiento basados en archivos y redis, pero también se pueden especificar adaptadores externos personalizados (el valor simplemente necesita estar en el lua_package_path).
El adaptador de almacenamiento predeterminado persiste los certificados en archivos locales. Sin embargo, es posible que desees considerar otro adaptador de almacenamiento (como redis) por un par de razones: - La entrada/salida de archivos causa bloqueos en OpenResty, lo cual se debe evitar para un rendimiento óptimo. Sin embargo, los archivos solo se leen y escriben la primera vez que se ve un certificado, y luego las cosas se almacenan en caché en memoria, por lo que la cantidad real de entrada/salida de archivos debería ser bastante mínima. - Los archivos locales no funcionarán si los certificados necesitan ser compartidos entre varios servidores (para un entorno equilibrado en carga).
Ejemplo:
auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")
redis
Por defecto: { host = "127.0.0.1", port = 6379 }
Si se está utilizando el adaptador de almacenamiento redis, entonces se pueden especificar opciones de conexión adicionales en esta tabla. Acepta las siguientes opciones:
host: Host al que conectarse (por defecto127.0.0.1).port: Puerto al que conectarse (por defecto6379).socket: En lugar de especificarhostyportpara conectarse, se puede dar una ruta de socket unix en su lugar (en el formato de"unix:/path/to/unix.sock").connect_options: Opciones de conexión adicionales para pasar a la función Redisconnect.auth: Valor para pasar al comandoAUTH.db: El número de base de datos de Redis utilizado por lua-resty-auto-ssl para guardar certificados.prefix: Prefijo para todas las claves almacenadas en Redis con esta cadena.
Ejemplo:
auto_ssl:set("redis", {
host = "10.10.10.1"
})
request_domain
Por defecto: function(ssl, ssl_options) return ssl.server_name() end
Una función que determina el nombre de host de la solicitud. Por defecto, se utiliza el dominio SNI, pero se puede implementar una función personalizada para determinar el nombre de dominio para solicitudes no SNI (basando el dominio en algo que se pueda determinar fuera de SSL, como el puerto o la dirección IP que recibió la solicitud).
Los argumentos de la función de devolución de llamada son:
ssl: Una instancia del módulongx.ssl.ssl_options: Una tabla de opciones de configuración opcionales que se pasaron a la funciónssl_certificate. Esto se puede usar para personalizar el comportamiento en función de cadaserverde nginx.
Ejemplo:
Este ejemplo, junto con los bloques server de nginx que lo acompañan, se basará en los nombres de dominio SNI por defecto, pero para clientes no SNI responderá con hosts predefinidos basados en el puerto de conexión. Las conexiones al puerto 9000 registrarán y devolverán un certificado para foo.example.com, mientras que las conexiones al puerto 9001 registrarán y devolverán un certificado para bar.example.com. Cualquier otro puerto devolverá el certificado de respaldo predeterminado de 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
Por defecto: la CA predeterminada de Let's Encrypt
URL del entorno de Let's Encrypt a utilizar. Normalmente no deberías establecer esto, a menos que desees utilizar el entorno de pruebas de Let's Encrypt.
Ejemplo:
auto_ssl:set("ca", "https://some-other-letsencrypt.org/directory")
hook_server_port
Por defecto: 8999
Internamente, utilizamos un servidor especial que se ejecuta en el puerto 8999 para manejar tareas de certificados. El puerto utilizado para este servicio puede cambiarse aquí. Ten en cuenta que también necesitarás cambiarlo en tu configuración de nginx.
Ejemplo:
auto_ssl:set("hook_server_port", 90)
json_adapter
Por defecto: resty.auto-ssl.json_adapters.cjson
Opciones: resty.auto-ssl.json_adapters.cjson, resty.auto-ssl.json_adapters.dkjson
El adaptador JSON a utilizar para codificar y decodificar JSON. Por defecto, se utiliza cjson, que se incluye con las instalaciones de OpenResty y probablemente debería usarse en la mayoría de los casos. Sin embargo, se puede utilizar un adaptador que use el puro Lua dkjson para entornos donde cjson puede no estar disponible (deberás instalar manualmente la dependencia dkjson a través de luarocks para utilizar este adaptador).
Se proporcionan adaptadores json de cjson y dkjson, pero también se pueden especificar adaptadores externos personalizados (el valor simplemente necesita estar en el lua_package_path).
Ejemplo:
auto_ssl:set("json_adapter", "resty.auto-ssl.json_adapters.dkjson")
http_proxy_options
Por defecto: nil
Configura un proxy HTTP a utilizar al realizar solicitudes de OCSP stapling. Acepta una tabla de opciones para set_proxy_options de lua-resty-http.
Ejemplo:
auto_ssl:set("http_proxy_options", {
http_proxy = "http://localhost:3128",
})
Configuración de ssl_certificate
La función ssl_certificate acepta una tabla opcional de opciones de configuración. Estas opciones se pueden utilizar para personalizar y controlar el comportamiento de SSL en función de cada server de nginx. Algunas opciones integradas pueden controlar el comportamiento predeterminado de lua-resty-auto-ssl, pero cualquier otro dato personalizado se puede dar como opciones, que luego se pasarán a las funciones de devolución de llamada allow_domain y request_domain.
Opciones de configuración integradas:
generate_certs
Por defecto: true
Esta variable se puede utilizar para deshabilitar la generación de certificados en una ubicación de bloque de servidor.
Ejemplo:
server {
listen 8443 ssl;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate({ generate_certs = false })
}
}
Configuración Avanzada de Let's Encrypt
Internamente, lua-resty-auto-ssl utiliza dehydrated como su cliente de Let's Encrypt. Si deseas ajustar configuraciones de nivel inferior, como el tamaño de la clave privada, el algoritmo de clave pública o tu correo electrónico de registro, estas configuraciones se pueden configurar en un archivo de configuración de dehydrated personalizado.
- Para una lista completa de opciones soportadas, consulta el ejemplo de configuración de dehydrated.
- Los archivos de configuración personalizados de dehydrated se pueden colocar dentro del directorio
/etc/resty-auto-ssl/letsencrypt/conf.dpor defecto (o ajustar la ruta si has cambiado la configuración predeterminada dedirde lua-resty-auto-ssl).
Ejemplo de /etc/resty-auto-ssl/letsencrypt/conf.d/custom.sh:
KEYSIZE="4096"
KEY_ALGO="rsa"
CONTACT_EMAIL="[email protected]"
Precauciones
- Hosts Permitidos: Por defecto, resty-auto-ssl no realizará ningún registro SSL hasta que definas la función
allow_domain. Puedes devolvertruepara manejar todos los dominios posibles, pero ten en cuenta que nombres de host SNI falsos pueden ser utilizados para desencadenar un número indefinido de intentos de registro SSL (los cuales serán rechazados). Un enfoque mejor puede ser permitir solo los dominios permitidos de alguna manera. - Código No Confiable: Asegúrate de que tu servidor OpenResty donde está instalado no pueda ejecutar código no confiable. Los certificados y las claves privadas deben ser legibles por el usuario del servidor web, por lo que es importante que estos datos no sean comprometidos.
- Almacenamiento de Archivos: El adaptador de almacenamiento predeterminado persiste los certificados en archivos locales. Sin embargo, es posible que desees considerar otro adaptador de almacenamiento (como redis) por un par de razones:
- La entrada/salida de archivos causa bloqueos en OpenResty, lo cual se debe evitar para un rendimiento óptimo. Sin embargo, los archivos solo se leen y escriben la primera vez que se ve un certificado, y luego las cosas se almacenan en caché en memoria, por lo que la cantidad real de entrada/salida de archivos debería ser bastante mínima.
- Los archivos locales no funcionarán si los certificados necesitan ser compartidos entre varios servidores (para un entorno equilibrado en carga).
Desarrollo
Después de clonar el repositorio, se puede usar Docker para ejecutar la suite de pruebas:
$ docker-compose run --rm app make test
Las pruebas se pueden encontrar en el directorio spec, y la suite de pruebas se implementa utilizando busted.
Proceso de Lanzamiento
Para lanzar una nueva versión en LuaRocks:
- Asegúrate de que
CHANGELOG.mdesté actualizado. - Mueve el archivo rockspec al nuevo número de versión (
git mv lua-resty-auto-ssl-X.X.X-1.rockspec lua-resty-auto-ssl-X.X.X-1.rockspec), y actualiza las variablesversionytagen el archivo rockspec. - Realiza un commit y etiqueta el lanzamiento (
git tag -a vX.X.X -m "Tagging vX.X.X" && git push origin vX.X.X). - Ejecuta
make release VERSION=X.X.X. - Copia las notas de CHANGELOG en un nuevo Lanzamiento de GitHub.
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-auto-ssl.