Zum Inhalt

auto-ssl: On the fly (und kostenlos) SSL-Registrierung und -Erneuerung innerhalb von nginx-module-lua/nginx mit Let's Encrypt

Installation

Wenn Sie noch kein RPM-Repository-Abonnement eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder 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

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-auto-ssl v0.13.1, das am 01. Oktober 2019 veröffentlicht wurde.


CI

On the fly (und kostenlos) SSL-Registrierung und -Erneuerung innerhalb von OpenResty/nginx mit Let's Encrypt.

Dieses OpenResty-Plugin stellt automatisch und transparent SSL-Zertifikate von Let's Encrypt (einer kostenlosen Zertifizierungsstelle) aus, sobald Anfragen eingehen. Es funktioniert wie folgt:

  • Eine SSL-Anfrage für einen SNI-Hostname wird empfangen.
  • Wenn das System bereits ein SSL-Zertifikat für diese Domain hat, wird es sofort zurückgegeben (mit OCSP-Stapling).
  • Wenn das System noch kein SSL-Zertifikat für diese Domain hat, wird ein neues SSL-Zertifikat von Let's Encrypt ausgestellt. Die Domainvalidierung wird für Sie erledigt. Nach Erhalt des neuen Zertifikats (normalerweise innerhalb weniger Sekunden) wird das neue Zertifikat gespeichert, zwischengespeichert und dem Client zurückgegeben (ohne die ursprüngliche Anfrage abzubrechen).

Dies nutzt die Funktionalität ssl_certificate_by_lua in OpenResty 1.9.7.2+.

Durch die Verwendung von lua-resty-auto-ssl zur Registrierung von SSL-Zertifikaten bei Let's Encrypt stimmen Sie dem Let's Encrypt Subscriber Agreement zu.

Erstellen Sie /etc/resty-auto-ssl und stellen Sie sicher, dass es von dem Benutzer, unter dem Ihre nginx-Worker ausgeführt werden (in diesem Beispiel "www-data"), beschreibbar ist.

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

Implementieren Sie die erforderliche Konfiguration in Ihrer nginx-Konfiguration. Hier ist ein minimales Beispiel:

```nginx
events {
  worker_connections 1024;
}

http {
  # Das "auto_ssl" Shared Dict sollte mit ausreichend Speicherplatz definiert werden, um
  # Ihre Zertifikatsdaten zu speichern. 1 MB Speicherplatz hält Zertifikate für
  # ungefähr 100 separate Domains.
  lua_shared_dict auto_ssl 1m;
  # Das "auto_ssl_settings" Shared Dict wird verwendet, um vorübergehend verschiedene Einstellungen
  # wie das Geheimnis, das vom Hook-Server auf Port 8999 verwendet wird, zu speichern. Ändern oder
  # weglassen Sie es nicht.
  lua_shared_dict auto_ssl_settings 64k;

  # Ein DNS-Resolver muss definiert werden, damit OCSP-Stapling funktioniert.
  #
  # Dieses Beispiel verwendet den DNS-Server von Google. Möglicherweise möchten Sie die Standard-DNS-Server Ihres Systems verwenden,
  # die in /etc/resolv.conf zu finden sind. Wenn Ihr Netzwerk
  # nicht IPv6-kompatibel ist, möchten Sie möglicherweise IPv6-Ergebnisse deaktivieren, indem Sie das
  # "ipv6=off"-Flag verwenden (wie "resolver 8.8.8.8 ipv6=off").
  resolver 8.8.8.8;

  # Initiale Einrichtungsaufgaben.
  init_by_lua_block {
    auto_ssl = (require "resty.auto-ssl").new()

    -- Definieren Sie eine Funktion, um zu bestimmen, welche SNI-Domains automatisch behandelt
    -- und für die neue Zertifikate registriert werden sollen. Standardmäßig wird keine Domain zugelassen,
    -- daher muss dies konfiguriert werden.
    auto_ssl:set("allow_domain", function(domain)
      return true
    end)

    auto_ssl:init()
  }

  init_worker_by_lua_block {
    auto_ssl:init_worker()
  }

  # HTTPS-Server
  server {
    listen 443 ssl;

    # Dynamischer Handler für die Ausstellung oder Rückgabe von Zertifikaten für SNI-Domains.
    ssl_certificate_by_lua_block {
      auto_ssl:ssl_certificate()
    }

    # Sie müssen weiterhin eine statische ssl_certificate-Datei definieren, damit nginx starten kann.
    #
    # Sie können ein selbstsigniertes Fallback mit folgendem Befehl generieren:
    #
    # 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
  server {
    listen 80;

    # Endpunkt, der zur Durchführung der Domainüberprüfung mit Let's Encrypt verwendet wird.
    location /.well-known/acme-challenge/ {
      content_by_lua_block {
        auto_ssl:challenge_server()
      }
    }
  }

  # Interner Server, der auf Port 8999 läuft, um Zertifikatsaufgaben zu bearbeiten.
  server {
    listen 127.0.0.1:8999;

    # Erhöhen Sie die Größe des Body-Puffers, um sicherzustellen, dass die internen POSTs immer
    # den gesamten POST-Inhalt im Speicher analysieren können.
    client_body_buffer_size 128k;
    client_max_body_size 128k;

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

Konfiguration

Zusätzliche Konfigurationsoptionen können für die erstellte auto_ssl-Instanz festgelegt werden:

allow_domain

Standard: function(domain, auto_ssl, ssl_options, renewal) return false end

Eine Funktion, die bestimmt, ob die eingehende Domain automatisch ein neues SSL-Zertifikat ausstellen soll.

Standardmäßig führt resty-auto-ssl keine SSL-Registrierungen durch, bis Sie die Funktion allow_domain definieren. Sie können true zurückgeben, um alle möglichen Domains zu behandeln, aber beachten Sie, dass gefälschte SNI-Hostnamen dann verwendet werden können, um eine unbestimmte Anzahl von SSL-Registrierungsversuchen auszulösen (die abgelehnt werden). Ein besserer Ansatz könnte sein, die erlaubten Domains auf irgendeine Weise auf eine Whitelist zu setzen.

Die Argumente der Callback-Funktion sind:

  • domain: Die Domain der eingehenden Anfrage.
  • auto_ssl: Die aktuelle auto-ssl-Instanz.
  • ssl_options: Eine Tabelle von optionalen Konfigurationsoptionen, die an die ssl_certificate-Funktion übergeben wurden. Dies kann verwendet werden, um das Verhalten pro nginx server anzupassen (siehe Beispiel in request_domain). Beachten Sie, dass diese Option nicht übergeben wird, wenn diese Funktion für Erneuerungen aufgerufen wird, sodass Ihre Funktion dies entsprechend behandeln sollte.
  • renewal: Boolescher Wert, der angibt, ob diese Funktion während der Zertifikatserneuerung aufgerufen wird oder nicht. Wenn true, wird das Argument ssl_options nicht vorhanden sein.

Wenn Sie den Redis-Speicheradapter verwenden, können Sie die aktuelle Redis-Verbindung innerhalb des allow_domain-Callbacks abrufen, indem Sie auto_ssl.storage.adapter:get_connection() aufrufen.

Beispiel:

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

dir

Standard: /etc/resty-auto-ssl

Das Basisverzeichnis, das zum Speichern von Konfigurationen, temporären Dateien und Zertifikatsdateien (wenn der file-Speicheradapter verwendet wird) verwendet wird. Dieses Verzeichnis muss von dem Benutzer beschreibbar sein, unter dem die nginx-Worker ausgeführt werden.

Beispiel:

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

renew_check_interval

Standard: 86400

Wie häufig (in Sekunden) alle Domains auf Zertifikatserneuerungen überprüft werden sollen. Standardmäßig wird alle 1 Tag überprüft. Zertifikate werden automatisch erneuert, wenn sie in weniger als 30 Tagen ablaufen.

Beispiel:

auto_ssl:set("renew_check_interval", 172800)

storage_adapter

Standard: resty.auto-ssl.storage_adapters.file
Optionen: resty.auto-ssl.storage_adapters.file, resty.auto-ssl.storage_adapters.redis

Der Speichermechanismus, der für die persistente Speicherung der SSL-Zertifikate verwendet wird. Es werden dateibasierte und redis-basierte Speicheradapter bereitgestellt, aber auch benutzerdefinierte externe Adapter können angegeben werden (der Wert muss einfach im lua_package_path vorhanden sein).

Der Standard-Speicheradapter speichert die Zertifikate in lokalen Dateien. Sie möchten jedoch möglicherweise einen anderen Speicheradapter (wie Redis) in Betracht ziehen, aus mehreren Gründen: - Datei-I/O verursacht Blocking in OpenResty, was für optimale Leistung vermieden werden sollte. Dateien werden jedoch nur beim ersten Mal gelesen und geschrieben, wenn ein Zertifikat gesehen wird, und dann werden die Dinge im Speicher zwischengespeichert, sodass die tatsächliche Menge an Datei-I/O ziemlich minimal sein sollte. - Lokale Dateien funktionieren nicht, wenn die Zertifikate über mehrere Server hinweg geteilt werden müssen (für eine Lastenausgleichsumgebung).

Beispiel:

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

redis

Standard: { host = "127.0.0.1", port = 6379 }

Wenn der redis-Speicheradapter verwendet wird, können zusätzliche Verbindungsoptionen in dieser Tabelle angegeben werden. Akzeptiert die folgenden Optionen:

  • host: Host, zu dem eine Verbindung hergestellt werden soll (standardmäßig 127.0.0.1).
  • port: Port, zu dem eine Verbindung hergestellt werden soll (standardmäßig 6379).
  • socket: Anstelle von host und port, um eine Verbindung herzustellen, kann stattdessen ein Unix-Socket-Pfad angegeben werden (im Format von "unix:/path/to/unix.sock").
  • connect_options: Zusätzliche Verbindungsoptionen, die an die Redis connect-Funktion übergeben werden.
  • auth: Wert, der an den AUTH-Befehl übergeben wird.
  • db: Die Redis-Datenbanknummer, die von lua-resty-auto-ssl verwendet wird, um Zertifikate zu speichern.
  • prefix: Alle in Redis gespeicherten Schlüssel mit diesem String präfixieren.

Beispiel:

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

request_domain

Standard: function(ssl, ssl_options) return ssl.server_name() end

Eine Funktion, die den Hostnamen der Anfrage bestimmt. Standardmäßig wird die SNI-Domain verwendet, aber eine benutzerdefinierte Funktion kann implementiert werden, um den Domainnamen für Nicht-SNI-Anfragen zu bestimmen (indem die Domain auf etwas basiert, das außerhalb von SSL bestimmt werden kann, wie der Port oder die IP-Adresse, die die Anfrage empfangen hat).

Die Argumente der Callback-Funktion sind:

  • ssl: Eine Instanz des ngx.ssl-Moduls.
  • ssl_options: Eine Tabelle von optionalen Konfigurationsoptionen, die an die ssl_certificate-Funktion übergeben wurden. Dies kann verwendet werden, um das Verhalten pro nginx server anzupassen.

Beispiel:

Dieses Beispiel, zusammen mit den begleitenden nginx server-Blöcken, wird standardmäßig auf SNI-Domainnamen zurückgreifen, aber für Nicht-SNI-Clients mit vordefinierten Hosts basierend auf dem verbindenden Port antworten. Verbindungen zu Port 9000 registrieren und geben ein Zertifikat für foo.example.com zurück, während Verbindungen zu Port 9001 ein Zertifikat für bar.example.com registrieren und zurückgeben. Alle anderen Ports geben das Standard-nginx-Fallback-Zertifikat zurück.

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

Standard: das Standard-CA von Let's Encrypt

URL der Let's Encrypt-Umgebung, die verwendet werden soll. Normalerweise sollten Sie dies nicht festlegen, es sei denn, Sie möchten die Staging-Umgebung von Let's Encrypt nutzen.

Beispiel:

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

hook_server_port

Standard: 8999

Intern verwenden wir einen speziellen Server, der auf Port 8999 läuft, um Zertifikatsaufgaben zu bearbeiten. Der für diesen Dienst verwendete Port kann hier geändert werden. Bitte beachten Sie, dass Sie ihn auch in Ihrer nginx-Konfiguration ändern müssen.

Beispiel:

auto_ssl:set("hook_server_port", 90)

json_adapter

Standard: resty.auto-ssl.json_adapters.cjson
Optionen: resty.auto-ssl.json_adapters.cjson, resty.auto-ssl.json_adapters.dkjson

Der JSON-Adapter, der zum Kodieren und Dekodieren von JSON verwendet wird. Standardmäßig wird cjson verwendet, der mit OpenResty-Installationen gebündelt ist und in den meisten Fällen wahrscheinlich verwendet werden sollte. Ein Adapter, der das reine Lua dkjson verwendet, kann jedoch für Umgebungen verwendet werden, in denen cjson möglicherweise nicht verfügbar ist (Sie müssen die dkjson-Abhängigkeit manuell über luarocks installieren, um diesen Adapter zu verwenden).

cjson- und dkjson-JSON-Adapter werden bereitgestellt, aber auch benutzerdefinierte externe Adapter können angegeben werden (der Wert muss einfach im lua_package_path vorhanden sein).

Beispiel:

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

http_proxy_options

Standard: nil

Konfigurieren Sie einen HTTP-Proxy, der bei der Durchführung von OCSP-Stapling-Anfragen verwendet werden soll. Akzeptiert eine Tabelle von Optionen für lua-resty-http's set_proxy_options.

Beispiel:

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

ssl_certificate Konfiguration

Die ssl_certificate-Funktion akzeptiert eine optionale Tabelle von Konfigurationsoptionen. Diese Optionen können verwendet werden, um das SSL-Verhalten pro nginx server anzupassen und zu steuern. Einige integrierte Optionen können das Standardverhalten von lua-resty-auto-ssl steuern, aber auch andere benutzerdefinierte Daten können als Optionen angegeben werden, die dann an die allow_domain und request_domain Callback-Funktionen weitergegeben werden.

Integrierte Konfigurationsoptionen:

generate_certs

Standard: true

Diese Variable kann verwendet werden, um die Generierung von Zertifikaten pro Serverblock-Standort zu deaktivieren.

Beispiel:

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

Erweiterte Let's Encrypt-Konfiguration

Intern verwendet lua-resty-auto-ssl dehydrated als seinen Let's Encrypt-Client. Wenn Sie niedrigere Einstellungen wie die Größe des privaten Schlüssels, den öffentlichen Schlüsselalgorithmus oder Ihre Registrierungs-E-Mail anpassen möchten, können diese Einstellungen in einer benutzerdefinierten dehydrated-Konfigurationsdatei konfiguriert werden.

  • Für eine vollständige Liste der unterstützten Optionen siehe dehydrated's Beispielkonfiguration.
  • Benutzerdefinierte dehydrated-Konfigurationsdateien können standardmäßig im Verzeichnis /etc/resty-auto-ssl/letsencrypt/conf.d abgelegt werden (oder passen Sie den Pfad an, wenn Sie die Standardeinstellung für das lua-resty-auto-ssl dir-Setting geändert haben).

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

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

Vorsichtsmaßnahmen

  • Erlaubte Hosts: Standardmäßig führt resty-auto-ssl keine SSL-Registrierungen durch, bis Sie die Funktion allow_domain definieren. Sie können true zurückgeben, um alle möglichen Domains zu behandeln, aber beachten Sie, dass gefälschte SNI-Hostnamen dann verwendet werden können, um eine unbestimmte Anzahl von SSL-Registrierungsversuchen auszulösen (die abgelehnt werden). Ein besserer Ansatz könnte sein, die erlaubten Domains auf irgendeine Weise auf eine Whitelist zu setzen.
  • Untrusted Code: Stellen Sie sicher, dass Ihr OpenResty-Server, auf dem dies installiert ist, keinen nicht vertrauenswürdigen Code ausführen kann. Die Zertifikate und privaten Schlüssel müssen vom Webserverbenutzer lesbar sein, daher ist es wichtig, dass diese Daten nicht kompromittiert werden.
  • Dateispeicherung: Der Standard-Speicheradapter speichert die Zertifikate in lokalen Dateien. Sie möchten jedoch möglicherweise einen anderen Speicheradapter (wie Redis) in Betracht ziehen, aus mehreren Gründen:
  • Datei-I/O verursacht Blocking in OpenResty, was für optimale Leistung vermieden werden sollte. Dateien werden jedoch nur beim ersten Mal gelesen und geschrieben, wenn ein Zertifikat gesehen wird, und dann werden die Dinge im Speicher zwischengespeichert, sodass die tatsächliche Menge an Datei-I/O ziemlich minimal sein sollte.
  • Lokale Dateien funktionieren nicht, wenn die Zertifikate über mehrere Server hinweg geteilt werden müssen (für eine Lastenausgleichsumgebung).

Entwicklung

Nach dem Auschecken des Repos kann Docker verwendet werden, um die Test-Suite auszuführen:

$ docker-compose run --rm app make test

Tests finden Sie im spec-Verzeichnis, und die Test-Suite ist mit busted implementiert.

Veröffentlichungsprozess

Um eine neue Version zu LuaRocks zu veröffentlichen:

  • Stellen Sie sicher, dass CHANGELOG.md auf dem neuesten Stand ist.
  • Verschieben Sie die rockspec-Datei auf die neue Versionsnummer (git mv lua-resty-auto-ssl-X.X.X-1.rockspec lua-resty-auto-ssl-X.X.X-1.rockspec) und aktualisieren Sie die Variablen version und tag in der rockspec-Datei.
  • Committen und taggen Sie die Veröffentlichung (git tag -a vX.X.X -m "Tagging vX.X.X" && git push origin vX.X.X).
  • Führen Sie make release VERSION=X.X.X aus.
  • Kopieren Sie die CHANGELOG-Notizen in eine neue GitHub-Veröffentlichung.

GitHub

Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-auto-ssl.