auto-ssl: Registro e renovação de SSL em tempo real (e gratuito) dentro do nginx-module-lua/nginx com Let's Encrypt
Instalação
Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Então você pode prosseguir com os seguintes passos.
CentOS/RHEL 7 ou 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 com NGINX, certifique-se de que o nginx-module-lua está instalado.
Este documento descreve o lua-resty-auto-ssl v0.13.1 lançado em 01 de outubro de 2019.
Registro e renovação de SSL em tempo real (e gratuito) dentro do OpenResty/nginx com Let's Encrypt.
Este plugin OpenResty emite automaticamente e de forma transparente certificados SSL do Let's Encrypt (uma autoridade certificadora gratuita) à medida que as solicitações são recebidas. Funciona da seguinte forma:
- Uma solicitação SSL para um hostname SNI é recebida.
- Se o sistema já possui um certificado SSL para esse domínio, ele é retornado imediatamente (com OCSP stapling).
- Se o sistema ainda não possui um certificado SSL para este domínio, ele emite um novo certificado SSL do Let's Encrypt. A validação do domínio é tratada para você. Após receber o novo certificado (geralmente dentro de alguns segundos), o novo certificado é salvo, armazenado em cache e retornado ao cliente (sem descartar a solicitação original).
Isso utiliza a funcionalidade ssl_certificate_by_lua no OpenResty 1.9.7.2+.
Ao usar lua-resty-auto-ssl para registrar certificados SSL com Let's Encrypt, você concorda com o Contrato de Assinante do Let's Encrypt.
Crie /etc/resty-auto-ssl e certifique-se de que é gravável pelo usuário que seus
trabalhadores nginx estão executando (neste exemplo, "www-data").
$ sudo mkdir /etc/resty-auto-ssl $ sudo chown www-data /etc/resty-auto-ssl
Implemente a configuração necessária dentro do seu arquivo de configuração do nginx. Aqui está um exemplo mínimo:
```nginx
events {
worker_connections 1024;
}
http {
# O dicionário compartilhado "auto_ssl" deve ser definido com espaço de armazenamento suficiente para
# manter seus dados de certificado. 1MB de armazenamento mantém certificados para
# aproximadamente 100 domínios separados.
lua_shared_dict auto_ssl 1m;
# O dicionário compartilhado "auto_ssl_settings" é usado para armazenar temporariamente várias configurações
# como o segredo usado pelo servidor de hook na porta 8999. Não altere ou
# omita isso.
lua_shared_dict auto_ssl_settings 64k;
# Um resolvedor DNS deve ser definido para que o OCSP stapling funcione.
#
# Este exemplo usa o servidor DNS do Google. Você pode querer usar os servidores DNS
# padrão do seu sistema, que podem ser encontrados em /etc/resolv.conf. Se sua rede
# não for compatível com IPv6, você pode querer desativar resultados IPv6 usando a
# flag "ipv6=off" (como "resolver 8.8.8.8 ipv6=off").
resolver 8.8.8.8;
# Tarefas de configuração inicial.
init_by_lua_block {
auto_ssl = (require "resty.auto-ssl").new()
-- Defina uma função para determinar quais domínios SNI devem ser tratados automaticamente
-- e registrar novos certificados. Por padrão, não permite nenhum domínio,
-- então isso deve ser configurado.
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;
# Manipulador dinâmico para emitir ou retornar certificados para domínios SNI.
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
# Você ainda deve definir um arquivo ssl_certificate estático para o nginx iniciar.
#
# Você pode gerar um fallback autoassinado com:
#
# 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 usado para realizar a verificação de domínio com Let's Encrypt.
location /.well-known/acme-challenge/ {
content_by_lua_block {
auto_ssl:challenge_server()
}
}
}
# Servidor interno rodando na porta 8999 para lidar com tarefas de certificado.
server {
listen 127.0.0.1:8999;
# Aumente o tamanho do buffer do corpo, para garantir que os POSTs internos possam sempre
# analisar o conteúdo completo do POST na memória.
client_body_buffer_size 128k;
client_max_body_size 128k;
location / {
content_by_lua_block {
auto_ssl:hook_server()
}
}
}
}
Configuração
Opções de configuração adicionais podem ser definidas na instância auto_ssl que é criada:
allow_domain
Padrão: function(domain, auto_ssl, ssl_options, renewal) return false end
Uma função que determina se o domínio de entrada deve emitir automaticamente um novo certificado SSL.
Por padrão, o resty-auto-ssl não realizará nenhum registro SSL até que você defina a função allow_domain. Você pode retornar true para lidar com todos os domínios possíveis, mas esteja ciente de que nomes de host SNI falsos podem ser usados para acionar um número indefinido de tentativas de registro SSL (que serão rejeitadas). Uma abordagem melhor pode ser colocar em uma lista branca os domínios permitidos de alguma forma.
Os argumentos da função de callback são:
domain: O domínio da solicitação de entrada.auto_ssl: A instância atual do auto-ssl.ssl_options: Uma tabela de opções de configuração opcionais que foram passadas para assl_certificatefunction. Isso pode ser usado para personalizar o comportamento em uma base porserverdo nginx (veja o exemplo emrequest_domain). Observe que esta opção não é passada quando esta função é chamada para renovações, então sua função deve lidar com isso de acordo.renewal: Valor booleano indicando se esta função está sendo chamada durante a renovação do certificado ou não. Quandotrue, o argumentossl_optionsnão estará presente.
Ao usar o adaptador de armazenamento Redis, você pode acessar a conexão Redis atual dentro do callback allow_domain acessando auto_ssl.storage.adapter:get_connection().
Exemplo:
auto_ssl:set("allow_domain", function(domain, auto_ssl, ssl_options, renewal)
return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)
dir
Padrão: /etc/resty-auto-ssl
O diretório base usado para armazenar configuração, arquivos temporários e arquivos de certificado (se estiver usando o adaptador de armazenamento file). Este diretório deve ser gravável pelo usuário que os trabalhadores do nginx estão executando.
Exemplo:
auto_ssl:set("dir", "/some/other/location")
renew_check_interval
Padrão: 86400
Com que frequência (em segundos) todos os domínios devem ser verificados para renovações de certificado. O padrão é verificar a cada 1 dia. Os certificados serão renovados automaticamente se expirarem em menos de 30 dias.
Exemplo:
auto_ssl:set("renew_check_interval", 172800)
storage_adapter
Padrão: resty.auto-ssl.storage_adapters.file
Opções: resty.auto-ssl.storage_adapters.file, resty.auto-ssl.storage_adapters.redis
O mecanismo de armazenamento usado para armazenamento persistente dos certificados SSL. Adaptadores de armazenamento baseados em arquivos e redis são fornecidos, mas adaptadores externos personalizados também podem ser especificados (o valor simplesmente precisa estar no lua_package_path).
O adaptador de armazenamento padrão persiste os certificados em arquivos locais. No entanto, você pode querer considerar outro adaptador de armazenamento (como redis) por algumas razões: - A I/O de arquivos causa bloqueios no OpenResty, o que deve ser evitado para um desempenho ideal. No entanto, os arquivos são lidos e escritos apenas na primeira vez que um certificado é visto, e então as coisas são armazenadas em cache na memória, então a quantidade real de I/O de arquivos deve ser bastante mínima. - Arquivos locais não funcionarão se os certificados precisarem ser compartilhados entre vários servidores (para um ambiente balanceado).
Exemplo:
auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")
redis
Padrão: { host = "127.0.0.1", port = 6379 }
Se o adaptador de armazenamento redis estiver sendo usado, então opções de conexão adicionais podem ser especificadas nesta tabela. Aceita as seguintes opções:
host: Host para conectar (padrão é127.0.0.1).port: Porta para conectar (padrão é6379).socket: Em vez de especificarhosteportpara conectar, um caminho de socket unix pode ser dado em vez disso (no formato de"unix:/path/to/unix.sock").connect_options: Opções de conexão adicionais a serem passadas para a função Redisconnect.auth: Valor a ser passado para o comandoAUTH.db: O número do banco de dados Redis usado pelo lua-resty-auto-ssl para salvar certificados.prefix: Prefixo para todas as chaves armazenadas no Redis com esta string.
Exemplo:
auto_ssl:set("redis", {
host = "10.10.10.1"
})
request_domain
Padrão: function(ssl, ssl_options) return ssl.server_name() end
Uma função que determina o hostname da solicitação. Por padrão, o domínio SNI é usado, mas uma função personalizada pode ser implementada para determinar o nome do domínio para solicitações não-SNI (baseando o domínio em algo que pode ser determinado fora do SSL, como a porta ou o endereço IP que recebeu a solicitação).
Os argumentos da função de callback são:
ssl: Uma instância do módulongx.ssl.ssl_options: Uma tabela de opções de configuração opcionais que foram passadas para assl_certificatefunction. Isso pode ser usado para personalizar o comportamento em uma base porserverdo nginx.
Exemplo:
Este exemplo, junto com os blocos server do nginx, irá usar por padrão nomes de domínio SNI, mas para clientes não-SNI responderá com hosts predefinidos com base na porta de conexão. Conexões à porta 9000 registrarão e retornarão um certificado para foo.example.com, enquanto conexões à porta 9001 registrarão e retornarão um certificado para bar.example.com. Quaisquer outras portas retornarão o certificado de fallback padrão do 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
Padrão: a CA padrão do Let's Encrypt
URL do ambiente do Let's Encrypt a ser usado. Normalmente você não deve definir isso, a menos que queira usar o ambiente de teste do Let's Encrypt.
Exemplo:
auto_ssl:set("ca", "https://some-other-letsencrypt.org/directory")
hook_server_port
Padrão: 8999
Internamente, usamos um servidor especial rodando na porta 8999 para lidar com tarefas de certificado. A porta usada para este serviço pode ser alterada aqui. Observe que você também precisará alterá-la em sua configuração do nginx.
Exemplo:
auto_ssl:set("hook_server_port", 90)
json_adapter
Padrão: resty.auto-ssl.json_adapters.cjson
Opções: resty.auto-ssl.json_adapters.cjson, resty.auto-ssl.json_adapters.dkjson
O adaptador JSON a ser usado para codificar e decodificar JSON. O padrão é usar cjson, que está incluído nas instalações do OpenResty e deve ser usado na maioria dos casos. No entanto, um adaptador usando o puro Lua dkjson pode ser usado para ambientes onde cjson pode não estar disponível (você precisará instalar manualmente a dependência dkjson via luarocks para usar este adaptador).
Os adaptadores json cjson e dkjson são fornecidos, mas adaptadores externos personalizados também podem ser especificados (o valor simplesmente precisa estar no lua_package_path).
Exemplo:
auto_ssl:set("json_adapter", "resty.auto-ssl.json_adapters.dkjson")
http_proxy_options
Padrão: nil
Configure um proxy HTTP a ser usado ao fazer solicitações de OCSP stapling. Aceita uma tabela de opções para set_proxy_options do lua-resty-http.
Exemplo:
auto_ssl:set("http_proxy_options", {
http_proxy = "http://localhost:3128",
})
Configuração de ssl_certificate
A função ssl_certificate aceita uma tabela opcional de opções de configuração. Essas opções podem ser usadas para personalizar e controlar o comportamento SSL em uma base por server do nginx. Algumas opções internas podem controlar o comportamento padrão do lua-resty-auto-ssl, mas quaisquer outros dados personalizados podem ser fornecidos como opções, que serão passadas para as funções de callback allow_domain e request_domain.
Opções de configuração internas:
generate_certs
Padrão: true
Esta variável pode ser usada para desabilitar a geração de certificados em uma localização de bloco de servidor.
Exemplo:
server {
listen 8443 ssl;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate({ generate_certs = false })
}
}
Configuração Avançada do Let's Encrypt
Internamente, o lua-resty-auto-ssl usa dehydrated como seu cliente Let's Encrypt. Se você deseja ajustar configurações de nível inferior, como o tamanho da chave privada, algoritmo da chave pública ou seu e-mail de registro, essas configurações podem ser configuradas em um arquivo de configuração customizado do dehydrated.
- Para uma lista completa de opções suportadas, consulte o exemplo de configuração do dehydrated.
- Arquivos de configuração personalizados do dehydrated podem ser colocados dentro do diretório
/etc/resty-auto-ssl/letsencrypt/conf.dpor padrão (ou ajuste o caminho se você alterou a configuração padrão dodirdo lua-resty-auto-ssl).
Exemplo de /etc/resty-auto-ssl/letsencrypt/conf.d/custom.sh:
KEYSIZE="4096"
KEY_ALGO="rsa"
CONTACT_EMAIL="[email protected]"
Precauções
- Hosts Permitidos: Por padrão, o resty-auto-ssl não realizará nenhum registro SSL até que você defina a função
allow_domain. Você pode retornartruepara lidar com todos os domínios possíveis, mas esteja ciente de que nomes de host SNI falsos podem ser usados para acionar um número indefinido de tentativas de registro SSL (que serão rejeitadas). Uma abordagem melhor pode ser colocar em uma lista branca os domínios permitidos de alguma forma. - Código Não Confiável: Certifique-se de que seu servidor OpenResty onde isso está instalado não pode executar código não confiável. Os certificados e chaves privadas devem ser legíveis pelo usuário do servidor web, então é importante que esses dados não sejam comprometidos.
- Armazenamento de Arquivos: O adaptador de armazenamento padrão persiste os certificados em arquivos locais. No entanto, você pode querer considerar outro adaptador de armazenamento (como redis) por algumas razões:
- A I/O de arquivos causa bloqueios no OpenResty, o que deve ser evitado para um desempenho ideal. No entanto, os arquivos são lidos e escritos apenas na primeira vez que um certificado é visto, e então as coisas são armazenadas em cache na memória, então a quantidade real de I/O de arquivos deve ser bastante mínima.
- Arquivos locais não funcionarão se os certificados precisarem ser compartilhados entre vários servidores (para um ambiente balanceado).
Desenvolvimento
Após clonar o repositório, o Docker pode ser usado para executar a suíte de testes:
$ docker-compose run --rm app make test
Os testes podem ser encontrados no diretório spec, e a suíte de testes é implementada usando busted.
Processo de Lançamento
Para lançar uma nova versão no LuaRocks:
- Certifique-se de que o
CHANGELOG.mdestá atualizado. - Mova o arquivo rockspec para o novo número da versão (
git mv lua-resty-auto-ssl-X.X.X-1.rockspec lua-resty-auto-ssl-X.X.X-1.rockspec), e atualize as variáveisversionetagno arquivo rockspec. - Faça commit e marque o lançamento (
git tag -a vX.X.X -m "Tagging vX.X.X" && git push origin vX.X.X). - Execute
make release VERSION=X.X.X. - Copie as notas do CHANGELOG para um novo Lançamento no GitHub.
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub do nginx-module-auto-ssl.