[PATCH] Add ssl_provider directive (ticket #2449)

Nick Bogdanov nickrbogdanov at gmail.com
Tue Mar 14 15:29:10 UTC 2023


# HG changeset patch
# User Nick Bogdanov <nickrbogdanov at gmail.com>
# Date 1677975659 28800
#      Sat Mar 04 16:20:59 2023 -0800
# Node ID a00302750c65e122292da5094093e45f2f644600
# Parent  cffaf3f2eec8fd33605c2a37814f5ffc30371989
Add ssl_provider directive (ticket #2449).

This change allows nginx to load modules that use the new OpenSSL
Provider interface.  My primary use case involves securing the
webserver's private TLS key using a TPM2 chip, so it can't be stolen
if the server is compromised.  The way I tested this is as follows:

1. Install basic TPM2 support.  On Ubuntu 22.04 I used

    apt install tpm2-tools tpm2-abrmd libtss2-tcti-tabrmd0

2. Install https://github.com/tpm2-software/tpm2-openssl .  Version
1.2.0-rc0 or higher is required.  At the time of this writing, it's
likely you'll have to build from source.

3. Generate a parent key on your TPM (one-time setup):

    tpm2_createprimary -C o -g sha256 -G ecc -c primary_sh.ctx

    tpm2_evictcontrol -C o -c 0x81000001 || true

    tpm2_evictcontrol -C o -c primary_sh.ctx 0x81000001

4. Generate a TPM-backed RSA privkey and a corresponding self-signed
x509 cert:

    openssl genpkey -provider tpm2 -algorithm RSA
        -pkeyopt parent:0x81000001 -out rsakey.pem

    openssl req -provider tpm2 -provider default -x509
        -subj "/C=GB/CN=foo" -key rsakey.pem -out rsacert.pem

rsakey.pem will start with "-----BEGIN TSS2 PRIVATE KEY-----" to indicate
that the key material is encrypted with a key that is only available inside
the TPM chip.

5. At the start of nginx.conf, tell nginx to use the tpm2 provider
first, and then fall back to the default provider for unsupported
operations:

    ssl_provider tpm2;
    ssl_provider default;

6. Inside a "server {" section for an existing TLS server, point nginx
to the new TPM-backed cert and key:

    ssl_certificate         /tmp/rsacert.pem;
    ssl_certificate_key     /tmp/rsakey.pem;

If the ssl_provider option took effect, it will be able to recognize
the new TSS2 rsakey.pem and instruct the TPM chip to handle the signing
operation during the TLS handshake.

In my setup, the initial handshake time increased from 5ms->67ms after
enabling TPM2.  More details here:
https://github.com/tpm2-software/tpm2-openssl/issues/58

diff -r cffaf3f2eec8 -r a00302750c65 contrib/vim/syntax/nginx.vim
--- a/contrib/vim/syntax/nginx.vim	Thu Feb 02 23:38:48 2023 +0300
+++ b/contrib/vim/syntax/nginx.vim	Sat Mar 04 16:20:59 2023 -0800
@@ -620,6 +620,7 @@
 syn keyword ngxDirective contained ssl_prefer_server_ciphers
 syn keyword ngxDirective contained ssl_preread
 syn keyword ngxDirective contained ssl_protocols
+syn keyword ngxDirective contained ssl_provider
 syn keyword ngxDirective contained ssl_reject_handshake
 syn keyword ngxDirective contained ssl_session_cache
 syn keyword ngxDirective contained ssl_session_ticket_key
diff -r cffaf3f2eec8 -r a00302750c65 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c	Thu Feb 02 23:38:48 2023 +0300
+++ b/src/event/ngx_event_openssl.c	Sat Mar 04 16:20:59 2023 -0800
@@ -90,6 +90,7 @@
 
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
 
 
@@ -102,6 +103,13 @@
       0,
       NULL },
 
+    { ngx_string("ssl_provider"),
+      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+      ngx_openssl_provider,
+      0,
+      0,
+      NULL },
+
       ngx_null_command
 };
 
@@ -5939,6 +5947,26 @@
 #endif
 }
 
+static char *
+ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#ifdef OPENSSL_PROVIDER_SUPPORT
+    ngx_str_t  *value = cf->args->elts;
+
+    if (OSSL_PROVIDER_load(NULL, (char *)value[1].data) == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+                      "OSSL_PROVIDER_load(\"%V\") failed", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+
+#else
+
+    return "is not supported";
+
+#endif
+}
 
 static void
 ngx_openssl_exit(ngx_cycle_t *cycle)
diff -r cffaf3f2eec8 -r a00302750c65 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h	Thu Feb 02 23:38:48 2023 +0300
+++ b/src/event/ngx_event_openssl.h	Sat Mar 04 16:20:59 2023 -0800
@@ -28,6 +28,10 @@
 #ifndef OPENSSL_NO_OCSP
 #include <openssl/ocsp.h>
 #endif
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+#include <openssl/provider.h>
+#define OPENSSL_PROVIDER_SUPPORT
+#endif
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>


More information about the nginx-devel mailing list