From c2e9288043904f5adb279c8d932ebe3205408615 Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Wed, 14 Feb 2018 15:20:58 +0000 Subject: [PATCH] Revert r1824221: wrong backport. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1824246 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 5 - STATUS | 29 ++ docs/manual/mod/mod_remoteip.xml | 71 --- modules/metadata/mod_remoteip.c | 772 +------------------------------ 4 files changed, 31 insertions(+), 846 deletions(-) diff --git a/CHANGES b/CHANGES index a06f95cba3..28fcfc9629 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,6 @@ -*- coding: utf-8 -*- Changes with Apache 2.4.30 - *) mod_remoteip: Add support for PROXY protocol (code donated by Cloudzilla). - Add ability for PROXY protocol processing to be optional to donated code. - See also: http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt - [Cloudzilla/roadrunner2@GitHub, Jim Jagielski, Daniel Ruggeri] - *) mod_proxy, mod_ssl: Handle SSLProxy* directives in sections, allowing per backend TLS configuration. [Yann Ylavic] diff --git a/STATUS b/STATUS index b75935cbd5..6a4df39d6f 100644 --- a/STATUS +++ b/STATUS @@ -118,6 +118,35 @@ RELEASE SHOWSTOPPERS: PATCHES ACCEPTED TO BACKPORT FROM TRUNK: [ start all new proposals below, under PATCHES PROPOSED. ] + *) mod_remoteip: Add PROXY protocol support + trunk patch: http://svn.apache.org/r1776575 + http://svn.apache.org/r1776578 (doc fix) + http://svn.apache.org/r1776624 + http://svn.apache.org/r1776627 (shortened name + doc fix) + http://svn.apache.org/r1776674 (attribution moved to CHANGES) + http://svn.apache.org/r1776734 + http://svn.apache.org/r1776740 (attribution updated in mod_remotip.c) + http://svn.apache.org/r1778268 (fix compiler warning) + http://svn.apache.org/r1780725 (set buckets aside) + http://svn.apache.org/r1781030 (fix strict GCC warning) + http://svn.apache.org/r1781031 (reference the filter by handle) + http://svn.apache.org/r1781701 (rework optional processing case) + http://svn.apache.org/r1788674 (final edge cases/ignore slave conns) + http://svn.apache.org/r1789800 (remove optional processing) + http://svn.apache.org/r1790169 (rename "exception" directive) + http://svn.apache.org/r1790457 (Update directive name in err message) + http://svn.apache.org/r1790691 + http://svn.apache.org/r1806985 + http://svn.apache.org/r1818279 + 2.4 convenience patch (includes CHANGES): + http://home.apache.org/~ylavic/patches/RemoteIPProxyProtocol.2.4-v3.patch + +1: druggeri, jim, minfrin + ylavic: RemoteIPProxyProtocol* are documented as scoped to server config + and virtual host, though using ap_server_conf makes them global + only (thus less useful too...). + jim: Can docco patch be post-backport? + minfrin: The docs seem correct, and there is a long explanation in the docs of + why the scoping is as it is. PATCHES PROPOSED TO BACKPORT FROM TRUNK: diff --git a/docs/manual/mod/mod_remoteip.xml b/docs/manual/mod/mod_remoteip.xml index eb01dc7c5e..7edf995b00 100644 --- a/docs/manual/mod/mod_remoteip.xml +++ b/docs/manual/mod/mod_remoteip.xml @@ -42,12 +42,6 @@ via the request headers. with the useragent IP address reported in the request header configured with the RemoteIPHeader directive.

-

Additionally, this module implements the server side of - HAProxy's - Proxy Protocol when - using the RemoteIPProxyProtocolEnable - directive.

-

Once replaced as instructed, this overridden useragent IP address is then used for the mod_authz_host Require ip @@ -65,7 +59,6 @@ via the request headers. mod_authz_host mod_status mod_log_config -Proxy Protocol Spec

Remote IP Processing @@ -218,70 +211,6 @@ RemoteIPProxiesHeader X-Forwarded-By - -RemoteIPProxyProtocol -Enable, optionally enable or disable the proxy protocol handling -ProxyProtocol On|Optional|Off -server configvirtual host - - - -

The RemoteIPProxyProtocolEnable enables or - disables the reading and handling of the proxy protocol connection header. - If enabled with the On flag, the upstream client must - send the header every time it opens a connection or the connection will - be aborted. If enabled with the Optional flag, the upstream - client may send the header.

- -

While this directive may be specified in any virtual host, it is - important to understand that because the proxy protocol is connection - based and protocol agnostic, the enabling and disabling is actually based - on ip-address and port. This means that if you have multiple name-based - virtual hosts for the same host and port, and you enable it any one of - them, then it is enabled for all them (with that host and port). It also - means that if you attempt to enable the proxy protocol in one and disable - in the other, that won't work; in such a case the last one wins and a - notice will be logged indicating which setting was being overridden.

- - When multiple virtual hosts on the same IP and port are - configured with a combination of On and Optional - flags, connections will not be aborted if the header is not sent. - Instead, enforcement will happen after the request is read so virtual - hosts configured with On will return a 400 Bad Request. - Virtual hosts configured with Optional will continue as - usual but without replacing the client IP information - - -Listen 80 -<VirtualHost *:80> - ServerName www.example.com - RemoteIPProxyProtocolEnable Optional - - #Requests to this virtual host may optionally not have - # a proxy protocol header provided -</VirtualHost> - -<VirtualHost *:80> - ServerName www.example.com - RemoteIPProxyProtocolEnable On - - #Requests to this virtual host must have a proxy protocol - # header provided. If it is missing, a 400 will result -</VirtualHost> - -Listen 8080 -<VirtualHost *:8080> - ServerName www.example.com - RemoteIPProxyProtocolEnable On - - #Requests to this virtual host must have a proxy protocol - # header provided. If it is missing, the connection will - # be aborted -</VirtualHost> - -
-
- RemoteIPTrustedProxy Declare client intranet IP addresses trusted to present the RemoteIPHeader value diff --git a/modules/metadata/mod_remoteip.c b/modules/metadata/mod_remoteip.c index 8604d6b95e..28e01df297 100644 --- a/modules/metadata/mod_remoteip.c +++ b/modules/metadata/mod_remoteip.c @@ -16,13 +16,11 @@ #include "ap_config.h" #include "ap_mmn.h" -#include "ap_listen.h" #include "httpd.h" #include "http_config.h" #include "http_connection.h" #include "http_protocol.h" #include "http_log.h" -#include "http_main.h" #include "apr_strings.h" #include "apr_lib.h" #define APR_WANT_BYTEFUNC @@ -38,12 +36,6 @@ typedef struct { void *internal; } remoteip_proxymatch_t; -typedef struct remoteip_addr_info { - struct remoteip_addr_info *next; - apr_sockaddr_t *addr; - server_rec *source; -} remoteip_addr_info; - typedef struct { /** The header to retrieve a proxy-via IP list */ const char *header_name; @@ -56,17 +48,6 @@ typedef struct { * with the most commonly encountered listed first */ apr_array_header_t *proxymatch_ip; - - remoteip_addr_info *proxy_protocol_enabled; - remoteip_addr_info *proxy_protocol_optional; - remoteip_addr_info *proxy_protocol_disabled; - - /** A flag indicating whether or not proxyprotocol - * is optoinal for this specific server - */ - int pp_optional; - - apr_pool_t *pool; } remoteip_config_t; typedef struct { @@ -78,92 +59,12 @@ typedef struct { const char *proxied_remote; } remoteip_req_t; -/* For PROXY protocol processing */ -static const char *remoteip_filter_name = "REMOTEIP_INPUT"; - -typedef struct { - char line[108]; -} proxy_v1; - -typedef union { - struct { /* for TCP/UDP over IPv4, len = 12 */ - apr_uint32_t src_addr; - apr_uint32_t dst_addr; - apr_uint16_t src_port; - apr_uint16_t dst_port; - } ip4; - struct { /* for TCP/UDP over IPv6, len = 36 */ - apr_byte_t src_addr[16]; - apr_byte_t dst_addr[16]; - apr_uint16_t src_port; - apr_uint16_t dst_port; - } ip6; - struct { /* for AF_UNIX sockets, len = 216 */ - apr_byte_t src_addr[108]; - apr_byte_t dst_addr[108]; - } unx; -} proxy_v2_addr; - -typedef struct { - apr_byte_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */ - apr_byte_t ver_cmd; /* protocol version and command */ - apr_byte_t fam; /* protocol family and address */ - apr_uint16_t len; /* number of following bytes part of the header */ - proxy_v2_addr addr; -} proxy_v2; - -typedef union { - proxy_v1 v1; - proxy_v2 v2; -} proxy_header; - -static const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; -#define MIN_V1_HDR_LEN 15 -#define MIN_V2_HDR_LEN 16 -#define MIN_HDR_LEN MIN_V1_HDR_LEN - -/* XXX: Unsure if this is needed if v6 support is not available on - this platform */ -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 46 -#endif - -typedef struct { - char header[sizeof(proxy_header)]; - apr_size_t rcvd; - apr_size_t need; - int version; - ap_input_mode_t mode; - apr_bucket_brigade *bb; - int done; -} remoteip_filter_context; - -/** Holds the resolved proxy info for this connection and any addition - configurable parameters -*/ -typedef struct { - /** The parsed client address in native format */ - apr_sockaddr_t *client_addr; - /** Character representation of the client */ - char *client_ip; - /** Flag indicating that the PROXY header may be omitted on this - connection (do not abort if it is missing). */ - int proxy_protocol_optional; -} remoteip_conn_config_t; - -typedef enum { HDR_DONE, HDR_ERROR, HDR_MISSING, HDR_NEED_MORE } remoteip_parse_status_t; - static void *create_remoteip_server_config(apr_pool_t *p, server_rec *s) { remoteip_config_t *config = apr_pcalloc(p, sizeof *config); /* config->header_name = NULL; * config->proxies_header_name = NULL; */ - config->proxy_protocol_enabled = NULL; - config->proxy_protocol_optional = NULL; - config->proxy_protocol_disabled = NULL; - config->pp_optional = 0; - config->pool = p; return config; } @@ -184,9 +85,6 @@ static void *merge_remoteip_server_config(apr_pool_t *p, void *globalv, config->proxymatch_ip = server->proxymatch_ip ? server->proxymatch_ip : global->proxymatch_ip; - config->pp_optional = server->pp_optional - ? server->pp_optional - : global->pp_optional; return config; } @@ -317,184 +215,13 @@ static const char *proxylist_read(cmd_parms *cmd, void *cfg, return NULL; } -/** Similar apr_sockaddr_equal, except that it compares ports too. */ -static int remoteip_sockaddr_equal(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2) -{ - return (addr1->port == addr2->port && apr_sockaddr_equal(addr1, addr2)); -} - -/** Similar remoteip_sockaddr_equal, except that it handles wildcard addresses - * and ports too. - */ -static int remoteip_sockaddr_compat(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2) -{ - /* test exact address equality */ - if (apr_sockaddr_equal(addr1, addr2) && - (addr1->port == addr2->port || addr1->port == 0 || addr2->port == 0)) { - return 1; - } - - /* test address wildcards */ - if (apr_sockaddr_is_wildcard(addr1) && - (addr1->port == 0 || addr1->port == addr2->port)) { - return 1; - } - - if (apr_sockaddr_is_wildcard(addr2) && - (addr2->port == 0 || addr2->port == addr1->port)) { - return 1; - } - - return 0; -} - -static int remoteip_addr_in_list(remoteip_addr_info *list, apr_sockaddr_t *addr) -{ - for (; list; list = list->next) { - if (remoteip_sockaddr_compat(list->addr, addr)) { - return 1; - } - } - - return 0; -} - -static void remoteip_warn_enable_conflict(remoteip_addr_info *prev, server_rec *new, const char* arg) -{ - char buf[INET6_ADDRSTRLEN]; - - apr_sockaddr_ip_getbuf(buf, sizeof(buf), prev->addr); - - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, new, APLOGNO(03491) - "RemoteIPProxyProtocolEnable: previous setting for %s:%hu from virtual " - "host {%s:%hu in %s} is being overriden by virtual host " - "{%s:%hu in %s}; new setting is '%s'", - buf, prev->addr->port, prev->source->server_hostname, - prev->source->addrs->host_port, prev->source->defn_name, - new->server_hostname, new->addrs->host_port, new->defn_name, - arg); -} - -static const char *remoteip_enable_proxy_protocol(cmd_parms *cmd, void *config, - const char *arg) -{ - remoteip_config_t *global_conf; - remoteip_config_t *server_conf; - server_addr_rec *addr; - remoteip_addr_info **add; - int list_len = 2; - remoteip_addr_info **rem_list[list_len]; - remoteip_addr_info *list; - int i; - - global_conf = ap_get_module_config(ap_server_conf->module_config, - &remoteip_module); - server_conf = ap_get_module_config(cmd->server->module_config, - &remoteip_module); - - if (strcasecmp(arg, "On") == 0) { - add = &global_conf->proxy_protocol_enabled; - rem_list[0] = &global_conf->proxy_protocol_optional; - rem_list[1] = &global_conf->proxy_protocol_disabled; - } - else if (strcasecmp(arg, "Optional") == 0) { - add = &global_conf->proxy_protocol_optional; - rem_list[0] = &global_conf->proxy_protocol_enabled; - rem_list[1] = &global_conf->proxy_protocol_disabled; - server_conf->pp_optional = 1; - } - else if (strcasecmp(arg, "Off") == 0 ) { - add = &global_conf->proxy_protocol_disabled; - rem_list[0] = &global_conf->proxy_protocol_enabled; - rem_list[1] = &global_conf->proxy_protocol_optional; - } - else { - return apr_pstrcat(cmd->pool, "Unrecognized option for ProxyProtocolEnable `%s'", arg, NULL); - } - - for (addr = cmd->server->addrs; addr; addr = addr->next) { - /* remove address from other lists */ - for (i = 0; i < list_len ; i++) { - remoteip_addr_info **rem = rem_list[i]; - if (*rem) { - if (remoteip_sockaddr_equal((*rem)->addr, addr->host_addr)) { - remoteip_warn_enable_conflict(*rem, cmd->server, arg); - *rem = (*rem)->next; - } - else { - for (list = *rem; list->next; list = list->next) { - if (remoteip_sockaddr_equal(list->next->addr, addr->host_addr)) { - remoteip_warn_enable_conflict(list->next, cmd->server, arg); - list->next = list->next->next; - break; - } - } - } - } - } - - /* add address to desired list */ - if (!remoteip_addr_in_list(*add, addr->host_addr)) { - remoteip_addr_info *info = apr_palloc(global_conf->pool, sizeof(*info)); - info->addr = addr->host_addr; - info->source = cmd->server; - info->next = *add; - *add = info; - } - } - - return NULL; -} - -static int remoteip_hook_pre_config(apr_pool_t *pconf, apr_pool_t *plog, - apr_pool_t *ptemp) -{ - remoteip_config_t *config = (remoteip_config_t *) - create_remoteip_server_config(pconf, NULL); - ap_set_module_config(ap_server_conf->module_config, &remoteip_module, - config); - - return OK; -} - -static int remoteip_hook_post_config(apr_pool_t *pconf, apr_pool_t *plog, - apr_pool_t *ptemp, server_rec *s) -{ - remoteip_config_t *conf; - remoteip_addr_info *info; - char buf[INET6_ADDRSTRLEN]; - - conf = ap_get_module_config(ap_server_conf->module_config, - &remoteip_module); - - for (info = conf->proxy_protocol_enabled; info; info = info->next) { - apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(03492) - "RemoteIPProxyProtocol: enabled on %s:%hu", buf, info->addr->port); - } - for (info = conf->proxy_protocol_optional; info; info = info->next) { - apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(03493) - "RemoteIPProxyProtocol: optional on %s:%hu", buf, info->addr->port); - } - for (info = conf->proxy_protocol_disabled; info; info = info->next) { - apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(03494) - "RemoteIPProxyProtocol: disabled on %s:%hu", buf, info->addr->port); - } - - return OK; -} - static int remoteip_modify_request(request_rec *r) { conn_rec *c = r->connection; remoteip_config_t *config = (remoteip_config_t *) ap_get_module_config(r->server->module_config, &remoteip_module); - remoteip_conn_config_t *conn_config = (remoteip_conn_config_t *) - ap_get_module_config(r->connection->conn_config, &remoteip_module); - remoteip_req_t *req = NULL; + apr_sockaddr_t *temp_sa; apr_status_t rv; @@ -510,37 +237,10 @@ static int remoteip_modify_request(request_rec *r) */ void *internal = NULL; - /* No header defined or results from our input filter */ - if (!config->header_name && !conn_config) { + if (!config->header_name) { return DECLINED; } - /* Easy parsing case - just position the data we already have from PROXY - protocol handling allowing it to take precedence and return - */ - if (conn_config) { - /* We may have gotten here if processing was optional - check for that */ - if (!conn_config->client_addr) { - if (config->pp_optional) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03495) - "RemoteIPProxyProtocol data is missing, but was optional. Allowing request."); - return OK; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03496) - "RemoteIPProxyProtocol data is missing, but required! Aborting request."); - return HTTP_BAD_REQUEST; - } - } - - r->useragent_addr = conn_config->client_addr; - r->useragent_ip = conn_config->client_ip; - - ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, - "Using %s as client's IP from PROXY protocol", r->useragent_ip); - return OK; - } - if (config->proxymatch_ip) { /* This indicates that a RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy or RemoteIPTrustedProxyList directive is configured. @@ -724,464 +424,6 @@ static int remoteip_modify_request(request_rec *r) return OK; } -static int remoteip_is_server_port(apr_port_t port) -{ - ap_listen_rec *lr; - - for (lr = ap_listeners; lr; lr = lr->next) { - if (lr->bind_addr && lr->bind_addr->port == port) { - return 1; - } - } - - return 0; -} - -/* - * Human readable format: - * PROXY {TCP4|TCP6|UNKNOWN} - */ -static remoteip_parse_status_t remoteip_process_v1_header(conn_rec *c, - remoteip_conn_config_t *conn_conf, - proxy_header *hdr, apr_size_t len, - apr_size_t *hdr_len) -{ - char *end, *word, *host, *valid_addr_chars, *saveptr; - char buf[sizeof(hdr->v1.line)]; - apr_port_t port; - apr_status_t ret; - apr_int32_t family; - -#define GET_NEXT_WORD(field) \ - word = apr_strtok(NULL, " ", &saveptr); \ - if (!word) { \ - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03497) \ - "RemoteIPProxyProtocol: no " field " found in header '%s'", \ - hdr->v1.line); \ - return HDR_ERROR; \ - } - - end = memchr(hdr->v1.line, '\r', len - 1); - if (!end || end[1] != '\n') { - return HDR_NEED_MORE; /* partial or invalid header */ - } - - *end = '\0'; - *hdr_len = end + 2 - hdr->v1.line; /* skip header + CRLF */ - - /* parse in separate buffer so have the original for error messages */ - strcpy(buf, hdr->v1.line); - - apr_strtok(buf, " ", &saveptr); - - /* parse family */ - GET_NEXT_WORD("family") - if (strcmp(word, "UNKNOWN") == 0) { - conn_conf->client_addr = c->client_addr; - conn_conf->client_ip = c->client_ip; - return HDR_DONE; - } - else if (strcmp(word, "TCP4") == 0) { - family = APR_INET; - valid_addr_chars = "0123456789."; - } - else if (strcmp(word, "TCP6") == 0) { -#if APR_HAVE_IPV6 - family = APR_INET6; - valid_addr_chars = "0123456789abcdefABCDEF:"; -#else - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03498) - "RemoteIPProxyProtocol: Unable to parse v6 address - APR is not compiled with IPv6 support", - word, hdr->v1.line); - return HDR_ERROR; -#endif - } - else { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03499) - "RemoteIPProxyProtocol: unknown family '%s' in header '%s'", - word, hdr->v1.line); - return HDR_ERROR; - } - - /* parse client-addr */ - GET_NEXT_WORD("client-address") - - if (strspn(word, valid_addr_chars) != strlen(word)) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03500) - "RemoteIPProxyProtocol: invalid client-address '%s' found in " - "header '%s'", word, hdr->v1.line); - return HDR_ERROR; - } - - host = word; - - /* parse dest-addr */ - GET_NEXT_WORD("destination-address") - - /* parse client-port */ - GET_NEXT_WORD("client-port") - if (sscanf(word, "%hu", &port) != 1) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03501) - "RemoteIPProxyProtocol: error parsing port '%s' in header '%s'", - word, hdr->v1.line); - return HDR_ERROR; - } - - /* parse dest-port */ - /* GET_NEXT_WORD("destination-port") - no-op since we don't care about it */ - - /* create a socketaddr from the info */ - ret = apr_sockaddr_info_get(&conn_conf->client_addr, host, family, port, 0, - c->pool); - if (ret != APR_SUCCESS) { - conn_conf->client_addr = NULL; - ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03502) - "RemoteIPProxyProtocol: error converting family '%d', host '%s'," - " and port '%hu' to sockaddr; header was '%s'", - family, host, port, hdr->v1.line); - return HDR_ERROR; - } - - conn_conf->client_ip = apr_pstrdup(c->pool, host); - - return HDR_DONE; -} - -/** Add our filter to the connection if it is requested - */ -static int remoteip_hook_pre_connection(conn_rec *c, void *csd) -{ - remoteip_config_t *conf; - remoteip_conn_config_t *conn_conf; - int optional; - - conf = ap_get_module_config(ap_server_conf->module_config, - &remoteip_module); - - /* Used twice - do the check only once */ - optional = remoteip_addr_in_list(conf->proxy_protocol_optional, c->local_addr); - - /* check if we're enabled for this connection */ - if ((!remoteip_addr_in_list(conf->proxy_protocol_enabled, c->local_addr) - && !optional ) - || remoteip_addr_in_list(conf->proxy_protocol_disabled, c->local_addr)) { - return DECLINED; - } - - /* mod_proxy creates outgoing connections - we don't want those */ - if (!remoteip_is_server_port(c->local_addr->port)) { - return DECLINED; - } - - /* add our filter */ - if (!ap_add_input_filter(remoteip_filter_name, NULL, NULL, c)) { - /* XXX: Shouldn't this WARN in log? */ - return DECLINED; - } - - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03503) - "RemoteIPProxyProtocol: enabled on connection to %s:%hu", - c->local_ip, c->local_addr->port); - - /* this holds the resolved proxy info for this connection */ - conn_conf = apr_pcalloc(c->pool, sizeof(*conn_conf)); - - /* Propagate the optional flag so the connection handler knows not to - abort if the header is mising. NOTE: This means we must check after - we read the request that the header was NOT optional, too. - */ - conn_conf->proxy_protocol_optional = optional; - - ap_set_module_config(c->conn_config, &remoteip_module, conn_conf); - - return OK; -} - -/* Binary format: - * - * sig = \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A - * cmd = <4-bits-version><4-bits-command> - * 4-bits-version = \x02 - * 4-bits-command = {\x00|\x01} (\x00 = LOCAL: discard con info; \x01 = PROXY) - * proto = <4-bits-family><4-bits-protocol> - * 4-bits-family = {\x00|\x01|\x02|\x03} (AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX) - * 4-bits-protocol = {\x00|\x01|\x02} (UNSPEC, STREAM, DGRAM) - */ -static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c, - remoteip_conn_config_t *conn_conf, - proxy_header *hdr) -{ - apr_status_t ret; - - switch (hdr->v2.ver_cmd & 0xF) { - case 0x01: /* PROXY command */ - switch (hdr->v2.fam) { - case 0x11: /* TCPv4 */ - ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL, - APR_INET, - ntohs(hdr->v2.addr.ip4.src_port), - 0, c->pool); - if (ret != APR_SUCCESS) { - conn_conf->client_addr = NULL; - ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03504) - "RemoteIPPProxyProtocol: error creating sockaddr"); - return HDR_ERROR; - } - - conn_conf->client_addr->sa.sin.sin_addr.s_addr = - hdr->v2.addr.ip4.src_addr; - break; - - case 0x21: /* TCPv6 */ -#if APR_HAVE_IPV6 - ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL, - APR_INET6, - ntohs(hdr->v2.addr.ip6.src_port), - 0, c->pool); - if (ret != APR_SUCCESS) { - conn_conf->client_addr = NULL; - ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03505) - "RemoteIPProxyProtocol: error creating sockaddr"); - return HDR_ERROR; - } - memcpy(&conn_conf->client_addr->sa.sin6.sin6_addr.s6_addr, - hdr->v2.addr.ip6.src_addr, 16); - break; -#else - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03506) - "RemoteIPProxyProtocol: APR is not compiled with IPv6 support"); - return HDR_ERROR; -#endif - default: - /* unsupported protocol, keep local connection address */ - return HDR_DONE; - } - break; /* we got a sockaddr now */ - - case 0x00: /* LOCAL command */ - /* keep local connection address for LOCAL */ - return HDR_DONE; - - default: - /* not a supported command */ - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03507) - "RemoteIPProxyProtocol: unsupported command %.2hx", - (unsigned short)hdr->v2.ver_cmd); - return HDR_ERROR; - } - - /* got address - compute the client_ip from it */ - ret = apr_sockaddr_ip_get(&conn_conf->client_ip, conn_conf->client_addr); - if (ret != APR_SUCCESS) { - conn_conf->client_addr = NULL; - ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03508) - "RemoteIPProxyProtocol: error converting address to string"); - return HDR_ERROR; - } - - return HDR_DONE; -} - -/** Determine if this is a v1 or v2 PROXY header. - */ -static int remoteip_determine_version(conn_rec *c, const char *ptr) -{ - proxy_header *hdr = (proxy_header *) ptr; - - /* assert len >= 14 */ - - if (memcmp(&hdr->v2, v2sig, sizeof(v2sig)) == 0 && - (hdr->v2.ver_cmd & 0xF0) == 0x20) { - return 2; - } - else if (memcmp(hdr->v1.line, "PROXY ", 6) == 0) { - return 1; - } - else { - return -1; - } -} - -/* Capture the first bytes on the protocol and parse the proxy protocol header. - * Removes itself when the header is complete. - */ -static apr_status_t remoteip_input_filter(ap_filter_t *f, - apr_bucket_brigade *bb_out, - ap_input_mode_t mode, - apr_read_type_e block, - apr_off_t readbytes) -{ - apr_status_t ret; - remoteip_filter_context *ctx = f->ctx; - remoteip_conn_config_t *conn_conf; - apr_bucket *b; - remoteip_parse_status_t psts; - const char *ptr; - apr_size_t len; - - if (f->c->aborted) { - return APR_ECONNABORTED; - } - - /* allocate/retrieve the context that holds our header */ - if (!ctx) { - ctx = f->ctx = apr_palloc(f->c->pool, sizeof(*ctx)); - ctx->rcvd = 0; - ctx->need = MIN_HDR_LEN; - ctx->version = 0; - ctx->mode = AP_MODE_READBYTES; - ctx->bb = apr_brigade_create(f->c->pool, f->c->bucket_alloc); - ctx->done = 0; - } - - if (ctx->done) { - /* Note: because we're a connection filter we can't remove ourselves - * when we're done, so we have to stay in the chain and just go into - * passthrough mode. - */ - return ap_get_brigade(f->next, bb_out, mode, block, readbytes); - } - - conn_conf = ap_get_module_config(f->c->conn_config, &remoteip_module); - - /* try to read a header's worth of data */ - while (!ctx->done) { - if (APR_BRIGADE_EMPTY(ctx->bb)) { - ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, - ctx->need - ctx->rcvd); - if (ret != APR_SUCCESS) { - return ret; - } - } - if (APR_BRIGADE_EMPTY(ctx->bb)) { - return APR_EOF; - } - - while (!ctx->done && !APR_BRIGADE_EMPTY(ctx->bb)) { - b = APR_BRIGADE_FIRST(ctx->bb); - - ret = apr_bucket_read(b, &ptr, &len, block); - if (APR_STATUS_IS_EAGAIN(ret) && block == APR_NONBLOCK_READ) { - return APR_SUCCESS; - } - if (ret != APR_SUCCESS) { - return ret; - } - - memcpy(ctx->header + ctx->rcvd, ptr, len); - ctx->rcvd += len; - - /* Remove instead of delete - we may put this bucket - back into bb_out if the header was optional and we - pass down the chain */ - APR_BUCKET_REMOVE(b); - psts = HDR_NEED_MORE; - - if (ctx->version == 0) { - /* reading initial chunk */ - if (ctx->rcvd >= MIN_HDR_LEN) { - ctx->version = remoteip_determine_version(f->c, ctx->header); - if (ctx->version < 0) { - psts = HDR_MISSING; - } - else if (ctx->version == 1) { - ctx->mode = AP_MODE_GETLINE; - ctx->need = sizeof(proxy_v1); - } - else if (ctx->version == 2) { - ctx->need = MIN_V2_HDR_LEN; - } - } - } - else if (ctx->version == 1) { - psts = remoteip_process_v1_header(f->c, conn_conf, - (proxy_header *) ctx->header, - ctx->rcvd, &ctx->need); - } - else if (ctx->version == 2) { - if (ctx->rcvd >= MIN_V2_HDR_LEN) { - ctx->need = MIN_V2_HDR_LEN + - ntohs(((proxy_header *) ctx->header)->v2.len); - } - if (ctx->rcvd >= ctx->need) { - psts = remoteip_process_v2_header(f->c, conn_conf, - (proxy_header *) ctx->header); - } - } - else { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(03509) - "RemoteIPProxyProtocol: internal error: unknown version " - "%d", ctx->version); - f->c->aborted = 1; - apr_bucket_delete(b); - apr_brigade_destroy(ctx->bb); - return APR_ECONNABORTED; - } - - switch (psts) { - case HDR_MISSING: - if (conn_conf->proxy_protocol_optional) { - /* Same as DONE, but don't delete the bucket. Rather, put it - back into the brigade and move the request along the stack */ - ctx->done = 1; - APR_BRIGADE_INSERT_HEAD(bb_out, b); - return ap_pass_brigade(f->next, ctx->bb); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(03510) - "RemoteIPProxyProtocol: no valid PROXY header found"); - /* fall through to error case */ - } - case HDR_ERROR: - f->c->aborted = 1; - apr_bucket_delete(b); - apr_brigade_destroy(ctx->bb); - return APR_ECONNABORTED; - - case HDR_DONE: - apr_bucket_delete(b); - ctx->done = 1; - break; - - case HDR_NEED_MORE: - /* It is safe to delete this bucket if we get here since we know - that we are definitely processing a header (we've read enough to - know if the signatures exist on the line) */ - apr_bucket_delete(b); - break; - } - } - } - - /* we only get here when done == 1 */ - if (psts == HDR_DONE) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03511) - "RemoteIPProxyProtocol: received valid PROXY header: %s:%hu", - conn_conf->client_ip, conn_conf->client_addr->port); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03512) - "RemoteIPProxyProtocol: PROXY header was missing"); - } - - if (ctx->rcvd > ctx->need || !APR_BRIGADE_EMPTY(ctx->bb)) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(03513) - "RemoteIPProxyProtocol: internal error: have data left over; " - " need=%lu, rcvd=%lu, brigade-empty=%d", ctx->need, - ctx->rcvd, APR_BRIGADE_EMPTY(ctx->bb)); - f->c->aborted = 1; - apr_brigade_destroy(ctx->bb); - return APR_ECONNABORTED; - } - - /* clean up */ - apr_brigade_destroy(ctx->bb); - ctx->bb = NULL; - - /* now do the real read for the upper layer */ - return ap_get_brigade(f->next, bb_out, mode, block, readbytes); -} - static const command_rec remoteip_cmds[] = { AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF, @@ -1205,21 +447,11 @@ static const command_rec remoteip_cmds[] = RSRC_CONF | EXEC_ON_READ, "The filename to read the list of internal proxies, " "see the RemoteIPInternalProxy directive"), - AP_INIT_TAKE1("RemoteIPProxyProtocolEnable", remoteip_enable_proxy_protocol, NULL, - RSRC_CONF, "Enable proxy-protocol handling (`on', `off')"), { NULL } }; static void register_hooks(apr_pool_t *p) { - /* mod_ssl is CONNECTION + 5, so we want something higher (earlier); - * mod_reqtimeout is CONNECTION + 8, so we want something lower (later) */ - ap_register_input_filter(remoteip_filter_name, remoteip_input_filter, NULL, - AP_FTYPE_CONNECTION + 7); - - ap_hook_pre_config(remoteip_hook_pre_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_post_config(remoteip_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_pre_connection(remoteip_hook_pre_connection, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(remoteip_modify_request, NULL, NULL, APR_HOOK_FIRST); } -- 2.40.0