mirror of
https://github.com/nginx/nginx.git
synced 2026-05-13 09:36:42 +00:00
Core: add PROXY protocol v2 TLV passthrough from incoming header.
When the downstream connection carries an incoming PROXY protocol v2 header, the operator can forward specific TLVs to the upstream PP v2 header using the new proxy_protocol_passthrough directive (implemented in the stream and mail modules). This commit adds the infrastructure: ngx_proxy_protocol_write_conf_t gains two fields: passthrough - ngx_array_t of ngx_uint_t type bytes to forward passthrough_all - flag: forward every incoming type except 0x03 ngx_proxy_protocol_eval_tlvs() gains a passthrough section that runs after the user-TLV loop and before the SSL auto-fill section. Passthrough beats auto-fill for the same TLV type; explicit proxy_protocol_tlv and suppression (empty value) beat passthrough. Three new static helpers support the precedence logic: ngx_proxy_protocol_v2_tlv_has_type() - type in output array? ngx_proxy_protocol_v2_tlv_has_ssl_raw() - ssl-raw in output array? ngx_proxy_protocol_v2_user_owns_type() - type configured by user? ngx_proxy_protocol_v2_add_passthrough() is the public config-time helper called by the directive parsers. It rejects CRC32c (0x03), which is invalid on a re-assembled header, and SSL sub-TLV names (ssl_version, ssl_cn, etc.) which cannot be selectively extracted from the raw SSL TLV body at passthrough time.
This commit is contained in:
parent
e75bfc5b8e
commit
7b584f5210
2 changed files with 233 additions and 10 deletions
|
|
@ -951,18 +951,97 @@ static const ngx_uint_t ngx_proxy_protocol_v2_ssl_sub_types[] = {
|
|||
#endif
|
||||
|
||||
|
||||
/* check if a given top-level TLV type is already in the output array */
|
||||
static ngx_uint_t
|
||||
ngx_proxy_protocol_v2_tlv_has_type(ngx_array_t *tlvs, ngx_uint_t type)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_proxy_protocol_write_tlv_t *tlv;
|
||||
|
||||
tlv = tlvs->elts;
|
||||
for (i = 0; i < tlvs->nelts; i++) {
|
||||
if (!tlv[i].is_ssl_sub && !tlv[i].is_ssl_verify
|
||||
&& tlv[i].type == type)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* check if the output array already has a raw SSL TLV passthrough entry */
|
||||
static ngx_uint_t
|
||||
ngx_proxy_protocol_v2_tlv_has_ssl_raw(ngx_array_t *tlvs)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_proxy_protocol_write_tlv_t *tlv;
|
||||
|
||||
tlv = tlvs->elts;
|
||||
for (i = 0; i < tlvs->nelts; i++) {
|
||||
if (tlv[i].is_ssl_raw) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* check if the operator has configured this type at all (explicit value,
|
||||
* empty-value suppression, or ssl-raw for type 0x20)
|
||||
*/
|
||||
static ngx_uint_t
|
||||
ngx_proxy_protocol_v2_user_owns_type(
|
||||
ngx_proxy_protocol_write_conf_t *conf, ngx_uint_t type)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_proxy_protocol_conf_tlv_t *ctlv;
|
||||
|
||||
if (conf->tlvs == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctlv = conf->tlvs->elts;
|
||||
for (i = 0; i < conf->tlvs->nelts; i++) {
|
||||
if (ctlv[i].is_ssl_verify) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctlv[i].is_ssl_raw) {
|
||||
if (type == NGX_PROXY_PROTOCOL_V2_TYPE_SSL) {
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctlv[i].is_ssl_sub && ctlv[i].type == type) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ngx_array_t *
|
||||
ngx_proxy_protocol_eval_tlvs(ngx_connection_t *c,
|
||||
ngx_proxy_protocol_write_conf_t *conf, size_t *sizep)
|
||||
{
|
||||
ngx_uint_t i, n;
|
||||
ngx_str_t value;
|
||||
ngx_array_t *tlvs;
|
||||
ngx_proxy_protocol_conf_tlv_t *ctlv;
|
||||
ngx_proxy_protocol_write_tlv_t *tlv;
|
||||
u_char *pp, *pp_end;
|
||||
ngx_uint_t i, n, pt;
|
||||
ngx_str_t value;
|
||||
ngx_array_t *tlvs;
|
||||
ngx_proxy_protocol_conf_tlv_t *ctlv;
|
||||
ngx_proxy_protocol_write_tlv_t *tlv;
|
||||
ngx_proxy_protocol_tlv_t *inp;
|
||||
ngx_uint_t *pt_type;
|
||||
size_t ptlen;
|
||||
#if (NGX_SSL)
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t type;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t type;
|
||||
#endif
|
||||
|
||||
n = (conf->tlvs != NULL ? conf->tlvs->nelts : 0) + 7;
|
||||
|
|
@ -999,9 +1078,89 @@ ngx_proxy_protocol_eval_tlvs(ngx_connection_t *c,
|
|||
}
|
||||
}
|
||||
|
||||
/* passthrough: forward incoming TLVs (higher priority than auto-fill) */
|
||||
|
||||
if (c->proxy_protocol != NULL
|
||||
&& c->proxy_protocol->tlvs.len > 0
|
||||
&& (conf->passthrough_all || conf->passthrough != NULL))
|
||||
{
|
||||
if (conf->passthrough_all) {
|
||||
pp = c->proxy_protocol->tlvs.data;
|
||||
pp_end = pp + c->proxy_protocol->tlvs.len;
|
||||
|
||||
while (pp + sizeof(ngx_proxy_protocol_tlv_t) <= pp_end) {
|
||||
inp = (ngx_proxy_protocol_tlv_t *) pp;
|
||||
pt = inp->type;
|
||||
ptlen = ngx_proxy_protocol_parse_uint16(inp->len);
|
||||
|
||||
pp += sizeof(ngx_proxy_protocol_tlv_t);
|
||||
|
||||
if (pp + ptlen > pp_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pt == NGX_PROXY_PROTOCOL_V2_TYPE_CRC32C
|
||||
|| ngx_proxy_protocol_v2_user_owns_type(conf, pt)
|
||||
|| ngx_proxy_protocol_v2_tlv_has_type(tlvs, pt))
|
||||
{
|
||||
pp += ptlen;
|
||||
continue;
|
||||
}
|
||||
|
||||
tlv = ngx_array_push(tlvs);
|
||||
if (tlv == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tlv->type = pt;
|
||||
tlv->value.data = pp;
|
||||
tlv->value.len = ptlen;
|
||||
tlv->is_ssl_sub = 0;
|
||||
tlv->is_ssl_verify = 0;
|
||||
tlv->is_ssl_raw = (pt == NGX_PROXY_PROTOCOL_V2_TYPE_SSL)
|
||||
? 1 : 0;
|
||||
|
||||
pp += ptlen;
|
||||
}
|
||||
|
||||
} else {
|
||||
pt_type = conf->passthrough->elts;
|
||||
|
||||
for (i = 0; i < conf->passthrough->nelts; i++) {
|
||||
if (ngx_proxy_protocol_v2_user_owns_type(conf, pt_type[i])
|
||||
|| ngx_proxy_protocol_v2_tlv_has_type(tlvs, pt_type[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_proxy_protocol_lookup_tlv(c,
|
||||
&c->proxy_protocol->tlvs, pt_type[i], &value)
|
||||
!= NGX_OK
|
||||
|| value.len == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tlv = ngx_array_push(tlvs);
|
||||
if (tlv == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tlv->type = pt_type[i];
|
||||
tlv->value = value;
|
||||
tlv->is_ssl_sub = 0;
|
||||
tlv->is_ssl_verify = 0;
|
||||
tlv->is_ssl_raw = (pt_type[i] == NGX_PROXY_PROTOCOL_V2_TYPE_SSL)
|
||||
? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if (NGX_SSL)
|
||||
if (c->ssl != NULL) {
|
||||
if (!ngx_proxy_protocol_v2_user_has_ssl_raw(conf)) {
|
||||
if (!ngx_proxy_protocol_v2_user_has_ssl_raw(conf)
|
||||
&& !ngx_proxy_protocol_v2_tlv_has_ssl_raw(tlvs))
|
||||
{
|
||||
for (i = 0;
|
||||
i < sizeof(ngx_proxy_protocol_v2_ssl_sub_types)
|
||||
/ sizeof(ngx_proxy_protocol_v2_ssl_sub_types[0]);
|
||||
|
|
@ -1037,7 +1196,9 @@ ngx_proxy_protocol_eval_tlvs(ngx_connection_t *c,
|
|||
}
|
||||
|
||||
if (!ngx_proxy_protocol_v2_user_has_type(conf,
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_ALPN, 0))
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_ALPN, 0)
|
||||
&& !ngx_proxy_protocol_v2_tlv_has_type(tlvs,
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_ALPN))
|
||||
{
|
||||
if (ngx_proxy_protocol_v2_auto_alpn(c, &value) == NGX_OK) {
|
||||
tlv = ngx_array_push(tlvs);
|
||||
|
|
@ -1054,7 +1215,9 @@ ngx_proxy_protocol_eval_tlvs(ngx_connection_t *c,
|
|||
}
|
||||
|
||||
if (!ngx_proxy_protocol_v2_user_has_type(conf,
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_AUTHORITY, 0))
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_AUTHORITY, 0)
|
||||
&& !ngx_proxy_protocol_v2_tlv_has_type(tlvs,
|
||||
NGX_PROXY_PROTOCOL_V2_TYPE_AUTHORITY))
|
||||
{
|
||||
if (ngx_proxy_protocol_v2_auto_authority(c, &value) == NGX_OK) {
|
||||
tlv = ngx_array_push(tlvs);
|
||||
|
|
@ -1371,6 +1534,62 @@ ngx_proxy_protocol_tlv_type(ngx_str_t *name, ngx_uint_t *typep,
|
|||
}
|
||||
|
||||
|
||||
char *
|
||||
ngx_proxy_protocol_v2_add_passthrough(ngx_conf_t *cf,
|
||||
ngx_array_t **passthrough, ngx_str_t *name)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t type, is_ssl_sub, is_ssl_verify, is_ssl_raw, *tp;
|
||||
|
||||
rc = ngx_proxy_protocol_tlv_type(name, &type, &is_ssl_sub, &is_ssl_verify,
|
||||
&is_ssl_raw);
|
||||
if (rc == NGX_DECLINED) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"unknown PROXY protocol TLV \"%V\"", name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid PROXY protocol TLV \"%V\"", name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (is_ssl_sub || is_ssl_verify) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"SSL sub-TLV types cannot be used in "
|
||||
"\"proxy_protocol_passthrough\"; use "
|
||||
"\"proxy_protocol_passthrough ssl\" to forward "
|
||||
"the entire SSL TLV verbatim");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (type == NGX_PROXY_PROTOCOL_V2_TYPE_CRC32C) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"CRC32c TLV (0x03) cannot be passed through: "
|
||||
"the checksum covers the original wire bytes "
|
||||
"and is invalid on a re-assembled header");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (*passthrough == NULL) {
|
||||
*passthrough = ngx_array_create(cf->pool, 4, sizeof(ngx_uint_t));
|
||||
if (*passthrough == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
tp = ngx_array_push(*passthrough);
|
||||
if (tp == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*tp = type;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
ngx_proxy_protocol_v2_add_tlv(ngx_conf_t *cf, ngx_array_t **tlvsp,
|
||||
ngx_str_t *name, void *cv)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ typedef struct {
|
|||
ngx_uint_t version;
|
||||
ngx_array_t *tlvs;
|
||||
ngx_flag_t crc32c;
|
||||
ngx_array_t *passthrough;
|
||||
ngx_uint_t passthrough_all;
|
||||
} ngx_proxy_protocol_write_conf_t;
|
||||
|
||||
|
||||
|
|
@ -48,6 +50,8 @@ ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
|
|||
ngx_str_t *value);
|
||||
char *ngx_proxy_protocol_v2_add_tlv(ngx_conf_t *cf, ngx_array_t **tlvsp,
|
||||
ngx_str_t *name, void *cv);
|
||||
char *ngx_proxy_protocol_v2_add_passthrough(ngx_conf_t *cf,
|
||||
ngx_array_t **passthrough, ngx_str_t *name);
|
||||
|
||||
|
||||
#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue