Opened 22 months ago
Last modified 11 months ago
#2449 new enhancement
Allow using OpenSSL 3.0 "provider" API instead of deprecated "engine" API
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | nginx-core | Version: | 1.23.x |
Keywords: | openssl, provider, tpm, pkcs11, engine | Cc: | |
uname -a: | Linux www 5.15.0-58-generic #64-Ubuntu SMP Thu Jan 5 11:43:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux | ||
nginx -V: |
nginx version: nginx/1.18.0 (Ubuntu)
built with OpenSSL 3.0.2 15 Mar 2022 TLS SNI support enabled configure arguments: --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-d8gVax/nginx-1.18.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --add-dynamic-module=/build/nginx-d8gVax/nginx-1.18.0/debian/modules/http-geoip2 --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module |
Description
I would like to use hardware encryption to protect my webserver's TLS privkey from theft. My server has a TPM 2.0 chip which supports this, but it's unnecessarily difficult to configure it in nginx. I'm proposing a change to make the process more user-friendly.
For background: old versions of OpenSSL supported HSMs and hardware offloading through the ENGINE API. In nginx this is enabled through the ssl_engine
directive: hxxps://github.com/Infineon/optiga-tpm-cheatsheet#nginx--curl
(Please s/hxxps/https/ due to Trac spam filter)
In OpenSSL 3.0 the authors introduced a "Provider API" which is intended to replace the old ENGINE API. It works in a similar way. When using the CLI to sign or create TPM-backed keys, you add -provider tpm2 -provider base
to the arguments: hxxps://github.com/Infineon/optiga-tpm-cheatsheet#pem-encoded-key-object-2
I'm running Ubuntu 22.04 LTS on this webserver. This Linux distribution has a tpm2-openssl package ( hxxps://github.com/tpm2-software/tpm2-openssl ) which conforms to the new OpenSSL 3.0 Provider API. The CLI examples on the optiga-tpm-cheatsheet work right out of the box, with no extra configuration. This makes it quick and easy to set up hardware-backed keys, just by installing a single package. For instance, anyone running this distro on an x86 Linux PC can do:
sudo apt install tpm2-openssl
openssl genpkey -provider tpm2 -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out testkey.priv
echo "test" | openssl pkeyutl -provider tpm2 -provider base -digest sha256 -inkey testkey.priv -sign -rawin -hexdump
But unfortunately, at this time nginx only supports the ENGINE API, not the Provider API. In order to use hardware backed keys with nginx, users would need to compile, install, and configure the legacy tpm2tss ENGINE implementation, and keep it up to date themselves without help from the Debian/Ubuntu package maintainers.
I believe that with a small tweak to nginx, it would be possible for users to specify e.g.
ssl_provider tpm2,base
to tell OpenSSL 3.x to use the tpm2-openssl Provider to support hardware-backed private keys in nginx.
Change History (9)
comment:1 by , 22 months ago
comment:2 by , 22 months ago
What I found when enabling the tpm2+base providers globally in openssl.cnf, is that applications could utilize the encrypted TSS2 privkeys but they lost the ability to sign using standard non-TPM-backed privkeys. Thus forcing me to choose between "all TPM2" or "all default," systemwide.
I think that if nginx called SSL_CTX_config(ctx, "nginx") then I could add an nginx-specific provider configuration to openssl.cnf and that would largely avoid this downside? But maybe there's a better way to do this without an nginx code change.
comment:3 by , 22 months ago
It is certainly possible to provide an nginx-specific OpenSSL configuration file via the OPENSSL_CONF
environment variable.
comment:6 by , 16 months ago
For the record, the above commit makes it possible to provide nginx-specific configuration in system-wide openssl.cnf. For example, following the PROVIDERS.md example configuration:
openssl_conf = openssl_init [openssl_init] providers = provider_sect [provider_sect] default = default_sect legacy = legacy_sect [default_sect] activate = 1 [legacy_sect] activate = 1
It is now possible to provide nginx-specific configuration, by using nginx
instead of openssl_conf
:
nginx = openssl_init [openssl_init] providers = provider_sect [provider_sect] default = default_sect legacy = legacy_sect [default_sect] activate = 1 [legacy_sect] activate = 1
In particular, this can be used to configure providers specifically for nginx via system-wide OpenSSL configuration file, without using nginx-specific OpenSSL configuration file and OPENSSL_CONF
.
comment:7 by , 15 months ago
It is possible to load providers via openssl.cnf
For example
[openssl_init] providers = provider_sect # List of providers to load [provider_sect] default = default_sect pkcs11 = pkcs11_sect [default_sect] activate = 1 [pkcs11_sect] module = /usr/local/lib/ossl-modules/pkcs11.so pkcs11-module-path = /usr/local/lib/softhsm/libsofthsm2.so activate = 1
But unfortunately, nginx does not support selecting a specific provider if using the ssl_certificate_key directive.
The value provider:name:id cannot be specified instead of the file, which would load a secret key with a specified id from the OpenSSL provider name.
comment:8 by , 13 months ago
The value provider:name:id cannot be specified instead of the file, which would load a secret key with a specified id from the OpenSSL provider name.
Specifically for the tpm2 provider as mentioned in the ticket this does not seem to be needed: it provides appropriate PEM handling routines which make it possible to load provider-backed keys just like normal PEM-encoded keys from files.
For pkcs11 provider this does not seem to be implemented though (at least yet), and instead requires explicit support for loading keys via the OSSL_STORE API. It indeed might worth implementing loading keys (and probably other objects, such as certificates and CRLs) via OSSL_STORE API.
comment:9 by , 11 months ago
In addition to simplifying the use-cases described above, I found the patch on the mailing list really useful for enabling Open Quantum Safe's oqsprovider specifically for nginx, without enabling it system-wide in "openssl.cnf". I'd really like this functionality in mainline.
It was also useful in the situation where nginx was built with custom OpenSSL to test other things, loading providers defined in "openssl.cnf" does not function as expected in those cases, being able to manually specify one is very useful.
Note that it should be possible to load OpenSSL providers using the configuration file, see README-PROVIDERS.md in OpenSSL sources for an example.
Support for loading providers directly from nginx configuration might still be useful from simplicity point of view, though it does not look like something required to use tpm2-openssl provider.