diff -r 82e174e47663 src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c Thu Apr 08 00:16:30 2021 +0300 +++ b/src/core/ngx_proxy_protocol.c Fri Apr 09 16:10:29 2021 +0300 @@ -13,6 +13,34 @@ #define NGX_PROXY_PROTOCOL_AF_INET6 2
+#define NGX_PROXY_PROTOCOL_V2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" +#define NGX_PROXY_PROTOCOL_V2_SIG_LEN 12 +#define NGX_PROXY_PROTOCOL_V2_HDR_LEN 16 +#define NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET \ + (NGX_PROXY_PROTOCOL_V2_HDR_LEN + (4 + 4 + 2 + 2)) +#define NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET6 \ + (NGX_PROXY_PROTOCOL_V2_HDR_LEN + (16 + 16 + 2 + 2)) + +#define NGX_PROXY_PROTOCOL_V2_CMD_PROXY (0x20 | 0x01) + +#define NGX_PROXY_PROTOCOL_V2_TRANS_STREAM 0x01 + +#define NGX_PROXY_PROTOCOL_V2_FAM_UNSPEC 0x00 +#define NGX_PROXY_PROTOCOL_V2_FAM_INET 0x10 +#define NGX_PROXY_PROTOCOL_V2_FAM_INET6 0x20 + +#define NGX_PROXY_PROTOCOL_V2_TYPE_ALPN 0x01 +#define NGX_PROXY_PROTOCOL_V2_TYPE_SSL 0x20 +#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_VERSION 0x21 +#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CIPHER 0x23 +#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_SIG_ALG 0x24 +#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_KEY_ALG 0x25 + +#define NGX_PROXY_PROTOCOL_V2_CLIENT_SSL 0x01 +#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN 0x02 +#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS 0x04 + + #define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1]) @@ -40,12 +68,68 @@ } ngx_proxy_protocol_inet6_addrs_t; +typedef union { + struct { + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ip4; + struct { + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } ip6; +} ngx_proxy_protocol_addrs_t; + + +typedef struct { + u_char signature[12]; + uint8_t version_command; + uint8_t family_transport; + uint16_t len; + ngx_proxy_protocol_addrs_t addr; +} ngx_proxy_protocol_v2_header_t; + + +struct ngx_tlv_s { + uint8_t type; + uint8_t length_hi; + uint8_t length_lo; + uint8_t value[0]; +} __attribute__((packed)); + +typedef struct ngx_tlv_s ngx_tlv_t; + + +#if (NGX_STREAM_SSL) +struct ngx_tlv_ssl_s { + ngx_tlv_t tlv; + uint8_t client; + uint32_t verify; + uint8_t sub_tlv[]; +} __attribute__((packed)); + +typedef struct ngx_tlv_ssl_s ngx_tlv_ssl_t; +#endif + + static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr); static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port, u_char sep); static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last); +static u_char *ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, + u_char *last); +#if (NGX_HAVE_INET6) +static void ngx_v4tov6(struct in6_addr *sin6_addr, struct sockaddr *addr); +#endif +#if (NGX_STREAM_SSL) +static u_char *ngx_copy_tlv(u_char *pos, u_char *last, u_char type, + u_char *value, uint16_t value_len); +#endif u_char * @@ -223,7 +307,8 @@ u_char * -ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) +ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last, + ngx_uint_t pp_version) { ngx_uint_t port, lport; @@ -235,6 +320,10 @@ return NULL; } + if (pp_version == 2) { + return ngx_proxy_protocol_v2_write(c, buf, last); + } + switch (c->sockaddr->sa_family) { case AF_INET: @@ -420,3 +509,344 @@ return end; } + + +static u_char * +ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, u_char *last) +{ + struct sockaddr *src, *dst; + ngx_proxy_protocol_v2_header_t *header; +#if (NGX_HAVE_INET6) + struct in6_addr v6_tmp; + ngx_int_t v6_used; +#endif +#if (NGX_STREAM_SSL) + ngx_tlv_ssl_t *tlv; + u_char *value, *pos; + u_char kbuf[100]; + const unsigned char *data; + unsigned int data_len; + + X509 *crt; + EVP_PKEY *key; + const ASN1_OBJECT *algorithm; + const char *s; + + long rc; + size_t tlv_len; +#endif + size_t len; + + header = (ngx_proxy_protocol_v2_header_t *) buf; + + header->len = 0; + + src = c->sockaddr; + dst = c->local_sockaddr; + + len = 0; + +#if (NGX_HAVE_INET6) + v6_used = 0; +#endif + + ngx_memcpy(header->signature, NGX_PROXY_PROTOCOL_V2_SIG, + NGX_PROXY_PROTOCOL_V2_SIG_LEN); + + header->version_command = NGX_PROXY_PROTOCOL_V2_CMD_PROXY; + header->family_transport = NGX_PROXY_PROTOCOL_V2_TRANS_STREAM; + + /** Addrs */ + + switch (src->sa_family) { + + case AF_INET: + + if (dst->sa_family == AF_INET) { + + header->addr.ip4.src_addr = + ((struct sockaddr_in *) src)->sin_addr.s_addr; + header->addr.ip4.src_port = ((struct sockaddr_in *) src)->sin_port; + } +#if (NGX_HAVE_INET6) + else /** dst == AF_INET6 */{ + + ngx_v4tov6(&v6_tmp, src); + ngx_memcpy(header->addr.ip6.src_addr, &v6_tmp, 16); + header->addr.ip6.src_port = ((struct sockaddr_in *) src)->sin_port; + } +#endif + break; + +#if (NGX_HAVE_INET6) + case AF_INET6: + v6_used = 1; + + ngx_memcpy(header->addr.ip6.src_addr, + &((struct sockaddr_in6 *) src)->sin6_addr, 16); + header->addr.ip6.src_port = ((struct sockaddr_in6 *) src)->sin6_port; + + break; +#endif + + default: + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported src address family %ui", + src->sa_family); + goto unspec; + }; + + switch (dst->sa_family) { + case AF_INET: + + if (src->sa_family == AF_INET) { + + header->addr.ip4.dst_addr = + ((struct sockaddr_in *) dst)->sin_addr.s_addr; + header->addr.ip4.dst_port = ((struct sockaddr_in *) dst)->sin_port; + } +#if (NGX_HAVE_INET6) + else /** src == AF_INET6 */{ + + ngx_v4tov6(&v6_tmp, dst); + ngx_memcpy(header->addr.ip6.dst_addr, &v6_tmp, 16); + header->addr.ip6.dst_port = ((struct sockaddr_in *) dst)->sin_port; + + } +#endif + break; + +#if (NGX_HAVE_INET6) + case AF_INET6: + v6_used = 1; + + ngx_memcpy(header->addr.ip6.dst_addr, + &((struct sockaddr_in6 *) dst)->sin6_addr, 16); + header->addr.ip6.dst_port = ((struct sockaddr_in6 *) dst)->sin6_port; + + break; +#endif + + default: + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported dest address family %ui", + dst->sa_family); + goto unspec; + } + +#if (NGX_HAVE_INET6) + if (!v6_used) { + header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET; + len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET; + + } else { + header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET6; + len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET6; + + } +#else + header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET; + len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET; +#endif + + /** SSL TLVs */ + +#if (NGX_STREAM_SSL) + + data = NULL; + data_len = 0; + + tlv = (ngx_tlv_ssl_t *) (buf + len); + ngx_memzero(tlv, sizeof(ngx_tlv_ssl_t)); + + tlv->tlv.type = NGX_PROXY_PROTOCOL_V2_TYPE_SSL; + pos = buf + len + sizeof(ngx_tlv_ssl_t); + + tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_SSL; + + if (c->ssl != NULL) { + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + SSL_get0_alpn_selected(c->ssl->connection, &data, &data_len); + +#ifdef TLSEXT_TYPE_next_proto_neg + if (data_len == 0) { + SSL_get0_next_proto_negotiated(c->ssl->connection, + &data, &data_len); + } +#endif + +#else /* TLSEXT_TYPE_next_proto_neg */ + SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &data_len); +#endif + + if (data_len) { + + pos = ngx_copy_tlv(pos, last, + NGX_PROXY_PROTOCOL_V2_TYPE_ALPN, + (u_char *) data, (uint16_t) data_len); + if (pos == NULL) { + return NULL; + } + } + + value = (u_char *) SSL_get_version(c->ssl->connection); + if (value != NULL) { + + pos = ngx_copy_tlv(pos, last, + NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_VERSION, + value, ngx_strlen(value)); + if (pos == NULL) { + return NULL; + } + } + + crt = SSL_get_peer_certificate(c->ssl->connection); + if (crt != NULL) { + + tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS; + + rc = SSL_get_verify_result(c->ssl->connection); + tlv->verify = htonl(rc); + + if (rc == X509_V_OK) { + + if (ngx_ssl_ocsp_get_status(c, &s) == NGX_OK) { + tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN; + } + } + + X509_free(crt); + } + + crt = SSL_get_certificate(c->ssl->connection); + if (crt != NULL) { + + key = X509_get_pubkey(crt); + + /** Key */ + if (key != NULL) { + + switch (EVP_PKEY_base_id(key)) { + case EVP_PKEY_RSA: + value = (u_char *) "RSA"; + break; + case EVP_PKEY_EC: + value = (u_char *) "EC"; + break; + case EVP_PKEY_DSA: + value = (u_char *) "DSA"; + break; + default: + value = NULL; + break; + } + + if (value != NULL) { + + value = ngx_snprintf(kbuf, sizeof(kbuf) - 1, "%s%d%Z", + value, EVP_PKEY_bits(key)); + + pos = ngx_copy_tlv(pos, last, + NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_KEY_ALG, + kbuf, ngx_strlen(kbuf)); + } + + EVP_PKEY_free(key); + + if (pos == NULL) { + return NULL; + } + } + + /* ALG */ + X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(crt)); + value = (u_char *) OBJ_nid2sn(OBJ_obj2nid(algorithm)); + + if (value != NULL) { + + pos = ngx_copy_tlv(pos, last, + NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_SIG_ALG, + value, ngx_strlen(value)); + if (pos == NULL) { + return NULL; + } + } + } + + value = (u_char *) SSL_get_cipher_name(c->ssl->connection); + if (value != NULL) { + + pos = ngx_copy_tlv(pos, last, + NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CIPHER, + value, ngx_strlen(value)); + if (pos == NULL) { + return NULL; + } + } + } + + tlv_len = pos - (buf + len); + + tlv->tlv.length_hi = (uint16_t) (tlv_len - sizeof(ngx_tlv_t)) >> 8; + tlv->tlv.length_lo = (uint16_t) (tlv_len - sizeof(ngx_tlv_t)) & 0x00ff; + + len = len + tlv_len; + +#endif + + header->len = htons(len - NGX_PROXY_PROTOCOL_V2_HDR_LEN); + return buf + len; + +unspec: + header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_UNSPEC; + header->len = 0; + + return buf + NGX_PROXY_PROTOCOL_V2_HDR_LEN; +} + + +#if (NGX_HAVE_INET6) +static void +ngx_v4tov6(struct in6_addr *sin6_addr, struct sockaddr *addr) +{ + static const char rfc4291[] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF }; + + struct in_addr tmp_addr, *sin_addr; + + sin_addr = &((struct sockaddr_in *) addr)->sin_addr; + + tmp_addr.s_addr = sin_addr->s_addr; + ngx_memcpy(sin6_addr->s6_addr, rfc4291, sizeof(rfc4291)); + ngx_memcpy(sin6_addr->s6_addr + 12, &tmp_addr.s_addr, 4); +} +#endif + + +#if (NGX_STREAM_SSL) + +static u_char * +ngx_copy_tlv(u_char *pos, u_char *last, u_char type, + u_char *value, uint16_t value_len) +{ + ngx_tlv_t *tlv; + + if (last - pos < (long) sizeof(*tlv)) { + return NULL; + } + + tlv = (ngx_tlv_t *) pos; + + tlv->type = type; + tlv->length_hi = (uint16_t) value_len >> 8; + tlv->length_lo = (uint16_t) value_len & 0x00ff; + ngx_memcpy(tlv->value, value, value_len); + + return pos + (value_len + sizeof(*tlv)); +} + +#endif + + diff -r 82e174e47663 src/core/ngx_proxy_protocol.h --- a/src/core/ngx_proxy_protocol.h Thu Apr 08 00:16:30 2021 +0300 +++ b/src/core/ngx_proxy_protocol.h Fri Apr 09 16:10:29 2021 +0300 @@ -13,7 +13,7 @@ #include <ngx_core.h> -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 +#define NGX_PROXY_PROTOCOL_MAX_HEADER 214 struct ngx_proxy_protocol_s { @@ -27,7 +27,7 @@ u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last); u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, - u_char *last); + u_char *last, ngx_uint_t pp_version); #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff -r 82e174e47663 src/stream/ngx_stream_proxy_module.c --- a/src/stream/ngx_stream_proxy_module.c Thu Apr 08 00:16:30 2021 +0300 +++ b/src/stream/ngx_stream_proxy_module.c Fri Apr 09 16:10:29 2021 +0300 @@ -30,7 +30,7 @@ ngx_uint_t responses; ngx_uint_t next_upstream_tries; ngx_flag_t next_upstream; - ngx_flag_t proxy_protocol; + ngx_uint_t proxy_protocol; ngx_stream_upstream_local_t *local; ngx_flag_t socket_keepalive; @@ -121,6 +121,14 @@ #endif +static ngx_conf_enum_t ngx_stream_proxy_protocol[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("v2"), 2 }, + { ngx_null_string, 0 } +}; + + static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_downstream_buffer = { ngx_conf_deprecated, "proxy_downstream_buffer", "proxy_buffer_size" }; @@ -239,10 +247,10 @@ { ngx_string("proxy_protocol"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_conf_set_enum_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol), - NULL }, + &ngx_stream_proxy_protocol }, #if (NGX_STREAM_SSL) @@ -891,7 +899,8 @@ cl->buf->pos = p; - p = ngx_proxy_protocol_write(c, p, p + NGX_PROXY_PROTOCOL_MAX_HEADER); + p = ngx_proxy_protocol_write(c, p, p + NGX_PROXY_PROTOCOL_MAX_HEADER, + u->proxy_protocol); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; @@ -942,14 +951,15 @@ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream proxy send PROXY protocol header"); - p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER); + u = s->upstream; + + p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER, + u->proxy_protocol); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return NGX_ERROR; } - u = s->upstream; - pc = u->peer.connection; size = p - buf; @@ -1998,7 +2008,7 @@ conf->responses = NGX_CONF_UNSET_UINT; conf->next_upstream_tries = NGX_CONF_UNSET_UINT; conf->next_upstream = NGX_CONF_UNSET; - conf->proxy_protocol = NGX_CONF_UNSET; + conf->proxy_protocol = NGX_CONF_UNSET_UINT; conf->local = NGX_CONF_UNSET_PTR; conf->socket_keepalive = NGX_CONF_UNSET; @@ -2053,7 +2063,7 @@ ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1); - ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); + ngx_conf_merge_uint_value(conf->proxy_protocol, prev->proxy_protocol, 0); ngx_conf_merge_ptr_value(conf->local, prev->local, NULL); diff -r 82e174e47663 src/stream/ngx_stream_upstream.h --- a/src/stream/ngx_stream_upstream.h Thu Apr 08 00:16:30 2021 +0300 +++ b/src/stream/ngx_stream_upstream.h Fri Apr 09 16:10:29 2021 +0300 @@ -141,7 +141,7 @@ ngx_stream_upstream_resolved_t *resolved; ngx_stream_upstream_state_t *state; unsigned connected:1; - unsigned proxy_protocol:1; + unsigned proxy_protocol:2; } ngx_stream_upstream_t;
_______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel