Aller au contenu

auto-ssl: Enregistrement et renouvellement SSL à la volée (et gratuit) à l'intérieur de nginx-module-lua/nginx avec Let's Encrypt

Installation

Si vous n'avez pas configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

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

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-auto-ssl v0.13.1 publié le 01 octobre 2019.


CI

Enregistrement et renouvellement SSL à la volée (et gratuit) à l'intérieur de OpenResty/nginx avec Let's Encrypt.

Ce plugin OpenResty émet automatiquement et de manière transparente des certificats SSL de Let's Encrypt (une autorité de certification gratuite) à mesure que les demandes sont reçues. Cela fonctionne comme suit :

  • Une demande SSL pour un nom d'hôte SNI est reçue.
  • Si le système a déjà un certificat SSL pour ce domaine, il est immédiatement retourné (avec OCSP stapling).
  • Si le système n'a pas encore de certificat SSL pour ce domaine, il émet un nouveau certificat SSL de Let's Encrypt. La validation du domaine est gérée pour vous. Après avoir reçu le nouveau certificat (généralement dans quelques secondes), le nouveau certificat est enregistré, mis en cache et retourné au client (sans abandonner la demande originale).

Cela utilise la fonctionnalité ssl_certificate_by_lua dans OpenResty 1.9.7.2+.

En utilisant lua-resty-auto-ssl pour enregistrer des certificats SSL avec Let's Encrypt, vous acceptez l'accord d'abonnement Let's Encrypt.

Créez /etc/resty-auto-ssl et assurez-vous qu'il est accessible en écriture par l'utilisateur sous lequel vos

travailleurs nginx s'exécutent (dans cet exemple, "www-data").

$ sudo mkdir /etc/resty-auto-ssl $ sudo chown www-data /etc/resty-auto-ssl

Implémentez la configuration nécessaire dans votre configuration nginx. Voici un exemple minimal :

```nginx
events {
  worker_connections 1024;
}

http {
  # Le dictionnaire partagé "auto_ssl" doit être défini avec suffisamment d'espace de stockage pour
  # contenir vos données de certificat. 1 Mo de stockage contient des certificats pour
  # environ 100 domaines distincts.
  lua_shared_dict auto_ssl 1m;
  # Le dictionnaire partagé "auto_ssl_settings" est utilisé pour stocker temporairement divers paramètres
  # comme le secret utilisé par le serveur de hook sur le port 8999. Ne le changez pas ou
  # ne l'omettez pas.
  lua_shared_dict auto_ssl_settings 64k;

  # Un résolveur DNS doit être défini pour que l'OCSP stapling fonctionne.
  #
  # Cet exemple utilise le serveur DNS de Google. Vous pouvez vouloir utiliser les serveurs DNS par défaut de votre système,
  # qui peuvent être trouvés dans /etc/resolv.conf. Si votre réseau
  # n'est pas compatible IPv6, vous pouvez désactiver les résultats IPv6 en utilisant le
  # drapeau "ipv6=off" (comme "resolver 8.8.8.8 ipv6=off").
  resolver 8.8.8.8;

  # Tâches de configuration initiale.
  init_by_lua_block {
    auto_ssl = (require "resty.auto-ssl").new()

    -- Définissez une fonction pour déterminer quels domaines SNI gérer automatiquement
    -- et pour lesquels enregistrer de nouveaux certificats. Par défaut, aucun domaine n'est autorisé,
    -- donc cela doit être configuré.
    auto_ssl:set("allow_domain", function(domain)
      return true
    end)

    auto_ssl:init()
  }

  init_worker_by_lua_block {
    auto_ssl:init_worker()
  }

  # Serveur HTTPS
  server {
    listen 443 ssl;

    # Gestionnaire dynamique pour émettre ou retourner des certificats pour des domaines SNI.
    ssl_certificate_by_lua_block {
      auto_ssl:ssl_certificate()
    }

    # Vous devez toujours définir un fichier ssl_certificate statique pour que nginx démarre.
    #
    # Vous pouvez générer un fallback auto-signé avec :
    #
    # 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;
  }

  # Serveur HTTP
  server {
    listen 80;

    # Point de terminaison utilisé pour effectuer la vérification de domaine avec Let's Encrypt.
    location /.well-known/acme-challenge/ {
      content_by_lua_block {
        auto_ssl:challenge_server()
      }
    }
  }

  # Serveur interne fonctionnant sur le port 8999 pour gérer les tâches de certificat.
  server {
    listen 127.0.0.1:8999;

    # Augmentez la taille du tampon du corps, pour garantir que les POST internes peuvent toujours
    # analyser l'intégralité du contenu du POST en mémoire.
    client_body_buffer_size 128k;
    client_max_body_size 128k;

    location / {
      content_by_lua_block {
        auto_ssl:hook_server()
      }
    }
  }
}

Configuration

Des options de configuration supplémentaires peuvent être définies sur l'instance auto_ssl qui est créée :

allow_domain

Par défaut : function(domain, auto_ssl, ssl_options, renewal) return false end

Une fonction qui détermine si le domaine entrant doit automatiquement émettre un nouveau certificat SSL.

Par défaut, resty-auto-ssl ne procédera à aucun enregistrement SSL tant que vous n'avez pas défini la fonction allow_domain. Vous pouvez retourner true pour gérer tous les domaines possibles, mais soyez conscient que des noms d'hôtes SNI erronés peuvent alors être utilisés pour déclencher un nombre indéfini de tentatives d'enregistrement SSL (qui seront rejetées). Une meilleure approche peut être de mettre en liste blanche les domaines autorisés d'une manière ou d'une autre.

Les arguments de la fonction de rappel sont :

  • domain : Le domaine de la demande entrante.
  • auto_ssl : L'instance auto-ssl actuelle.
  • ssl_options : Un tableau d'options de configuration facultatives qui ont été passées à la fonction ssl_certificate. Cela peut être utilisé pour personnaliser le comportement sur une base par server nginx (voir exemple dans request_domain). Notez que cette option n'est pas passée lorsque cette fonction est appelée pour des renouvellements, donc votre fonction doit gérer cela en conséquence.
  • renewal : Valeur booléenne indiquant si cette fonction est appelée lors du renouvellement de certificat ou non. Lorsque true, l'argument ssl_options ne sera pas présent.

Lorsque vous utilisez l'adaptateur de stockage Redis, vous pouvez accéder à la connexion Redis actuelle à l'intérieur du rappel allow_domain en accédant à auto_ssl.storage.adapter:get_connection().

Exemple :

auto_ssl:set("allow_domain", function(domain, auto_ssl, ssl_options, renewal)
  return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)

dir

Par défaut : /etc/resty-auto-ssl

Le répertoire de base utilisé pour stocker la configuration, les fichiers temporaires et les fichiers de certificat (si vous utilisez l'adaptateur de stockage file). Ce répertoire doit être accessible en écriture par l'utilisateur sous lequel les travailleurs nginx s'exécutent.

Exemple :

auto_ssl:set("dir", "/some/other/location")

renew_check_interval

Par défaut : 86400

À quelle fréquence (en secondes) tous les domaines doivent être vérifiés pour les renouvellements de certificat. Par défaut, cela vérifie tous les 1 jour. Les certificats seront automatiquement renouvelés s'ils expirent dans moins de 30 jours.

Exemple :

auto_ssl:set("renew_check_interval", 172800)

storage_adapter

Par défaut : resty.auto-ssl.storage_adapters.file
Options : resty.auto-ssl.storage_adapters.file, resty.auto-ssl.storage_adapters.redis

Le mécanisme de stockage utilisé pour le stockage persistant des certificats SSL. Des adaptateurs de stockage basés sur des fichiers et sur Redis sont fournis, mais des adaptateurs externes personnalisés peuvent également être spécifiés (la valeur doit simplement être sur le lua_package_path).

L'adaptateur de stockage par défaut persiste les certificats dans des fichiers locaux. Cependant, vous pouvez envisager un autre adaptateur de stockage (comme Redis) pour plusieurs raisons : - Les E/S de fichiers provoquent un blocage dans OpenResty, ce qui doit être évité pour une performance optimale. Cependant, les fichiers ne sont lus et écrits que la première fois qu'un certificat est vu, puis les choses sont mises en cache en mémoire, donc la quantité réelle d'E/S de fichiers devrait être assez minimale. - Les fichiers locaux ne fonctionneront pas si les certificats doivent être partagés entre plusieurs serveurs (pour un environnement équilibré).

Exemple :

auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")

redis

Par défaut : { host = "127.0.0.1", port = 6379 }

Si l'adaptateur de stockage redis est utilisé, des options de connexion supplémentaires peuvent être spécifiées dans ce tableau. Accepte les options suivantes :

  • host : Hôte auquel se connecter (par défaut 127.0.0.1).
  • port : Port auquel se connecter (par défaut 6379).
  • socket : Au lieu de spécifier host et port pour se connecter, un chemin de socket unix peut être donné à la place (au format "unix:/path/to/unix.sock").
  • connect_options : Options de connexion supplémentaires à passer à la fonction Redis connect.
  • auth : Valeur à passer à la commande AUTH.
  • db : Le numéro de base de données Redis utilisé par lua-resty-auto-ssl pour enregistrer les certificats.
  • prefix : Préfixe à ajouter à toutes les clés stockées dans Redis avec cette chaîne.

Exemple :

auto_ssl:set("redis", {
  host = "10.10.10.1"
})

request_domain

Par défaut : function(ssl, ssl_options) return ssl.server_name() end

Une fonction qui détermine le nom d'hôte de la demande. Par défaut, le domaine SNI est utilisé, mais une fonction personnalisée peut être implémentée pour déterminer le nom de domaine pour les demandes non-SNI (en se basant sur quelque chose qui peut être déterminé en dehors de SSL, comme le port ou l'adresse IP qui a reçu la demande).

Les arguments de la fonction de rappel sont :

  • ssl : Une instance du module ngx.ssl.
  • ssl_options : Un tableau d'options de configuration facultatives qui ont été passées à la fonction ssl_certificate. Cela peut être utilisé pour personnaliser le comportement sur une base par server nginx.

Exemple :

Cet exemple, ainsi que les blocs server nginx associés, par défaut aux noms de domaine SNI, mais pour les clients non-SNI, répondra avec des hôtes prédéfinis en fonction du port de connexion. Les connexions au port 9000 enregistreront et retourneront un certificat pour foo.example.com, tandis que les connexions au port 9001 enregistreront et retourneront un certificat pour bar.example.com. Tous les autres ports retourneront le certificat de secours par défaut 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

Par défaut : le CA Let's Encrypt par défaut

URL de l'environnement Let's Encrypt à utiliser. Normalement, vous ne devriez pas définir cela, sauf si vous souhaitez utiliser l'environnement de staging de Let's Encrypt.

Exemple :

auto_ssl:set("ca", "https://some-other-letsencrypt.org/directory")

hook_server_port

Par défaut : 8999

En interne, nous utilisons un serveur spécial fonctionnant sur le port 8999 pour gérer les tâches de certificat. Le port utilisé pour ce service peut être changé ici. Veuillez noter que vous devrez également le changer dans votre configuration nginx.

Exemple :

auto_ssl:set("hook_server_port", 90)

json_adapter

Par défaut : resty.auto-ssl.json_adapters.cjson
Options : resty.auto-ssl.json_adapters.cjson, resty.auto-ssl.json_adapters.dkjson

L'adaptateur JSON à utiliser pour encoder et décoder JSON. Par défaut, il utilise cjson, qui est inclus avec les installations OpenResty et devrait probablement être utilisé dans la plupart des cas. Cependant, un adaptateur utilisant le pur Lua dkjson peut être utilisé pour les environnements où cjson peut ne pas être disponible (vous devrez installer manuellement la dépendance dkjson via luarocks pour utiliser cet adaptateur).

Les adaptateurs json cjson et dkjson sont fournis, mais des adaptateurs externes personnalisés peuvent également être spécifiés (la valeur doit simplement être sur le lua_package_path).

Exemple :

auto_ssl:set("json_adapter", "resty.auto-ssl.json_adapters.dkjson")

http_proxy_options

Par défaut : nil

Configurez un proxy HTTP à utiliser lors de l'envoi de requêtes OCSP stapling. Accepte un tableau d'options pour lua-resty-http's set_proxy_options.

Exemple :

auto_ssl:set("http_proxy_options", {
  http_proxy = "http://localhost:3128",
})

Configuration de ssl_certificate

La fonction ssl_certificate accepte un tableau facultatif d'options de configuration. Ces options peuvent être utilisées pour personnaliser et contrôler le comportement SSL sur une base par server nginx. Certaines options intégrées peuvent contrôler le comportement par défaut de lua-resty-auto-ssl, mais d'autres données personnalisées peuvent être données en tant qu'options, qui seront ensuite transmises aux fonctions de rappel allow_domain et request_domain.

Options de configuration intégrées :

generate_certs

Par défaut : true

Cette variable peut être utilisée pour désactiver la génération de certificats sur un emplacement de bloc serveur par serveur.

Exemple :

server {
  listen 8443 ssl;
  ssl_certificate_by_lua_block {
    auto_ssl:ssl_certificate({ generate_certs = false })
  }
}

Configuration avancée de Let's Encrypt

En interne, lua-resty-auto-ssl utilise dehydrated comme client Let's Encrypt. Si vous souhaitez ajuster des paramètres de niveau inférieur, comme la taille de la clé privée, l'algorithme de clé publique ou votre e-mail d'enregistrement, ces paramètres peuvent être configurés dans un fichier de configuration dehydrated personnalisé.

  • Pour une liste complète des options prises en charge, voir l'exemple de configuration de dehydrated.
  • Les fichiers de configuration dehydrated personnalisés peuvent être placés par défaut dans le répertoire /etc/resty-auto-ssl/letsencrypt/conf.d (ou ajustez le chemin si vous avez changé le paramètre dir par défaut de lua-resty-auto-ssl).

Exemple de /etc/resty-auto-ssl/letsencrypt/conf.d/custom.sh :

KEYSIZE="4096"
KEY_ALGO="rsa"
CONTACT_EMAIL="[email protected]"

Précautions

  • Hôtes autorisés : Par défaut, resty-auto-ssl ne procédera à aucun enregistrement SSL tant que vous n'avez pas défini la fonction allow_domain. Vous pouvez retourner true pour gérer tous les domaines possibles, mais soyez conscient que des noms d'hôtes SNI erronés peuvent alors être utilisés pour déclencher un nombre indéfini de tentatives d'enregistrement SSL (qui seront rejetées). Une meilleure approche peut être de mettre en liste blanche les domaines autorisés d'une manière ou d'une autre.
  • Code non fiable : Assurez-vous que votre serveur OpenResty où cela est installé ne peut pas exécuter de code non fiable. Les certificats et les clés privées doivent être lisibles par l'utilisateur du serveur web, il est donc important que ces données ne soient pas compromises.
  • Stockage de fichiers : L'adaptateur de stockage par défaut persiste les certificats dans des fichiers locaux. Cependant, vous pouvez envisager un autre adaptateur de stockage (comme Redis) pour plusieurs raisons :
  • Les E/S de fichiers provoquent un blocage dans OpenResty, ce qui doit être évité pour une performance optimale. Cependant, les fichiers ne sont lus et écrits que la première fois qu'un certificat est vu, puis les choses sont mises en cache en mémoire, donc la quantité réelle d'E/S de fichiers devrait être assez minimale.
  • Les fichiers locaux ne fonctionneront pas si les certificats doivent être partagés entre plusieurs serveurs (pour un environnement équilibré).

Développement

Après avoir vérifié le dépôt, Docker peut être utilisé pour exécuter la suite de tests :

$ docker-compose run --rm app make test

Les tests peuvent être trouvés dans le répertoire spec, et la suite de tests est implémentée en utilisant busted.

Processus de publication

Pour publier une nouvelle version sur LuaRocks :

  • Assurez-vous que CHANGELOG.md est à jour.
  • Déplacez le fichier rockspec vers le nouveau numéro de version (git mv lua-resty-auto-ssl-X.X.X-1.rockspec lua-resty-auto-ssl-X.X.X-1.rockspec), et mettez à jour les variables version et tag dans le fichier rockspec.
  • Commitez et taguez la publication (git tag -a vX.X.X -m "Tagging vX.X.X" && git push origin vX.X.X).
  • Exécutez make release VERSION=X.X.X.
  • Copiez les notes CHANGELOG dans une nouvelle publication GitHub.

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-auto-ssl.