diff --git a/docs/manual/mod/mod_lua.html.en.utf8 b/docs/manual/mod/mod_lua.html.en.utf8 index 7e2d4becc55..33e8b8eed68 100644 --- a/docs/manual/mod/mod_lua.html.en.utf8 +++ b/docs/manual/mod/mod_lua.html.en.utf8 @@ -517,7 +517,19 @@ end is_https boolean no - Whether or not this request is done via HTTPS + Whether or not this request is done via HTTPS (alias to remote_is_https) + + + remote_is_https + boolean + no + Whether or not this request is done via HTTPS locally or on frontend + + + local_is_https + boolean + no + Whether or not the local connection is done via HTTPS is_initial_req diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml index aecbca3ed9e..28a53870f3a 100644 --- a/docs/manual/mod/mod_lua.xml +++ b/docs/manual/mod/mod_lua.xml @@ -484,7 +484,19 @@ end is_https boolean no - Whether or not this request is done via HTTPS + Whether or not this request is done via HTTPS (alias to remote_is_https) + + + remote_is_https + boolean + no + Whether or not this request is done via HTTPS locally or on frontend + + + local_is_https + boolean + no + Whether or not the local connection is done via HTTPS is_initial_req diff --git a/include/ap_mmn.h b/include/ap_mmn.h index ccd504d8e82..07bc9724c10 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -737,6 +737,7 @@ * 20211221.29 (2.5.1-dev) Add ap_set_time_process_request() to scoreboard.h * 20211221.30 (2.5.1-dev) Add ap_stat_check() to httpd.h * 20211221.31 (2.5.1-dev) Add ap_*_timingsafe() to httpd.h + * 20211221.32 (2.5.1-dev) Add ap_remote_is_ssl() and hooks */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -744,7 +745,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20211221 #endif -#define MODULE_MAGIC_NUMBER_MINOR 31 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 32 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/http_request.h b/include/http_request.h index 55e61ae2416..3cd82be0790 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -622,6 +622,25 @@ AP_DECLARE_HOOK(int,force_authn,(request_rec *r)) */ AP_DECLARE_HOOK(apr_status_t,dirwalk_stat,(apr_finfo_t *finfo, request_rec *r, apr_int32_t wanted)) +/** + * This hook allows modules that virtualize SSL state (i.e. mod_remoteip) + * based on the data from remote frontend to register their inquiry function + * for checking if a remote frontend connection is using SSL for the request. + * @param r The current request + * @return OK iff the frontend connection is using SSL, DONE if not, + * DECLINED iff the state is undefined (let later modules decide). + * @ingroup hooks + */ +AP_DECLARE_HOOK(int,remote_is_ssl,(request_rec *r)) + +/** + * Return != 0 iff the frontend connection for the request is encrypted with SSL. + * If there is no data from the hook (DECLINED), falls back to ap_ssl_conn_is_ssl() + * and so corresponds to the local connection security state (we are the frontend). + * @param r The current request + */ +AP_DECLARE(int) ap_remote_is_ssl(request_rec *r); + AP_DECLARE(int) ap_location_walk(request_rec *r); AP_DECLARE(int) ap_directory_walk(request_rec *r); AP_DECLARE(int) ap_file_walk(request_rec *r); diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c index 587b690c6fa..3acb68a06b4 100644 --- a/modules/lua/lua_request.c +++ b/modules/lua/lua_request.c @@ -754,6 +754,11 @@ static int req_ssl_is_https_field(request_rec *r) return ap_lua_ssl_is_https(r->connection); } +static int req_remote_is_https_field(request_rec *r) +{ + return ap_lua_remote_is_https(r); +} + static int req_ap_get_server_port(request_rec *r) { return (int) ap_get_server_port(r); @@ -2782,7 +2787,11 @@ void ap_lua_load_request_lmodule(lua_State *L, apr_pool_t *p) apr_hash_set(dispatch, "ssl_var_lookup", APR_HASH_KEY_STRING, makefun(&req_ssl_var_lookup, APL_REQ_FUNTYPE_LUACFUN, p)); apr_hash_set(dispatch, "is_https", APR_HASH_KEY_STRING, + makefun(&req_remote_is_https_field, APL_REQ_FUNTYPE_BOOLEAN, p)); + apr_hash_set(dispatch, "local_is_https", APR_HASH_KEY_STRING, makefun(&req_ssl_is_https_field, APL_REQ_FUNTYPE_BOOLEAN, p)); + apr_hash_set(dispatch, "remote_is_https", APR_HASH_KEY_STRING, + makefun(&req_remote_is_https_field, APL_REQ_FUNTYPE_BOOLEAN, p)); apr_hash_set(dispatch, "assbackwards", APR_HASH_KEY_STRING, makefun(&req_assbackwards_field, APL_REQ_FUNTYPE_BOOLEAN, p)); apr_hash_set(dispatch, "status", APR_HASH_KEY_STRING, diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c index c2c751a92ae..5b724bf1eb8 100644 --- a/modules/lua/mod_lua.c +++ b/modules/lua/mod_lua.c @@ -1715,6 +1715,11 @@ int ap_lua_ssl_is_https(conn_rec *c) return ap_ssl_conn_is_ssl(c); } +int ap_lua_remote_is_https(request_rec *r) +{ + return ap_remote_is_ssl(r); +} + /*******************************/ static const char *lua_authz_parse(cmd_parms *cmd, const char *require_line, diff --git a/modules/lua/mod_lua.h b/modules/lua/mod_lua.h index 33807fbdb56..c3738f878af 100644 --- a/modules/lua/mod_lua.h +++ b/modules/lua/mod_lua.h @@ -183,5 +183,6 @@ const char *ap_lua_ssl_val(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *var); int ap_lua_ssl_is_https(conn_rec *c); +int ap_lua_remote_is_https(request_rec *r); #endif /* !_MOD_LUA_H_ */ diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c index c89b2adc786..b5eee48f76c 100644 --- a/modules/mappers/mod_rewrite.c +++ b/modules/mappers/mod_rewrite.c @@ -2086,7 +2086,7 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) case 5: if (!strcmp(var, "HTTPS")) { - int flag = ap_ssl_conn_is_ssl(r->connection); + int flag = ap_remote_is_ssl(r); return apr_pstrdup(r->pool, flag ? "on" : "off"); } break; @@ -2195,6 +2195,13 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) } break; + case 'H': + if (!strcmp(var, "HTTPS_LOCAL")) { + int flag = ap_ssl_conn_is_ssl(r->connection); + return apr_pstrdup(r->pool, flag ? "on" : "off"); + } + break; + case 'I': if (!strcmp(var, "API_VERSION")) { return apr_psprintf(r->pool, "%d:%d", @@ -2244,6 +2251,13 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) case 12: switch (var[3]) { + case 'H': + if (!strcmp(var, "HTTPS_REMOTE")) { + int flag = ap_remote_is_ssl(r); + return apr_pstrdup(r->pool, flag ? "on" : "off"); + } + break; + case 'I': if (!strcmp(var, "SCRIPT_GROUP")) { result = ""; diff --git a/modules/metadata/mod_remoteip.c b/modules/metadata/mod_remoteip.c index 805c6543ea5..c6ede9ef1da 100644 --- a/modules/metadata/mod_remoteip.c +++ b/modules/metadata/mod_remoteip.c @@ -25,6 +25,9 @@ #include "http_connection.h" #include "http_protocol.h" #include "http_log.h" +#include "http_request.h" +#include "http_ssl.h" +#include "http_vhost.h" #include "http_main.h" #include "apr_strings.h" #include "apr_lib.h" @@ -61,6 +64,13 @@ typedef struct { */ apr_array_header_t *proxymatch_ip; + const char *host_header_name; + const char *port_header_name; + const char *proto_header_name; + const char *proto_https_enable; + int proto_https_enable_any; + int forbid_direct; + remoteip_addr_info *proxy_protocol_enabled; remoteip_addr_info *proxy_protocol_disabled; @@ -141,10 +151,18 @@ typedef struct { configurable parameters */ typedef struct { + /** Indicates presence of client address info in the structure **/ + int has_client_addr; /** The parsed client address in native format */ apr_sockaddr_t *client_addr; /** Character representation of the client */ char *client_ip; + /** Remote port */ + int remote_port; + /** Indicates presence of SSL flag */ + int has_remote_is_ssl; + /** Remote SSL flag */ + int remote_is_ssl; } remoteip_conn_config_t; typedef enum { HDR_DONE, HDR_ERROR, HDR_NEED_MORE } remoteip_parse_status_t; @@ -157,6 +175,11 @@ static void *create_remoteip_server_config(apr_pool_t *p, server_rec *s) * config->proxies_header_name = NULL; * config->proxy_protocol_enabled = NULL; * config->proxy_protocol_disabled = NULL; + * config->host_header_name = NULL; + * config->port_header_name = NULL; + * config->proto_header_name = NULL; + * config->proto_https_enable = NULL; + * config->proto_https_enable_any = 0; */ config->pool = p; return config; @@ -179,6 +202,23 @@ 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->host_header_name = server->host_header_name + ? server->host_header_name + : global->host_header_name; + config->port_header_name = server->port_header_name + ? server->port_header_name + : global->port_header_name; + config->proto_header_name = server->proto_header_name + ? server->proto_header_name + : global->proto_header_name; + config->proto_https_enable = server->proto_https_enable + ? server->proto_https_enable + : global->proto_https_enable; + config->proto_https_enable_any = (config->proto_https_enable && !strcmp("*", config->proto_https_enable)); + config->forbid_direct = server->forbid_direct + ? server->forbid_direct + : global->forbid_direct; + return config; } @@ -540,15 +580,21 @@ 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) { + /* Avoid doing any work on internal subrequests or redirects */ + if (r->main || r->prev) { + return DECLINED; + } + + /* No headers defined or results from our input filter */ + if (!conn_config && !config->header_name + && !config->host_header_name && !config->port_header_name + && !config->proto_header_name && !config->forbid_direct) { 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) { + + /* Position the remote IP data we may already have from PROXY protocol handling + */ + if (conn_config && conn_config->has_client_addr) { if (!conn_config->client_addr) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03496) "RemoteIPProxyProtocol data is missing, but required! Aborting request."); @@ -560,7 +606,8 @@ static int remoteip_modify_request(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Using %s as client's IP from PROXY protocol", r->useragent_ip); - return OK; + /* We do not return here as we may still have specific remote parameters headers set by proxy + */ } if (config->proxymatch_ip) { @@ -571,14 +618,117 @@ static int remoteip_modify_request(request_rec *r) internal = (void *) 1; } + temp_sa = r->useragent_addr ? r->useragent_addr : c->client_addr; + + if (internal && (config->host_header_name || config->port_header_name || config->proto_header_name || config->forbid_direct)) { + /* The trusted proxy (or list of) is configured, honor the option to forbid direct connections (allowing only trusted internal proxies) + Also, if the connecting host is trusted internal proxy and some request modification headers are set, we honor these headers there + */ + int i; + void *first_internal = NULL; + remoteip_proxymatch_t *match; + match = (remoteip_proxymatch_t *)config->proxymatch_ip->elts; + for (i = 0; i < config->proxymatch_ip->nelts; ++i) { + if (apr_ipsubnet_test(match[i].ip, temp_sa)) { + first_internal = match[i].internal; + break; + } + } + if ((i && i >= config->proxymatch_ip->nelts) || !first_internal) { + if (config->forbid_direct) { + /* Whoops, trusted internal proxy was not detected and direct requests are forbidden, deny the request + */ + return HTTP_FORBIDDEN; + } + } else { + /* Internal trusted proxy is connecting, perform request modification if any options for it are set + */ + char *val; + + /* Remote host option, applied directly to the request host header + */ + if (config->host_header_name) { + const char *host; + if ( (host = apr_table_get(r->headers_in, config->host_header_name)) ) { + /* Propagate host header value from the header set by proxy and update virtual host chosen (the primary purpose of it) + */ + apr_array_header_t *hdrs = apr_array_make(r->pool, 0, sizeof(char*)); + while (*host && (val = ap_get_token(r->pool, &host, 1))) { + APR_ARRAY_PUSH(hdrs, char *) = apr_pstrdup(r->pool, val); + if (*host != '\0') + ++host; + } + apr_table_setn(r->headers_in, "Host", apr_pstrdup(r->pool, APR_ARRAY_IDX(hdrs, hdrs->nelts - 1, char *))); + r->hostname = apr_pstrdup(r->pool, APR_ARRAY_IDX(hdrs, hdrs->nelts - 1, char *)); + ap_update_vhost_from_headers(r); + } + } + + /* Remote port option, due to the transient nature of subrequests we have to store the port number for entire connection for ap_default_port() hook + */ + if (config->port_header_name) { + const char *port; + + if (!conn_config) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO() + "Remote IP per-connection data is missing, but required for RemotePortHeader! Aborting request."); + return HTTP_BAD_REQUEST; + } + + if ( (port = apr_table_get(r->headers_in, config->port_header_name) )) { + r->parsed_uri.port = r->server->port; + conn_config->remote_port = atoi(port); + } else { + /* Subsequent requests inside a single connection need to have the port number reset if they do not have the header set + */ + conn_config->remote_port = 0; + } + } + + /* Remote HTTPS protocol option, due to transient nature of subrequests we have to store the flag for entire connection for ap_remote_is_ssl() and fixup hooks + */ + if (config->proto_header_name) { + const char *proto; + + if (!conn_config) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO() + "Remote IP per-connection data is missing, but required for RemoteProtoHeader! Aborting request."); + return HTTP_BAD_REQUEST; + } + + /* Indicate we have set the remote_is_ssl flag + */ + conn_config->has_remote_is_ssl = 1; + + if ( (proto = apr_table_get(r->headers_in, config->proto_header_name)) ) { + /* Check HTTPS protocol ID, defaulting to 'https' if not set, proto_https_enable_any is set in config option/merge for empty string or '*' + */ + if (config->proto_https_enable_any || !ap_cstr_casecmp(proto, config->proto_https_enable ? config->proto_https_enable : "https")) { + conn_config->remote_is_ssl = 1; + } else { + /* Subsequent requests inside a single connection need to have the flag reset if they do not have the content match + */ + conn_config->remote_is_ssl = 0; + } + } else { + /* Subsequent requests inside a single connection need to have the flag reset if they do not have the header found + */ + conn_config->remote_is_ssl = 0; + } + } + } + } + + /* No need to proceed further if we have no remote IP header set + */ + if (!config->header_name) return OK; + remote = (char *) apr_table_get(r->headers_in, config->header_name); if (!remote) { return OK; } remote = apr_pstrdup(r->pool, remote); - temp_sa = r->useragent_addr ? r->useragent_addr : c->client_addr; - while (remote) { /* verify user agent IP against the trusted proxy list @@ -749,6 +899,104 @@ static int remoteip_modify_request(request_rec *r) return OK; } +static int remoteip_hook_remote_is_ssl(request_rec *r) +{ + remoteip_conn_config_t *conn_config = (remoteip_conn_config_t *) + ap_get_module_config(r->connection->conn_config, &remoteip_module); + + if (conn_config && conn_config->has_remote_is_ssl) { + return conn_config->remote_is_ssl ? OK : DONE; + } + + return DECLINED; +} + +static const char *remoteip_hook_http_scheme(const request_rec *r) +{ + remoteip_conn_config_t *conn_config = (remoteip_conn_config_t *) + ap_get_module_config(r->connection->conn_config, &remoteip_module); + + if (!conn_config) { + return NULL; + } + + if (!conn_config->has_remote_is_ssl) { + return NULL; + } + + return (conn_config->remote_is_ssl) ? "https" : "http"; +} + +static apr_port_t remoteip_hook_default_port(const request_rec *r) +{ + remoteip_conn_config_t *conn_config = (remoteip_conn_config_t *) + ap_get_module_config(r->connection->conn_config, &remoteip_module); + + if (!conn_config) { + return 0; + } + + return conn_config->remote_port; +} + +static int remoteip_hook_fixups(request_rec *r) +{ + remoteip_conn_config_t *conn_config = (remoteip_conn_config_t *) + ap_get_module_config(r->connection->conn_config, &remoteip_module); + + if (!conn_config) { + return DECLINED; + } + + if (!conn_config->has_remote_is_ssl) { + return DECLINED; + } + + if (conn_config->remote_is_ssl) { + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + } else { + apr_table_unset(r->subprocess_env, "HTTPS"); + } + + return DECLINED; +} + +static const char *host_header_name_set(cmd_parms *cmd, void *dummy, const char *arg) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, &remoteip_module); + config->host_header_name = arg; + return NULL; +} + +static const char *port_header_name_set(cmd_parms *cmd, void *dummy, const char *arg) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, &remoteip_module); + config->port_header_name = arg; + return NULL; +} + +static const char *proto_header_name_set(cmd_parms *cmd, void *dummy, const char *arg) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, &remoteip_module); + config->proto_header_name = arg; + return NULL; +} + +static const char *proto_https_enable_set(cmd_parms *cmd, void *dummy, const char *arg) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, &remoteip_module); + config->proto_https_enable = arg; + config->proto_https_enable_any = (config->proto_https_enable && !strcmp("*", config->proto_https_enable)); + return NULL; +} + +static const char *forbid_direct_set(cmd_parms *cmd, void *dummy, int flag) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, &remoteip_module); + config->forbid_direct = flag; + return NULL; +} + static int remoteip_is_server_port(apr_port_t port) { ap_listen_rec *lr; @@ -868,35 +1116,22 @@ static remoteip_parse_status_t remoteip_process_v1_header(conn_rec *c, conn_conf->client_ip = apr_pstrdup(c->pool, host); + conn_conf->has_client_addr = 1; + return HDR_DONE; } -/** Add our filter to the connection if it is requested +/* Add our filter to the connection if it is requested */ -static int remoteip_hook_pre_connection(conn_rec *c, void *csd) +static int remoteip_pre_check_for_proxy_protocol(conn_rec *c, remoteip_config_t *conf) { - remoteip_config_t *conf; - remoteip_conn_config_t *conn_conf; int i; - /* Establish master config in slave connections, so that request processing - * finds it. */ - if (c->master != NULL) { - conn_conf = ap_get_module_config(c->master->conn_config, &remoteip_module); - if (conn_conf) { - ap_set_module_config(c->conn_config, &remoteip_module, conn_conf); - } - return DECLINED; - } - - conf = ap_get_module_config(ap_server_conf->module_config, - &remoteip_module); - /* check if we're enabled for this connection */ if (!remoteip_addr_in_list(conf->proxy_protocol_enabled, c->local_addr) || remoteip_addr_in_list(conf->proxy_protocol_disabled, c->local_addr)) { - return DECLINED; + return 0; } /* We are enabled for this IP/port, but check that we aren't @@ -905,27 +1140,70 @@ static int remoteip_hook_pre_connection(conn_rec *c, void *csd) apr_ipsubnet_t *ip = ((apr_ipsubnet_t**)conf->disabled_subnets->elts)[i]; if (ip && apr_ipsubnet_test(ip, c->client_addr)) - return DECLINED; + return 0; } /* mod_proxy creates outgoing connections - we don't want those */ if (!remoteip_is_server_port(c->local_addr->port)) { - return DECLINED; + return 0; } /* add our filter */ if (!ap_add_input_filter_handle(remoteip_filter, NULL, NULL, c)) { /* XXX: Shouldn't this WARN in log? */ - return DECLINED; + return 0; } 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)); + return 1; +} + +/* Pre-connection check for PROXY protocol and other conditions requiring + * per-connection configuration to be created + */ +static int remoteip_hook_pre_connection(conn_rec *c, void *csd) +{ + remoteip_config_t *conf; + remoteip_conn_config_t *conn_conf; + int need_conf; + + /* Establish master config in slave connections, so that request processing + * finds it. */ + if (c->master != NULL) { + conn_conf = ap_get_module_config(c->master->conn_config, &remoteip_module); + if (conn_conf) { + ap_set_module_config(c->conn_config, &remoteip_module, conn_conf); + } + return DECLINED; + } + + conf = ap_get_module_config(ap_server_conf->module_config, + &remoteip_module); + /* Check if we have PROXY protocol enabled, this goes first as it needs to install the filter + */ + if (remoteip_pre_check_for_proxy_protocol(c, conf)) { + need_conf = 1; + } + + /* Check if we need to store remote port or SSL information, not joined with previous check for clarity + */ + if (conf->port_header_name || conf->proto_header_name) { + need_conf = 1; + } + + /* Bail out if we do not need the configuration structure + */ + if (!need_conf) { + return DECLINED; + } + + /* This holds the resolved proxy info and/or remote SSL info for this connection + */ + conn_conf = apr_pcalloc(c->pool, sizeof(*conn_conf)); ap_set_module_config(c->conn_config, &remoteip_module, conn_conf); return OK; @@ -1036,6 +1314,8 @@ static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c, return HDR_ERROR; } + conn_conf->has_client_addr = 1; + return HDR_DONE; } @@ -1242,7 +1522,7 @@ static const command_rec remoteip_cmds[] = "e.g. X-Forwarded-By; if not given then do not record"), AP_INIT_ITERATE("RemoteIPTrustedProxy", proxies_set, 0, RSRC_CONF, "Specifies one or more proxies which are trusted " - "to present IP headers"), + "to present IP and request modification headers"), AP_INIT_ITERATE("RemoteIPInternalProxy", proxies_set, (void*)1, RSRC_CONF, "Specifies one or more internal (transparent) proxies " "which are trusted to present IP headers"), @@ -1259,6 +1539,25 @@ static const command_rec remoteip_cmds[] = AP_INIT_TAKE_ARGV("RemoteIPProxyProtocolExceptions", remoteip_disable_networks, NULL, RSRC_CONF, "Disable PROXY " "protocol handling for this list of networks in CIDR format"), + AP_INIT_TAKE1("RemoteHostHeader", host_header_name_set, NULL, RSRC_CONF, + "Specifies a request header to trust as the original Host header, " + "e.g. X-Forwarded-Host, valid only if direct client is internal proxy, " + "defaults to disabled"), + AP_INIT_TAKE1("RemotePortHeader", port_header_name_set, NULL, RSRC_CONF, + "Specifies a request header to trust as the original Port header, " + "e.g. X-Forwarded-Port, valid only if direct client is internal proxy, " + "defaults to disabled"), + AP_INIT_TAKE1("RemoteProtoHeader", proto_header_name_set, NULL, RSRC_CONF, + "Specifies a request header to trust as the original protocol header, " + "e.g. X-Forwarded-Proto, valid only if direct client is internal proxy, " + "defaults to disabled"), + AP_INIT_TAKE1("RemoteHTTPSEnableProto", proto_https_enable_set, NULL, RSRC_CONF, + "Specifies a value in the header specified by RemoteProtoHeader that " + "is required to set HTTPS flag enabled, setting this to '*' will enable " + "HTTPS flag just on header presence, disregarding the header value, the " + "comparison is case-insensitive, defaults to 'https'"), + AP_INIT_FLAG("RemoteAllowOnlyInternalProxies", forbid_direct_set, NULL, RSRC_CONF, + "Deny access from any hosts besides trusted internal proxies"), { NULL } }; @@ -1266,13 +1565,17 @@ 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) */ - remoteip_filter = + remoteip_filter = ap_register_input_filter("REMOTEIP_INPUT", remoteip_input_filter, NULL, AP_FTYPE_CONNECTION + 7); 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_remote_is_ssl(remoteip_hook_remote_is_ssl, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_http_scheme(remoteip_hook_http_scheme, NULL, NULL, APR_HOOK_FIRST); + ap_hook_default_port(remoteip_hook_default_port, NULL, NULL, APR_HOOK_FIRST); + ap_hook_fixups(remoteip_hook_fixups, NULL, NULL, APR_HOOK_LAST); } AP_DECLARE_MODULE(remoteip) = { diff --git a/server/request.c b/server/request.c index 791d1cea6b9..a3210f913d4 100644 --- a/server/request.c +++ b/server/request.c @@ -41,6 +41,7 @@ #include "http_protocol.h" #include "http_log.h" #include "http_main.h" +#include "http_ssl.h" #include "util_filter.h" #include "util_charset.h" #include "util_script.h" @@ -74,6 +75,7 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(dirwalk_stat) APR_HOOK_LINK(force_authn) APR_HOOK_LINK(token_checker) + APR_HOOK_LINK(remote_is_ssl) ) AP_IMPLEMENT_HOOK_RUN_FIRST(int,pre_translate_name, @@ -106,6 +108,8 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int,force_authn, (request_rec *r), (r), DECLINED) AP_IMPLEMENT_HOOK_RUN_FIRST(int,token_checker, (request_rec *r), (r), DECLINED) +AP_IMPLEMENT_HOOK_RUN_FIRST(int, remote_is_ssl, + (request_rec *r), (r), DECLINED) static int auth_internal_per_conf = 0; static int auth_internal_per_conf_hooks = 0; @@ -2603,3 +2607,12 @@ AP_DECLARE(int) ap_is_initial_req(request_rec *r) && (r->prev == NULL); /* otherwise, this is an internal redirect */ } +/* + * Is remote frontend connection SSL? Hooks return OK if yes, DONE if no + * If hook sequence returns DECLINED, we fall back to ap_ssl_conn_is_ssl() +*/ +AP_DECLARE(int) ap_remote_is_ssl(request_rec *r) +{ + int result = ap_run_remote_is_ssl(r); + return (result == DECLINED) ? ap_ssl_conn_is_ssl(r->connection) : (result == OK); +} diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c index 82f3288a2c1..8bb9e248dfa 100644 --- a/server/util_expr_eval.c +++ b/server/util_expr_eval.c @@ -1696,7 +1696,7 @@ APR_DECLARE_OPTIONAL_FN(int, http2_is_h2, (conn_rec *)); static APR_OPTIONAL_FN_TYPE(http2_is_h2) *is_http2 = NULL; static const char *const conn_var_names[] = { - "HTTPS", /* 0 */ + "HTTPS_LOCAL", /* 0 */ "IPV6", /* 1 */ "CONN_LOG_ID", /* 2 */ "CONN_REMOTE_ADDR", /* 3 */ @@ -1779,6 +1779,8 @@ static const char *const request_var_names[] = { "SERVER_PROTOCOL_VERSION_MAJOR", /* 30 */ "SERVER_PROTOCOL_VERSION_MINOR", /* 31 */ "REMOTE_PORT", /* 32 */ + "HTTPS", /* 33 */ + "HTTPS_REMOTE", /* 34 */ NULL }; @@ -1890,6 +1892,12 @@ static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data) return apr_psprintf(ctx->p, "%d", HTTP_VERSION_MINOR(r->proto_num)); case 32: return apr_psprintf(ctx->p, "%u", ctx->c->client_addr->port); + case 33: + case 34: + if (ap_remote_is_ssl(r)) + return "on"; + else + return "off"; default: ap_assert(0); return NULL;