mirror of https://github.com/nginx/nginx.git
Merge 812e356d82
into 78d1ab5a2c
This commit is contained in:
commit
a0168d0bc7
|
@ -0,0 +1,403 @@
|
|||
|
||||
# NGINX OpenSSL Encrypted Client Hello (ECH) integration.
|
||||
|
||||
> [!NOTE]
|
||||
> This documentation probably doesn't belong here, nor as a single file, but
|
||||
> may be useful to have in one place as we process the PR. TODO: find out where
|
||||
> to put the various bits and pieces once those are stable.
|
||||
|
||||
ECH is specified in
|
||||
[draft-ietf-tls-esni](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/).
|
||||
This documentation assumes a basic familiarity with the ECH specification.
|
||||
|
||||
This build only supports ECH "shared-mode" where the NGINX instance does the
|
||||
ECH decryption and also hosts both the ECH `public-name` and `backend` web
|
||||
sites. ECH "split-mode" where the NGINX instance only does ECH decryption but
|
||||
passes the TLS session on to a different backend service requires changes to
|
||||
OpenSSL that have yet to be merged to the ECH feature branch. There is a
|
||||
separate proof-of-concept implementation for that, but that is not documented
|
||||
here. (For more on ECH "split-mode" see the
|
||||
[defo-project-PoC](https://github.com/defo-project/ech-dev-utils/blob/main/howtos/nginx.md).)
|
||||
|
||||
## Build
|
||||
|
||||
### OpenSSL
|
||||
|
||||
> [!NOTE]
|
||||
> ECH is not yet a part of an OpenSSL release, our current goal is that ECH be
|
||||
> part of an OpenSSL 4.0 release in spring 2026.
|
||||
|
||||
There is client and server ECH code in the OpenSSL ECH feature branch at
|
||||
[https://github.com/openssl/openssl/tree/feature/ech](https://github.com/openssl/openssl/tree/feature/ech).
|
||||
At present, ECH-enabling NGINX therefore requires building from source, using
|
||||
the OpenSSL ECH feature branch.
|
||||
|
||||
To get the ECH feature branch:
|
||||
|
||||
```bash
|
||||
$ cd /home/user/code
|
||||
$ git clone https://github.com/openssl/openssl/ openssl-for-nginx
|
||||
$ cd openssl-for-nginx
|
||||
$ git checkout feature/ech
|
||||
```
|
||||
|
||||
Then an option to build NGINX is:
|
||||
|
||||
```bash
|
||||
$ cd /home/user/code
|
||||
$ git clone https://github.com/sftcd/nginx.git
|
||||
$ cd nginx
|
||||
$ ./auto/configure --with-debug --prefix=nginx --with-http_ssl_module --with-openssl=/home/user/code/openssl-for-nginx --with-openssl-opt="--debug" --with-http_v2_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module
|
||||
$ make
|
||||
...stuff...
|
||||
```
|
||||
|
||||
This results in an NGINX binary in `objs/nginx` with a statically linked
|
||||
OpenSSL, so as not to disturb system libraries.
|
||||
|
||||
### BoringSSL
|
||||
|
||||
BoringSSL is also supported by NGINX and also supports ECH, so to build
|
||||
with that, instead of our ECH-enabled OpenSSL:
|
||||
|
||||
```bash
|
||||
cd $HOME/code
|
||||
git clone https://boringssl.googlesource.com/boringssl
|
||||
cd boringssl
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/code/boringssl/inst -DBUILD_SHARED_LIBS=1
|
||||
make
|
||||
...
|
||||
make install
|
||||
```
|
||||
|
||||
Then an option to build NGINX is:
|
||||
|
||||
```bash
|
||||
$ cd /home/user/code
|
||||
$ git clone https://github.com/sftcd/nginx.git
|
||||
$ cd nginx
|
||||
$ ./auto/configure --prefix=nginx --with-cc-opt="-I $HOME/code/boringssl/inst/include" --with-ld-opt="-L $HOME/code//boringssl/inst/lib" --with-http_v2_module --with-http_ssl_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module
|
||||
$ make
|
||||
...stuff...
|
||||
```
|
||||
|
||||
This results in an NGINX binary in `objs/nginx` with a statically linked
|
||||
OpenSSL, so as not to disturb system libraries.
|
||||
|
||||
## ECH Key Generation and Publication
|
||||
|
||||
In the remaining, we describe a configuration that uses `example.com` as the
|
||||
ECH `public-name` and where `foo.example.com` is a web-site for which we want
|
||||
ECH to be used, with both hosted on the same NGINX instance.
|
||||
|
||||
Using ECH requries that NGINX load an ECH key pair with a private value for ECH
|
||||
decryption. Browsers will require that the public component of that key pair be
|
||||
published in the DNS. With OpenSSL we generate and store that key pair in a PEM
|
||||
formatted file as shown below.
|
||||
|
||||
To generate ECH PEM files, use the openssl binary produced by the build above
|
||||
(which is `/home/user/code/openssl-for-nginx/.openssl/bin/openssl`) to generate
|
||||
an ECH key pair and store the result in a PEM file. You should also supply the
|
||||
`public-name` required by the ECH protocol.
|
||||
|
||||
Key generation operations should be carried out under whatever local account is
|
||||
used for NGINX configuration.
|
||||
|
||||
```bash
|
||||
~# OSSL=/home/user/code/openssl-for-nginx/.openssl/bin/openssl
|
||||
~# mkdir -p /etc/nginx/echkeydir
|
||||
~# chmod 700 /etc/nginx/echkeydir
|
||||
~# cd /etc/nginx/echkeydir
|
||||
~# $OSSL ech -public-name example.com -o example.com.pem.ech
|
||||
~# cat example.com.pem.ech
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VuBCIEIJi22Im2rJ/lJqzNFZdGfsVfmknXAc8xz3fYPhD0Na5I
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN ECHCONFIG-----
|
||||
AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEA
|
||||
AQALZXhhbXBsZS5vcmcAAA==
|
||||
-----END ECHCONFIG-----
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The January 2025 lighttpd web server release included ECH and adopted a
|
||||
> naming convention for ECH PEM files that their names ought end in `.ech`.
|
||||
> This PR follows that covention.
|
||||
|
||||
The ECHConfig value then needs to be published in an HTTPS resource record in
|
||||
the DNS, so as to be accessible as shown below:
|
||||
|
||||
```bash
|
||||
$ dig +short HTTPS foo.example.com
|
||||
1 . ech=AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEAAQALZXhhbXBsZS5vcmcAAA==
|
||||
$
|
||||
```
|
||||
|
||||
Various other fields may be included in an HTTPS resource record. For many
|
||||
NGINX instances, existing methods for publishing DNS records may be used to
|
||||
achieve the above. In some cases, one might use [A well-known URI for
|
||||
publishing service
|
||||
parameters](https://datatracker.ietf.org/doc/html/draft-ietf-tls-wkech)
|
||||
designed to assist web servers in handling e.g. frequent ECH key rotation.
|
||||
|
||||
The `dig` example above assumes support for HTTPS RRs, for earlier
|
||||
versions of `dig` one would see something like:
|
||||
|
||||
``
|
||||
$ dig +short -t type65 foo.example.com
|
||||
\# 165 00010000040004D56C6C65000500820080FE0D003CF700200020189E 5FD51BC7527C67CB4883B4A79CC39642FE446965A473B7AB1E3A45F3 3058000400010001000D636F7665722E6465666F2E69650000FE0D00 3C44002000201DE542C51EF072BD7250FB486E812A697130C844602F D3148347457C685B1916000400010001000D636F7665722E6465666F 2E69650000000600102A00C6C0000001160005000000000010
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
To enable ECH for an NGINX instance, configure a directory name via the
|
||||
`ssl_echkeydir` directive where that directory contains a set of ECH PEM key
|
||||
files. The `ssl_echkeydir` directive should be in the "http" section of an
|
||||
NGINX configuration as shown in the example below. All ECH PEM files in that
|
||||
directory that are successfully decoded will be loaded.
|
||||
|
||||
The NGINX instance also needs to include a virtual server that matches the
|
||||
ECH `public_name` so that the ECH fallback can work. The first virtual
|
||||
server in the example below does this.
|
||||
|
||||
```
|
||||
http {
|
||||
log_format withech '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" "$ech_status"';
|
||||
access_log /var/log/nginx/access.log withech;
|
||||
ssl_echkeydir /etc/nginx/echkeydir;
|
||||
server {
|
||||
listen 443 default_server ssl;
|
||||
http2 on;
|
||||
ssl_certificate /etc/nginx/example.com.crt;
|
||||
ssl_certificate_key /etc/nginx/example.com.priv;
|
||||
ssl_protocols TLSv1.3;
|
||||
server_name example.com;
|
||||
location / {
|
||||
root /var/www/dir-example.com;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
ssl_certificate /etc/nginx/example.com.crt;
|
||||
ssl_certificate_key /etc/nginx/example.com.priv;
|
||||
ssl_protocols TLSv1.3;
|
||||
server_name foo.example.com;
|
||||
location / {
|
||||
root /var/www/dir-foo.example.com;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ssl_echkeydir` directive can also be used with the
|
||||
stream module, in the same manner.
|
||||
|
||||
## Logs
|
||||
|
||||
You can log ECH status information in the normal `access.log` by adding
|
||||
`$ech_status` to the `log_format`, e.g. the stanza below adds ECH status to the
|
||||
normal `combined` log format:
|
||||
|
||||
```
|
||||
log_format withech '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent"
|
||||
"ECH: $ssl_ech_status/$ssl_server_name/$ssl_ech_outer_sni"';
|
||||
access_log /var/log/nginx/access.log withech;
|
||||
```
|
||||
|
||||
That results in log lines like the following:
|
||||
|
||||
```
|
||||
127.0.0.1 - - [12/Oct/2025:18:54:07 +0100] "GET /index.html HTTP/1.1" 200 494 "-" "-"
|
||||
"ECH: GREASED/foo.example.com/-"
|
||||
127.0.0.1 - - [12/Oct/2025:18:54:15 +0100] "GET /index.html HTTP/1.1" 200 486 "-" "-"
|
||||
"ECH: GREASED/example.com/-"
|
||||
127.0.0.1 - - [12/Oct/2025:18:54:23 +0100] "GET /index.html HTTP/1.1" 200 494 "-" "-"
|
||||
"ECH: SUCCESS/foo.example.com/example.com"
|
||||
127.0.0.1 - - [12/Oct/2025:18:54:31 +0100] "GET /index.html HTTP/1.1" 200 494 "-" "-"
|
||||
"ECH: SUCCESS/foo.example.com/example.com"
|
||||
```
|
||||
|
||||
When ECH has succeeded with OpenSSL, then the outer SNI and inner SNI are included in that
|
||||
order. If a client GREASEd or didn't try ECH at all, and no outer SNI was
|
||||
provided, the HTTP host header will be shown instead. Connections that did not
|
||||
use TLS show that. The TLS version is not specifically shown, so TLSv1.2
|
||||
connections will show up as `NOT_TRIED`.
|
||||
|
||||
With BoringSSL, we don't get access to the outer SNI value, so that will
|
||||
be shown as `"-'`, nor the more detailed ECH status values (only SUCCESS/FAILED).
|
||||
|
||||
At start-up, and on configuration re-load, NGINX will log (to `error.log` at
|
||||
the "notice" log level) the names of ECH PEM files successfully loaded and the
|
||||
total number of ECH keys loaded, for each `server` stanza in the configuration.
|
||||
Errors in loading keys are also logged and may result in the server not
|
||||
starting. Example log lines would be:
|
||||
|
||||
```
|
||||
2025/10/12 18:54:07 [notice] 768265#0: ngx_ssl_load_echkeys, worked for: /etc/nginx/echkeydir/echconfig.pem.ech
|
||||
2025/10/12 18:54:07 [notice] 768265#0: ngx_ssl_load_echkeys, worked for: /etc/nginx/echkeydir/d13.pem.ech
|
||||
2025/10/12 18:54:07 [notice] 768265#0: ngx_ssl_load_echkeys, total keys loaded: 2
|
||||
```
|
||||
|
||||
## Testing with curl
|
||||
|
||||
If you have a build of curl that supports ECH, then you can
|
||||
use that. In my local test setup, the following works:
|
||||
|
||||
```
|
||||
$ ~/code/curl/src/curl --ech ecl:AD7+DQA6EwAgACCJDbbP6N6GbNTQT6v9cwGtT8YUgGCpqLqiNnDnsTIAIAAEAAEAAQALZXhhbXBsZS5jb20AAA== --connect-to foo.example.com:443:localhost:5443 https://foo.example.com/index.html --cacert cadir/oe.csr -v
|
||||
...
|
||||
* ECH: result: status is succeeded, inner is foo.example.com, outer is example.com
|
||||
...
|
||||
```
|
||||
|
||||
## CGI variables
|
||||
|
||||
We set the following variables for, e.g. PHP code:
|
||||
|
||||
- ``SSL_ECH_STATUS`` - ``success`` means that, others also mean what they say
|
||||
- ``SSL_ECH_INNER_SNI`` - has value that was in inner ClientHello SNI (or
|
||||
``NONE``)
|
||||
- ``SSL_ECH_OUTER_SNI`` - has value that was in outer ClientHello SNI (or
|
||||
``NONE``)
|
||||
|
||||
To see those using fastcgi you need to include the following in the relevant
|
||||
NGINX config:
|
||||
|
||||
```
|
||||
fastcgi_param SSL_ECH_STATUS $ssl_ech_status;
|
||||
fastcgi_param SSL_ECH_INNER_SNI $ssl_server_name;
|
||||
fastcgi_param SSL_ECH_OUTER_SNI $ssl_ech_outer_sni;
|
||||
```
|
||||
|
||||
## Code changes
|
||||
|
||||
- If the OpenSSL or BoringSSL library has ECH support, then ECH code is
|
||||
compiled. That is detected if either `SSL_OP_ECH_GREASE` (OpenSSL) or
|
||||
`SSL_R_ECH_REJECTED` (BoringSSL) is defined, which is checked in
|
||||
`src/events/ngx_event_openssl.c`. In other words, if NGINX is built using an
|
||||
OpenSSL version that has ECH support, then that will be used. If the OpenSSL
|
||||
version doesn't have ECH then most of the ECH-specific code in NGINX is
|
||||
compiled out.
|
||||
|
||||
- `src/http/modules/ngx_http_ssl_module.h` and
|
||||
`src/http/modules/ngx_http_ssl_module.c` define the new `ssl_echkeydir`
|
||||
directive and the variables that become visible to e.g. PHP code.
|
||||
|
||||
- `ngx_ssl_load_echkeys()` in `src/event/ngx_event_openssl.c` loads ECH PEM files as
|
||||
directed by the `ssl_echkeydir` directive, and enables shared-mode ECH
|
||||
decryption if some ECH keys are loaded. If `ssl_echkeydir` is set, but no keys
|
||||
are loaded, that results in an error and NGINX exits. Similarly, if
|
||||
`ssl_echkeydir` is set, but ECH support is not available, the server will
|
||||
exit. (As BoringSSL doesn't directly support the ECH PEM file format used,
|
||||
`ngx_ssl_ech_boring_read_pem` does the work of OpenSSL's
|
||||
`OSSL_ECHSTORE_read_pem`.)
|
||||
|
||||
- `ngx_ssl_get_ech_status()` and `ngx_ssl_get_ech_outer_sni()` also in
|
||||
`src/event/ngx_event_openssl.c` provide for setting the CGI variables
|
||||
mentioned above.
|
||||
|
||||
- Similar changes are made for the stream module in
|
||||
`src/stream/ngx_stream_ssl_module.c`
|
||||
and `src/stream/ngx_stream_ssl_module.h`.
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> `ngx_ssl_load_echkeys()` will include the public component all loaded keys in the ECH
|
||||
> `retry-configs` in the fallback scenario. If desired, we could add a naming
|
||||
> convention or additional configuration setting to distinguish which to
|
||||
> include in `retry-configs` or not. For now, we assume that'd better be done
|
||||
> in a subsequent PR, if experience shows the feature is really useful/needed.
|
||||
> (We can envisage some odd deployments where that might be the case, but not
|
||||
> clear those'd really happen - it'd seem to need loads of key pairs or else
|
||||
> some that are never published in the DNS that we don't want to expose to
|
||||
> random clients - neither seems compelling.)
|
||||
|
||||
## Reloading ECH keys
|
||||
|
||||
ECH uses a form of ephemeral-static (Elliptic curve) Diffie-Hellman key
|
||||
exchange, so in order to get better forward secrecy, there is a need to perhaps
|
||||
frequently rotate ECH keys. For example, some widely-used ECH-enabled web
|
||||
services rotate ECH keys hourly. That may be done e.g. via a cronjob and using
|
||||
[A well-known URI for publishing service
|
||||
parameters](https://datatracker.ietf.org/doc/html/draft-ietf-tls-wkech). In
|
||||
such a setup, the set of ECH PEM files in the `ssl_echkeydir` directory will
|
||||
change hourly, with the directory likely to contain perhaps three ECH PEM files
|
||||
(curent, hour-before and two-hours before). This creates a need to reload ECH
|
||||
PEM files regularly.
|
||||
|
||||
Sending a SIGHUP signal to the running process causes it to reload it's
|
||||
configuration, so if `$PIDFILE` is a file with the NGINX server process-id:
|
||||
|
||||
```bash
|
||||
$ kill -SIGHUP `cat $PIDFILE`
|
||||
```
|
||||
|
||||
When ECH PEM files are loaded or re-loaded that's logged to the error log,
|
||||
e.g.:
|
||||
|
||||
```
|
||||
2023/12/03 20:09:13 [notice] 273779#0: ngx_ssl_load_echkeys, worked for: /home/user/lt/echkeydir/echconfig.pem.ech
|
||||
2023/12/03 20:09:13 [notice] 273779#0: ngx_ssl_load_echkeys, worked for: /home/user/lt/echkeydir/d13.pem.ech
|
||||
2023/12/03 20:09:13 [notice] 273779#0: ngx_ssl_load_echkeys, total keys loaded: 2
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The ECH integration released by the lighttpd web server in January 2025
|
||||
> allows configuration of a timer used to cause ECH PEM files to be reloaded if
|
||||
> those have changed. This PR does not include that functionality but it could
|
||||
> be added if desired, e.g. if regularly reloading the entire NGINX
|
||||
> configuration is considered undesirable. See the [lighttpd
|
||||
> code](https://github.com/lighttpd/lighttpd1.4/blob/master/src/mod_openssl.c#L799)
|
||||
> for details.
|
||||
|
||||
## Debugging
|
||||
|
||||
To run NGINX in ``gdb`` you probably want to uncomment the ``daemon off;`` and
|
||||
``master_process off;`` lines in your config file. You probably also want to
|
||||
build with `CFLAGS="-g -O0"` to turn off optimization, and then, e.g. if you
|
||||
wanted to debug into the ``ngx_ssl_load_echkeys()`` function:
|
||||
|
||||
```bash
|
||||
$ gdb ~/code/nginx/objs/nginx
|
||||
GNU gdb (Ubuntu 13.1-2ubuntu2) 13.1
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
Type "show copying" and "show warranty" for details.
|
||||
This GDB was configured as "x86_64-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
<https://www.gnu.org/software/gdb/bugs/>.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
<http://www.gnu.org/software/gdb/documentation/>.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word"...
|
||||
Reading symbols from /home/user/code/nginx/objs/nginx...
|
||||
(gdb) b ngx_ssl_load_echkeys
|
||||
Breakpoint 1 at 0x1402e9: file src/event/ngx_event_openssl.c, line 1469.
|
||||
(gdb) r -c nginxmin.conf
|
||||
Starting program: /home/user/code/nginx/objs/nginx -c nginxmin.conf
|
||||
[Thread debugging using libthread_db enabled]
|
||||
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
|
||||
|
||||
Breakpoint 1, ngx_ssl_load_echkeys (ssl=ssl@entry=0x555555db64d8, dirname=dirname@entry=0x555555db6568)
|
||||
at src/event/ngx_event_openssl.c:1469
|
||||
1469 {
|
||||
(gdb) c
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, ngx_ssl_load_echkeys (ssl=ssl@entry=0x555555dbad68, dirname=dirname@entry=0x555555dbadf8)
|
||||
at src/event/ngx_event_openssl.c:1469
|
||||
1469 {
|
||||
(gdb) c
|
||||
Continuing.
|
||||
[Detaching after fork from child process 522259]
|
||||
```
|
|
@ -14,6 +14,20 @@
|
|||
#endif
|
||||
|
||||
|
||||
/* check defines from <openssl/ssl.h> for ECH support */
|
||||
#if !defined(SSL_OP_ECH_GREASE) && !defined(SSL_R_ECH_REJECTED)
|
||||
#define OPENSSL_NO_ECH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Boring needs us to handle ECH PEM file content directly, so we
|
||||
* need to know a bit more about HPKE internals
|
||||
*/
|
||||
#if !defined(OPENSSL_NO_ECH) && defined(OPENSSL_IS_BORINGSSL)
|
||||
#include <openssl/hpke.h>
|
||||
#define OSSL_ECH_FOR_RETRY 1
|
||||
#endif
|
||||
|
||||
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
|
||||
|
||||
|
||||
|
@ -1572,6 +1586,310 @@ ngx_ssl_passwords_cleanup(void *data)
|
|||
}
|
||||
|
||||
|
||||
#ifndef OPENSSL_NO_ECH
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
#if defined(BORINGSSL_API_VERSION)
|
||||
static ngx_int_t
|
||||
ngx_ssl_ech_boring_read_pem(ngx_ssl_t *ssl, SSL_ECH_KEYS *keys,
|
||||
const char *fname, int is_retry_config)
|
||||
{
|
||||
BIO *bio;
|
||||
long configlen;
|
||||
u_char *config, key[32];
|
||||
size_t keylen;
|
||||
EVP_PKEY *pkey;
|
||||
EVP_HPKE_KEY *hpkey;
|
||||
|
||||
pkey = NULL;
|
||||
hpkey = NULL;
|
||||
|
||||
bio = BIO_new_file((char *) fname, "r");
|
||||
if (bio == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"BIO_new_file(\"%s\") failed", fname);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* PEM file with PKCS#8 PrivateKey followed by ECHConfigList,
|
||||
* https://datatracker.ietf.org/doc/html/draft-farrell-tls-pemesni
|
||||
*/
|
||||
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
if (pkey == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"PEM_read_bio_PrivateKey(\"%s\") failed",
|
||||
fname);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (PEM_bytes_read_bio(&config, &configlen, NULL, "ECHCONFIG", bio,
|
||||
NULL, NULL)
|
||||
!= 1)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"PEM_bytes_read_bio(\"%s\") failed",
|
||||
fname);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Construct EVP_HPKE_KEY from private key */
|
||||
|
||||
if (EVP_PKEY_id(pkey) != EVP_PKEY_X25519) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"EVP_PKEY_id(\"%s\") unsupported ECH key type, "
|
||||
"only X25519 keys are supported on this platform",
|
||||
fname);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
keylen = 32;
|
||||
|
||||
if (EVP_PKEY_get_raw_private_key(pkey, key, &keylen) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"EVP_PKEY_get_raw_private_key() failed");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
|
||||
hpkey = EVP_HPKE_KEY_new();
|
||||
if (hpkey == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"EVP_HPKE_KEY_new() failed");
|
||||
}
|
||||
|
||||
if (EVP_HPKE_KEY_init(hpkey, EVP_hpke_x25519_hkdf_sha256(),
|
||||
key, keylen) != 1)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"EVP_HPKE_KEY_init() failed");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* PEM file contains ECHConfigList, whereas SSL_ECH_KEYS_add()
|
||||
* expects ECHConfig, without the 2-byte length prefix
|
||||
*/
|
||||
|
||||
if (SSL_ECH_KEYS_add(keys, is_retry_config, config + 2, configlen - 2,
|
||||
hpkey)
|
||||
!= 1)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_ECH_KEYS_add() failed");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
EVP_HPKE_KEY_free(hpkey);
|
||||
hpkey = NULL;
|
||||
|
||||
OPENSSL_free(config);
|
||||
config = NULL;
|
||||
|
||||
BIO_free(bio);
|
||||
bio = NULL;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
if (pkey) {
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
|
||||
if (config) {
|
||||
OPENSSL_free(config);
|
||||
}
|
||||
|
||||
if (hpkey) {
|
||||
EVP_HPKE_KEY_free(hpkey);
|
||||
}
|
||||
|
||||
ngx_explicit_memzero(&key, 32);
|
||||
|
||||
return NGX_ERROR;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load key files called <name>.ech we find in the ssl_echkeydir directory */
|
||||
static ngx_int_t
|
||||
ngx_ssl_load_echkeys(ngx_ssl_t *ssl, ngx_str_t *dirname)
|
||||
{
|
||||
int somekeyworked, numkeys, maxkeyfiles;
|
||||
char *den, *last4, privname[PATH_MAX];
|
||||
size_t elen, nlen;
|
||||
ngx_dir_t thedir;
|
||||
ngx_int_t nrv;
|
||||
struct stat thestat;
|
||||
#if !defined(BORINGSSL_API_VERSION)
|
||||
OSSL_ECHSTORE *es;
|
||||
#else
|
||||
SSL_ECH_KEYS *keys;
|
||||
#endif
|
||||
|
||||
#if defined(BORINGSSL_API_VERSION)
|
||||
keys = SSL_ECH_KEYS_new();
|
||||
if (keys == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#else
|
||||
es = OSSL_ECHSTORE_new(NULL, NULL);
|
||||
if (es == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, error allocating store" );
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
nrv = ngx_open_dir(dirname, &thedir);
|
||||
if (nrv != NGX_OK) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, error opening %s", dirname->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
somekeyworked = 0;
|
||||
numkeys = 0;
|
||||
maxkeyfiles = 1024; /* 1024 private key files (maxkeyfiles) is plenty */
|
||||
elen = dirname->len;
|
||||
for ( ;; ) {
|
||||
nrv = ngx_read_dir(&thedir);
|
||||
if (nrv != NGX_OK) {
|
||||
break;
|
||||
}
|
||||
den = (char *)ngx_de_name(&thedir);
|
||||
nlen = strlen(den);
|
||||
if (nlen > 4) {
|
||||
last4 = den + nlen - 4;
|
||||
if (strncmp(last4, ".ech", 4)) {
|
||||
continue;
|
||||
}
|
||||
if ((elen + 1 + nlen + 1) >= PATH_MAX) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, name too long: %s with %s",
|
||||
dirname->data, den);
|
||||
continue;
|
||||
}
|
||||
snprintf(privname, PATH_MAX, "%s/%s", dirname->data, den);
|
||||
if (!--maxkeyfiles) {
|
||||
/* so we don't loop forever, ever */
|
||||
ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, too many files to check!");
|
||||
ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, hardcoded maxkeyfiles = 1024");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (stat(privname, &thestat) == 0) {
|
||||
const int is_retry_config = OSSL_ECH_FOR_RETRY;
|
||||
#if defined(BORINGSSL_API_VERSION)
|
||||
|
||||
if (NGX_OK == ngx_ssl_ech_boring_read_pem(ssl, keys, privname,
|
||||
is_retry_config)) {
|
||||
ngx_ssl_error(NGX_LOG_NOTICE, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, worked for: %s",
|
||||
privname);
|
||||
somekeyworked = 1;
|
||||
numkeys++;
|
||||
}
|
||||
else {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, failed for: %s",
|
||||
privname);
|
||||
}
|
||||
#else
|
||||
BIO *in = BIO_new_file(privname, "r");
|
||||
|
||||
if (in != NULL
|
||||
&& 1 == OSSL_ECHSTORE_read_pem(es, in, is_retry_config)) {
|
||||
ngx_ssl_error(NGX_LOG_NOTICE, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, worked for: %s",
|
||||
privname);
|
||||
somekeyworked = 1;
|
||||
}
|
||||
else {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, failed for: %s",
|
||||
privname);
|
||||
}
|
||||
BIO_free_all(in);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
ngx_close_dir(&thedir);
|
||||
|
||||
if (somekeyworked == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys loaded no keys but ECH configured");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#if defined(BORINGSSL_API_VERSION)
|
||||
if (1 != SSL_CTX_set1_ech_keys(ssl->ctx, keys)) {
|
||||
SSL_ECH_KEYS_free(keys);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
SSL_ECH_KEYS_free(keys);
|
||||
#else
|
||||
if (OSSL_ECHSTORE_num_keys(es, &numkeys) != 1) {
|
||||
OSSL_ECHSTORE_free(es);
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys OSSL_ECHSTORE_num_keys failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (1 != SSL_CTX_set1_echstore(ssl->ctx, es)) {
|
||||
OSSL_ECHSTORE_free(es);
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys: SSL_CTX_set1_echstore failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
OSSL_ECHSTORE_free(es);
|
||||
#endif
|
||||
ngx_ssl_error(NGX_LOG_NOTICE, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys, total keys loaded: %d", numkeys);
|
||||
return NGX_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_echkeydir(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *dir)
|
||||
{
|
||||
#ifndef OPENSSL_NO_ECH
|
||||
if (!dir) {
|
||||
return NGX_OK;
|
||||
}
|
||||
if (dir->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
if (ngx_conf_full_name(cf->cycle, dir, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_load_echkeys(ssl, dir) != NGX_OK) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ngx_ssl_load_echkeys error for %s", dir->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
return NGX_OK;
|
||||
#else
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"ECH configured but not supported");
|
||||
return NGX_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
{
|
||||
|
@ -5336,6 +5654,84 @@ ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
|||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifndef OPENSSL_NO_ECH
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
int echrv;
|
||||
char *inner_sni, *outer_sni;
|
||||
|
||||
inner_sni = NULL;
|
||||
outer_sni = NULL;
|
||||
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
|
||||
switch (echrv) {
|
||||
case SSL_ECH_STATUS_NOT_TRIED:
|
||||
ngx_str_set(s, "NOT_TRIED");
|
||||
break;
|
||||
case SSL_ECH_STATUS_FAILED:
|
||||
ngx_str_set(s, "FAILED");
|
||||
break;
|
||||
case SSL_ECH_STATUS_BAD_NAME:
|
||||
ngx_str_set(s, "WORKED_BAD_NAME");
|
||||
break;
|
||||
case SSL_ECH_STATUS_SUCCESS:
|
||||
ngx_str_set(s, "SUCCESS");
|
||||
break;
|
||||
case SSL_ECH_STATUS_GREASE:
|
||||
ngx_str_set(s, "GREASED");
|
||||
break;
|
||||
case SSL_ECH_STATUS_BACKEND:
|
||||
ngx_str_set(s, "INNER");
|
||||
break;
|
||||
default:
|
||||
ngx_str_set(s, "STATUS_ERROR");
|
||||
break;
|
||||
}
|
||||
OPENSSL_free(inner_sni);
|
||||
OPENSSL_free(outer_sni);
|
||||
#else
|
||||
if (SSL_ech_accepted(c->ssl->connection)) {
|
||||
ngx_str_set(s, "SUCCESS");
|
||||
} else {
|
||||
ngx_str_set(s, "FAILED");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_ech_outer_sni(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#if !defined(OPENSSL_NO_ECH) && !defined(OPENSSL_IS_BORINGSSL)
|
||||
int echrv;
|
||||
char *inner_sni, *outer_sni;
|
||||
|
||||
inner_sni = NULL;
|
||||
outer_sni = NULL;
|
||||
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
|
||||
if (echrv == SSL_ECH_STATUS_SUCCESS && outer_sni) {
|
||||
s->len = ngx_strlen(outer_sni);
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_memcpy(s->data, outer_sni, s->len);
|
||||
} else {
|
||||
ngx_str_set(s, "");
|
||||
}
|
||||
OPENSSL_free(inner_sni);
|
||||
OPENSSL_free(outer_sni);
|
||||
#else
|
||||
/* boring doesn't give us the outer SNI */
|
||||
ngx_str_set(s, "");
|
||||
#endif
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
|
|
|
@ -297,6 +297,7 @@ enum ssl_select_cert_result_t ngx_ssl_select_certificate(
|
|||
|
||||
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
|
||||
ngx_uint_t flags);
|
||||
ngx_int_t ngx_ssl_echkeydir(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *dir);
|
||||
|
||||
void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
|
||||
ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);
|
||||
|
@ -326,6 +327,10 @@ ngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool,
|
|||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_ech_outer_sni(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
|
|
|
@ -215,6 +215,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
|||
offsetof(ngx_http_ssl_srv_conf_t, session_tickets),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_echkeydir"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, echkeydir),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_ticket_key"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
|
@ -355,6 +362,12 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
|||
{ ngx_string("ssl_curve"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_curve, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_status"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_status, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_outer_sni"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_outer_sni, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
|
@ -625,6 +638,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
|||
* sscf->ocsp_responder = { 0, NULL };
|
||||
* sscf->stapling_file = { 0, NULL };
|
||||
* sscf->stapling_responder = { 0, NULL };
|
||||
* sscf->echkeydir = { 0, NULL} ;
|
||||
*/
|
||||
|
||||
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
|
||||
|
@ -692,6 +706,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
|
||||
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->echkeydir, prev->echkeydir, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
|
||||
"");
|
||||
ngx_conf_merge_str_value(conf->trusted_certificate,
|
||||
|
@ -872,6 +888,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_echkeydir(cf, &conf->ssl, &conf->echkeydir) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ typedef struct {
|
|||
ngx_ssl_cache_t *certificate_cache;
|
||||
|
||||
ngx_str_t dhparam;
|
||||
ngx_str_t echkeydir;
|
||||
ngx_str_t ecdh_curve;
|
||||
ngx_str_t client_certificate;
|
||||
ngx_str_t trusted_certificate;
|
||||
|
|
|
@ -147,6 +147,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
|
|||
offsetof(ngx_stream_ssl_srv_conf_t, dhparam),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_echkeydir"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_srv_conf_t, echkeydir),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_ecdh_curve"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
|
@ -357,6 +364,12 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = {
|
|||
{ ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_status"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_status, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_outer_sni"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_outer_sni, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
|
@ -876,6 +889,7 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf)
|
|||
* sscf->ocsp_responder = { 0, NULL };
|
||||
* sscf->stapling_file = { 0, NULL };
|
||||
* sscf->stapling_responder = { 0, NULL };
|
||||
* sscf->echkeydir = { 0, NULL };
|
||||
*/
|
||||
|
||||
sscf->handshake_timeout = NGX_CONF_UNSET_MSEC;
|
||||
|
@ -941,6 +955,8 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
|
||||
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->echkeydir, prev->echkeydir, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
|
||||
"");
|
||||
ngx_conf_merge_str_value(conf->trusted_certificate,
|
||||
|
@ -1116,6 +1132,10 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_echkeydir(cf, &conf->ssl, &conf->echkeydir) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef struct {
|
|||
ngx_ssl_cache_t *certificate_cache;
|
||||
|
||||
ngx_str_t dhparam;
|
||||
ngx_str_t echkeydir;
|
||||
ngx_str_t ecdh_curve;
|
||||
ngx_str_t client_certificate;
|
||||
ngx_str_t trusted_certificate;
|
||||
|
|
Loading…
Reference in New Issue