diff options
| author | Vadim Zhestikov <v.zhestikov@f5.com> | 2026-02-02 14:46:00 -0800 |
|---|---|---|
| committer | VadimZhestikov <108960056+VadimZhestikov@users.noreply.github.com> | 2026-04-17 14:16:31 -0700 |
| commit | 98fc3bb78e8daef25c3d850c9cba8c2f787fb99e (patch) | |
| tree | 0efe4dbda50e46e41802d02df5e3802c9f5dbc2e /src | |
| parent | d7dd7e9ae4c2110f753cac02fd702eefef9fce16 (diff) | |
| download | nginx-98fc3bb78e8daef25c3d850c9cba8c2f787fb99e.tar.gz nginx-98fc3bb78e8daef25c3d850c9cba8c2f787fb99e.tar.bz2 | |
Stream: support ALPN for proxy_ssl upstream.
Added the proxy_ssl_alpn directive, which sets the list of protocols
to advertise via ALPN during upstream TLS handshakes. Each argument
is a complex value, so variables are accepted. In particular,
proxy_ssl_alpn $ssl_alpn_protocol;
inherits the protocol negotiated in the downstream TLS handshake.
When all evaluated values are empty or absent, no ALPN extension is
sent, equivalent to the directive not being set at all.
Closes #406 on GitHub.
Diffstat (limited to 'src')
| -rw-r--r-- | src/stream/ngx_stream_proxy_module.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 300bdf681..c358c8647 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -42,6 +42,7 @@ typedef struct { ngx_str_t ssl_ciphers; ngx_stream_complex_value_t *ssl_name; ngx_flag_t ssl_server_name; + ngx_array_t *ssl_alpn; ngx_flag_t ssl_verify; ngx_uint_t ssl_verify_depth; @@ -95,6 +96,8 @@ static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, #if (NGX_STREAM_SSL) static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s); +static char *ngx_stream_proxy_ssl_alpn_set_slot(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static char *ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, @@ -105,6 +108,7 @@ static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s); static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc); static void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c); static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_proxy_ssl_alpn(ngx_stream_session_t *s); static ngx_int_t ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s); static ngx_int_t ngx_stream_proxy_merge_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev); @@ -304,6 +308,13 @@ static ngx_command_t ngx_stream_proxy_commands[] = { offsetof(ngx_stream_proxy_srv_conf_t, ssl_server_name), NULL }, + { ngx_string("proxy_ssl_alpn"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_proxy_ssl_alpn_set_slot, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("proxy_ssl_verify"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -1044,6 +1055,61 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) static char * +ngx_stream_proxy_ssl_alpn_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + ngx_stream_proxy_srv_conf_t *pscf = conf; + + ngx_str_t *value; + ngx_uint_t i; + ngx_stream_complex_value_t *cv; + ngx_stream_compile_complex_value_t ccv; + + if (pscf->ssl_alpn != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + pscf->ssl_alpn = ngx_array_create(cf->pool, cf->args->nelts - 1, + sizeof(ngx_stream_complex_value_t)); + if (pscf->ssl_alpn == NULL) { + return NGX_CONF_ERROR; + } + + cv = ngx_array_push_n(pscf->ssl_alpn, cf->args->nelts - 1); + + for (i = 1; i < cf->args->nelts; i++) { + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[i]; + ccv.complex_value = &cv[i - 1]; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv[i - 1].lengths == NULL && value[i].len > 255) { + return "protocol too long"; + } + } + + return NGX_CONF_OK; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"proxy_ssl_alpn\" directive requires " + "OpenSSL with ALPN support"); + + return NGX_CONF_ERROR; +#endif +} + + +static char * ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -1200,6 +1266,13 @@ ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s) } } + if (pscf->ssl_alpn) { + if (ngx_stream_proxy_ssl_alpn(s) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + if (pscf->ssl_certificate && pscf->ssl_certificate->value.len && (pscf->ssl_certificate->lengths @@ -1400,6 +1473,82 @@ done: static ngx_int_t +ngx_stream_proxy_ssl_alpn(ngx_stream_session_t *s) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + size_t len; + u_char *p, *buf; + ngx_str_t proto; + ngx_uint_t i; + ngx_connection_t *c; + ngx_stream_upstream_t *u; + ngx_stream_complex_value_t *cv; + ngx_stream_proxy_srv_conf_t *pscf; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + u = s->upstream; + c = u->peer.connection; + + len = 0; + + cv = pscf->ssl_alpn->elts; + + for (i = 0; i < pscf->ssl_alpn->nelts; i++) { + + if (ngx_stream_complex_value(s, &cv[i], &proto) != NGX_OK) { + return NGX_ERROR; + } + + if (proto.len == 0 || proto.len > 255) { + continue; + } + + len += 1 + proto.len; + } + + if (len == 0) { + return NGX_OK; + } + + buf = ngx_pnalloc(c->pool, len); + if (buf == NULL) { + return NGX_ERROR; + } + + p = buf; + + for (i = 0; i < pscf->ssl_alpn->nelts; i++) { + + if (ngx_stream_complex_value(s, &cv[i], &proto) != NGX_OK) { + return NGX_ERROR; + } + + if (proto.len == 0 || proto.len > 255) { + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "upstream SSL ALPN: \"%V\"", &proto); + + *p++ = proto.len; + p = ngx_cpymem(p, proto.data, proto.len); + } + + if (SSL_set_alpn_protos(c->ssl->connection, buf, p - buf) != 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_set_alpn_protos() failed"); + return NGX_ERROR; + } + +#endif + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s) { ngx_str_t cert, key; @@ -2225,6 +2374,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf) conf->ssl_session_reuse = NGX_CONF_UNSET; conf->ssl_name = NGX_CONF_UNSET_PTR; conf->ssl_server_name = NGX_CONF_UNSET; + conf->ssl_alpn = NGX_CONF_UNSET_PTR; conf->ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_certificate = NGX_CONF_UNSET_PTR; @@ -2300,6 +2450,8 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0); + ngx_conf_merge_ptr_value(conf->ssl_alpn, prev->ssl_alpn, NULL); + ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 0); ngx_conf_merge_uint_value(conf->ssl_verify_depth, |
