diff --git a/modules/metadata/mod_remoteip.c b/modules/metadata/mod_remoteip.c index 7969766..61f7aa7 100644 --- a/modules/metadata/mod_remoteip.c +++ b/modules/metadata/mod_remoteip.c @@ -23,9 +23,10 @@ #include "httpd.h" #include "http_config.h" #include "http_connection.h" -#include "http_protocol.h" #include "http_log.h" #include "http_main.h" +#include "http_protocol.h" +#include "http_request.h" #include "apr_strings.h" #include "apr_lib.h" #define APR_WANT_BYTEFUNC @@ -55,6 +56,13 @@ typedef struct { * from the proxy-via IP header value list) */ const char *proxies_header_name; + /** A header that may indicate user is using a + * HTTPS connection to the reverse-proxy, and + * the value that it must match for it to do so. + */ + const char *secure_header_name; + const char *secure_header_value; + unsigned short secure_port; /** A list of trusted proxies, ideally configured * with the most commonly encountered listed first */ @@ -150,6 +158,8 @@ typedef struct { apr_sockaddr_t *client_addr; /** Character representation of the client */ char *client_ip; + /** The port on which the proxy server received the connection */ + unsigned short server_port; /** Flag indicating that the PROXY header may be omitted on this connection (do not abort if it is missing). */ int proxy_protocol_optional; @@ -168,6 +178,7 @@ static void *create_remoteip_server_config(apr_pool_t *p, server_rec *s) * config->pp_optional = 0; */ config->pool = p; + config->secure_port = 443; return config; } @@ -191,6 +202,15 @@ static void *merge_remoteip_server_config(apr_pool_t *p, void *globalv, config->pp_optional = server->pp_optional ? server->pp_optional : global->pp_optional; + config->secure_header_name = server->secure_header_name + ? server->secure_header_name + : global->secure_header_name; + config->secure_header_value = server->secure_header_value + ? server->secure_header_value + : global->secure_header_value; + config->secure_port = server->secure_port + ? server->secure_port + : global->secure_port; return config; } @@ -212,6 +232,44 @@ static const char *proxies_header_name_set(cmd_parms *cmd, void *dummy, return NULL; } +static const char *secure_header_set(cmd_parms *cmd, void *dummy, + const char *name, const char *value) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, + &remoteip_module); + if (!name || !value) + return "SecureIndicatorHeader requires header name and valid value"; + + config->secure_header_name = name; + config->secure_header_value = value; + return NULL; +} + +static int parse_validate_port(const char *src, unsigned short *dst) +{ + if (src) { + char *tail; + int intval; + intval = apr_strtoi64(src, &tail, 0); + if (errno == 0 && *tail == '\0' && intval > 0 && intval < 65536) { + *dst = (unsigned short) intval; + return 0; /* no error */ + } + return 2; /* not a number or invaid port range */ + } + return 1; /* null pointer */ +} + +static const char *secure_port_set(cmd_parms *cmd, void *dummy, const char *value) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, + &remoteip_module); + if (parse_validate_port(value, &config->secure_port)) + return "SecureIndicatorSSLPort must be an integer between 0 and 65536"; + + return NULL; +} + /* Would be quite nice if APR exported this */ /* apr:network_io/unix/sockaddr.c */ static int looks_like_ip(const char *ipstr) @@ -506,6 +564,7 @@ static int remoteip_modify_request(request_rec *r) char *proxy_ips = NULL; char *parse_remote; char *eos; + char *secure = NULL; unsigned char *addrbyte; /* If no RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy @@ -540,6 +599,10 @@ static int remoteip_modify_request(request_rec *r) r->useragent_addr = conn_config->client_addr; r->useragent_ip = conn_config->client_ip; + if (conn_config->server_port == config->secure_port) { + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Using %s as client's IP from PROXY protocol", r->useragent_ip); return OK; @@ -698,6 +761,21 @@ static int remoteip_modify_request(request_rec *r) return OK; } + if (config->secure_header_name) { + secure = (char *) apr_table_get(r->headers_in, config->secure_header_name); + } + + if (secure) { + if (!strcmp(secure, config->secure_header_value)) { + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + } + /* Header is available. Unset even if no match. */ + apr_table_unset(r->headers_in, config->secure_header_name); + } + else { + secure = NULL; + } + /* Port is not known so set it to zero; otherwise it can be misleading */ req->useragent_addr->port = 0; @@ -724,13 +802,46 @@ static int remoteip_modify_request(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, req->proxy_ips - ? "Using %s as client's IP by proxies %s" - : "Using %s as client's IP by internal proxies%s", + ? "Using %s as client's IP by %s proxies %s via" + : "Using %s as client's IP by %s internal proxies %s", req->useragent_ip, + secure ? "HTTPS" : "HTTP", (req->proxy_ips ? req->proxy_ips : "")); return OK; } +static int remoteip_ssl_hook_Fixup(struct request_rec *r) +{ + request_rec *realreq = r; + /* for internal redirect via mod_rewrite and other handlers */ + while (realreq->prev && apr_table_get(realreq->subprocess_env, "REDIRECT_STATUS")) + realreq = realreq->prev; + + const char* secure = apr_table_get(realreq->subprocess_env, "HTTPS"); + if (secure && secure[0] == 'o' && secure[1] == 'n' && secure[2] == '\0') + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + return OK; +} + +static const char* remoteip_read_scheme(const request_rec *r) +{ + const char* secure = (const char *) apr_table_get(r->subprocess_env, "HTTPS"); + if (secure && !strcmp(secure, "on")) + return "https"; + return NULL; +} + +static unsigned short remoteip_read_port(const request_rec *r) +{ + const char* secure = (const char *) apr_table_get(r->subprocess_env, "HTTPS"); + if (secure && !strcmp(secure, "on")) { + remoteip_config_t *config = (remoteip_config_t *) + ap_get_module_config(r->server->module_config, &remoteip_module); + return config->secure_port; + } + return 0; +} + static int remoteip_is_server_port(apr_port_t port) { ap_listen_rec *lr; @@ -827,7 +938,7 @@ static remoteip_parse_status_t remoteip_process_v1_header(conn_rec *c, /* parse client-port */ GET_NEXT_WORD("client-port") - if (sscanf(word, "%hu", &port) != 1) { + if (parse_validate_port(word, &port)) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03501) "RemoteIPProxyProtocol: error parsing port '%s' in header '%s'", word, hdr->v1.line); @@ -835,14 +946,21 @@ static remoteip_parse_status_t remoteip_process_v1_header(conn_rec *c, } /* parse dest-port */ - /* GET_NEXT_WORD("destination-port") - no-op since we don't care about it */ + GET_NEXT_WORD("destination-port") + + if (parse_validate_port(word, &conn_conf->server_port)) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03502) + "RemoteIPProxyProtocol: error parsing port '%s' in header '%s'", + word, hdr->v1.line); + return HDR_ERROR; + } /* 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) + ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c, APLOGNO(03503) "RemoteIPProxyProtocol: error converting family '%d', host '%s'," " and port '%hu' to sockaddr; header was '%s'", family, host, port, hdr->v1.line); @@ -935,6 +1053,7 @@ static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c, return HDR_ERROR; } + conn_conf->server_port = hdr->v2.addr.ip4.dst_port; conn_conf->client_addr->sa.sin.sin_addr.s_addr = hdr->v2.addr.ip4.src_addr; break; @@ -951,6 +1070,7 @@ static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c, "RemoteIPProxyProtocol: error creating sockaddr"); return HDR_ERROR; } + conn_conf->server_port = hdr->v2.addr.ip6.dst_port; memcpy(&conn_conf->client_addr->sa.sin6.sin6_addr.s6_addr, hdr->v2.addr.ip6.src_addr, 16); break; @@ -1232,6 +1352,12 @@ 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_TAKE2("SecureIndicatorHeader", secure_header_set, NULL, RSRC_CONF, + "Specifies a request header and value that indicates a secure connection, " + "e.g. \"X-Forwarded-Proto https\" or \"X-Secure-Connection on\""), + AP_INIT_TAKE1("SecureIndicatorSSLPort", secure_port_set, NULL, RSRC_CONF, + "Port to be used for redirections if SecureIndicatorHeader is set, " + "Default is \"SecureInidcatorSSLPort 443\" "), AP_INIT_TAKE1("RemoteIPProxyProtocol", remoteip_enable_proxy_protocol, NULL, RSRC_CONF, "Enable PROXY protocol handling (`on', `off', `optional')"), { NULL } @@ -1249,6 +1375,9 @@ static void register_hooks(apr_pool_t *p) 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); + ap_hook_http_scheme(remoteip_read_scheme, NULL, NULL, APR_HOOK_FIRST); + ap_hook_default_port(remoteip_read_port, NULL, NULL, APR_HOOK_FIRST); + ap_hook_fixups(remoteip_ssl_hook_Fixup, NULL, NULL, APR_HOOK_MIDDLE); } AP_DECLARE_MODULE(remoteip) = {