/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. * *PortionsoftheinputfiltercodeforPROXYprotocolsupportis *Copyright2014CloudzillaInc.
*/
typedefstruct { /** A proxy IP mask to match */
apr_ipsubnet_t *ip; /** Flagged if internal, otherwise an external trusted proxy */ void *internal;
} remoteip_proxymatch_t;
typedefstruct { /** The header to retrieve a proxy-via IP list */ constchar *header_name; /** A header to record the proxied IP's *(removedasthephysicalconnectionand *fromtheproxy-viaIPheadervaluelist)
*/ constchar *proxies_header_name; /** A list of trusted proxies, ideally configured *withthemostcommonlyencounteredlistedfirst
*/
apr_array_header_t *proxymatch_ip;
typedefstruct {
apr_sockaddr_t *useragent_addr; char *useragent_ip; /** The list of proxy IP's ignored as remote IP's */ constchar *proxy_ips; /** The remaining list of untrusted proxied remote IP's */ constchar *proxied_remote;
} remoteip_req_t;
/* For PROXY protocol processing */ static ap_filter_rec_t *remoteip_filter;
typedefstruct { char line[108];
} proxy_v1;
typedefunion { 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;
typedefstruct {
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;
/* XXX: Unsure if this is needed if v6 support is not available on
this platform */ #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif
typedefstruct { 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 additional configurableparameters
*/ typedefstruct { /** The parsed client address in native format */
apr_sockaddr_t *client_addr; /** Character representation of the client */ char *client_ip;
} remoteip_conn_config_t;
/* Would be quite nice if APR exported this */ /* apr:network_io/unix/sockaddr.c */ staticint looks_like_ip(constchar *ipstr)
{ if (ap_strchr_c(ipstr, ':')) { /* definitely not a hostname; assume it is intended to be an IPv6 address */ return1;
}
if (!config->proxymatch_ip) {
config->proxymatch_ip = apr_array_make(cmd->pool, 1, sizeof(*match));
}
match = (remoteip_proxymatch_t *) apr_array_push(config->proxymatch_ip);
match->internal = cmd->info;
if (looks_like_ip(ip)) { /* Note s may be null, that's fine (explicit host) */
rv = apr_ipsubnet_create(&match->ip, ip, s, cmd->pool);
} else
{
apr_sockaddr_t *temp_sa;
if (s) { return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg, " the subnet /", s, " is invalid for ",
cmd->cmd->name, NULL);
}
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, new, APLOGNO(03491) "RemoteIPProxyProtocol: previous setting for %s:%hu from virtual " "host {%s:%hu in %s} is being overridden 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,
flag ? "On" : "Off");
}
if ((mask = ap_strchr(addr, '/')))
*mask++ = '\0';
rv = apr_ipsubnet_create(ip, addr, mask, p);
if (APR_STATUS_IS_EINVAL(rv)) { /* looked nothing like an IP address */ return apr_psprintf(p, "ip address '%s' appears to be invalid", addr);
} elseif (rv != APR_SUCCESS) { return apr_psprintf(p, "ip address '%s' appears to be invalid: %pm",
addr, &rv);
}
/* If no RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy orRemoteIPTrustedProxyListdirectiveisconfigured, allproxieswillbeconsideredasexternaltrustedproxies.
*/ void *internal = NULL;
/* No header defined or results from our input filter */ if (!config->header_name && !conn_config) { return DECLINED;
}
/* Easy parsing case - just position the data we already have from PROXY protocolhandlingallowingittotakeprecedenceandreturn
*/ if (conn_config) { if (!conn_config->client_addr) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03496) "RemoteIPProxyProtocol data is missing, but required! Aborting request."); return HTTP_BAD_REQUEST;
}
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 orRemoteIPTrustedProxyListdirectiveisconfigured. Inthiscase,defaulttointernalproxy.
*/
internal = (void *) 1;
}
/* verify user agent IP against the trusted proxy list
*/ if (config->proxymatch_ip) { int i;
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)) { if (internal) { /* Allow an internal proxy to present an external proxy, butdonotallowanexternalproxytopresentaninternalproxy. Inthiscase,thepresentedinternalproxywillbeconsideredexternal.
*/
internal = match[i].internal;
} break;
}
} if (i && i >= config->proxymatch_ip->nelts) { break;
}
}
/* We map as IPv4 rather than IPv6 for equivalent host names *orIPV4OVERIPV6
*/
rv = apr_sockaddr_info_get(&temp_sa, parse_remote,
APR_UNSPEC, temp_sa->port,
APR_IPV4_ADDR_OK, r->pool); if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01568) "RemoteIP: Header %s value of %s cannot be parsed " "as a client IP",
config->header_name, parse_remote);
/* 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;
}
/** Add our filter to the connection if it is requested
*/ staticint remoteip_hook_pre_connection(conn_rec *c, void *csd)
{
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;
}
/* 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;
}
/* We are enabled for this IP/port, but check that we aren't
explicitly disabled */ for (i = 0; i < conf->disabled_subnets->nelts; i++) {
apr_ipsubnet_t *ip = ((apr_ipsubnet_t**)conf->disabled_subnets->elts)[i];
if (ip && apr_ipsubnet_test(ip, c->client_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_handle(remoteip_filter, 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));
/* 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;
}
/** Return length for a v2 protocol header. */ static apr_size_t remoteip_get_v2_len(proxy_header *hdr)
{ return ntohs(hdr->v2.len);
}
/** Determine if this is a v1 or v2 PROXY header.
*/ staticint remoteip_determine_version(conn_rec *c, constchar *ptr)
{
proxy_header *hdr = (proxy_header *) ptr;
/* we only get here when done == 1 */
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);
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=%" APR_SIZE_T_FMT ", rcvd=%" APR_SIZE_T_FMT ", 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);
}
staticconst command_rec remoteip_cmds[] =
{
AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF, "Specifies a request header to trust as the client IP, " "e.g. X-Forwarded-For"),
AP_INIT_TAKE1("RemoteIPProxiesHeader", proxies_header_name_set,
NULL, RSRC_CONF, "Specifies a request header to record proxy IP's, " "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"),
AP_INIT_ITERATE("RemoteIPInternalProxy", proxies_set, (void*)1, RSRC_CONF, "Specifies one or more internal (transparent) proxies " "which are trusted to present IP headers"),
AP_INIT_TAKE1("RemoteIPTrustedProxyList", proxylist_read, 0,
RSRC_CONF | EXEC_ON_READ, "The filename to read the list of trusted proxies, " "see the RemoteIPTrustedProxy directive"),
AP_INIT_TAKE1("RemoteIPInternalProxyList", proxylist_read, (void*)1,
RSRC_CONF | EXEC_ON_READ, "The filename to read the list of internal proxies, " "see the RemoteIPInternalProxy directive"),
AP_INIT_FLAG("RemoteIPProxyProtocol", remoteip_enable_proxy_protocol, NULL,
RSRC_CONF, "Enable PROXY protocol handling ('on', 'off')"),
AP_INIT_TAKE_ARGV("RemoteIPProxyProtocolExceptions",
remoteip_disable_networks, NULL, RSRC_CONF, "Disable PROXY " "protocol handling for this list of networks in CIDR format"),
{ NULL }
};
staticvoid 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 =
ap_register_input_filter("REMOTEIP_INPUT", remoteip_input_filter, NULL,
AP_FTYPE_CONNECTION + 7);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.