nginx / njs-acme

Nginx NJS module runtime to work with ACME providers like Let's Encrypt for automated no-reload TLS certificate issue/renewal.
Apache License 2.0
57 stars 9 forks source link

TLS-ALPN challenge support #28

Open ivanitskiy opened 1 year ago

ivanitskiy commented 1 year ago

Is your feature request related to a problem? Please describe

ACME supports tls-alpn chalnages.

Describe the solution you'd like

Here is a potential solution:

use $ssl_preread_alpn_protocols to detect ALPN protocol and proxy traffic to the appropriate server.

stream {
  # set tls_port vari base on ALPN protocol
  map $ssl_preread_alpn_protocols $tls_port {
    ~\bacme-tls/1\b 9443;
    ~\bh2\b 10443;
    ~\bhttp/1.1\b 10443;
    default 11443;
  }

  # listen on :443 port and then proxy to the appropriate server based on ALPN protocol
  server {
    listen :443;
    ssl_preread on;
    proxy_pass 127.0.0.1:$tls_port;
  }

Then we can use js_set to read challenges from the FS similarly to how we currently read HTTP-01 chanallnages by doing this:

location ~ "^/\.well-known/acme-challenge/[-_A-Za-z0-9]{22,128}$" {
      js_content acme.challengeResponse;
    }

Here is an approximate nginx config:

stream {
  map $ssl_preread_alpn_protocols $tls_port {
    ~\bacme-tls/1\b 9443;
    ~\bh2\b 10443;
    ~\bhttp/1.1\b 10443;
    default 11443;
  }

  server {
    listen localhost:8443;
    ssl_preread on;
    proxy_pass 127.0.0.1:$tls_port;
  }

  server {
    js_set $challenge_crt acme.js_ch_cert;
    js_set $challenge_key acme.js_ch_key;

    listen localhost:9443 ssl;
    ssl_certificate     data:$challenge_crt;
    ssl_certificate_key data:$challenge_key;
    ssl_protocols TLSv1.2 TLSv1.3;

    return "this is a challenge server\n";
  }

  server {
    js_set $production_crt acme.js_ch_cert;
    js_set $production_key acme.js_ch_key;

    listen localhost:11443 ssl;
    ssl_certificate     data:$production_crt;
    ssl_certificate_key data:$production_key;
    ssl_protocols TLSv1.2 TLSv1.3;

    return "this is a production stream server\n";
  }
}

http {
  server {
  js_set $dynamic_ssl_cert acme.js_cert;
  js_set $dynamic_ssl_key acme.js_key;

    listen localhost:10443 ssl;
    ssl_certificate     data:$dynamic_ssl_cert;
    ssl_certificate_key data:$dynamic_ssl_key;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
      return 200 "production server response\n";
    }
  }
}

we need to consider adding a new function similar to clientAutoMode or updating it so both tls-alpm and HTTP challenges are supported. this requires some experiments and dining in.