From: Nick Kew Date: Wed, 28 May 2008 21:11:24 +0000 (+0000) Subject: Introduce variable interpolation in reverse proxy configuration X-Git-Tag: 2.2.9~82 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=299881961d3b42ca6860731776334e4cdbb53a4c;p=thirdparty%2Fapache%2Fhttpd.git Introduce variable interpolation in reverse proxy configuration git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@661087 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 012561ab22a..818cae56f1f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.2.9 + *) mod_proxy: Support environment variable interpolation in reverse + proxying directives. [Nick Kew] + *) suexec: When group is given as a numeric gid, validate it by looking up the actual group name such that the name can be used in log entries. PR 7862 [, Leif W ] diff --git a/STATUS b/STATUS index 7efcb3db46c..7ae5b2b2e3b 100644 --- a/STATUS +++ b/STATUS @@ -90,37 +90,6 @@ RELEASE SHOWSTOPPERS: PATCHES ACCEPTED TO BACKPORT FROM TRUNK: [ start all new proposals below, under PATCHES PROPOSED. ] - * mod_proxy: Support variable interpolation in reverse proxy configuration - trunk: - http://svn.apache.org/viewvc?view=rev&revision=421686 (code) - http://svn.apache.org/viewvc?view=rev&revision=422178 (code) - http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_proxy.xml?r1=420990&r2=421725 (docs) - http://svn.apache.org/viewvc?view=rev&revision=589371 (code+docs) - 2.2.x: - http://people.apache.org/~niq/proxy-interp.patch (OUTDATED) - http://people.apache.org/~niq/proxy-interpolate.patch (current code) - rpluem says: Please add the documentation to your 2.2.x version of your - patch and a minor bump (changes to public mod_proxy.h) and - assume you are +1 to your own proposal. - niq says: I'll attend to that. As regards vote, I'm +1 on the proposal, - but -0 on including this as a new feature in 2.2.7. - Better IMHO to separate releasing it from the many bugfixes - in 2.2.7 mod_proxy. Hence +1 for 2.2.8. - niq says: docs done; take MMN-on-backport as implicit. One more - vote required for 2.2.9 - +1: wrowe, niq - rpluem says: There is a problem with the new element interpolate_env in - proxy_dir_conf: To have the struct in the same order as in trunk we need to - add it BEFORE ftp_directory_charset as done in the patch. OTOH not adding - it to the end of the struct requires a MAJOR bump which we cannot do for - a backport. My proposal: Add interpolate_env to the end of proxy_dir_conf - in 2.2.x, do a minor bump of 2.2.x, swap the order of interpolate_env and - ftp_directory_charset on trunk and do a MAJOR bump on trunk (where it does - not really harm). - Apart from this problem I am +1. - niq says: committed the /trunk/ change, made the 2.2.x change - you suggest locally. Taking your +1 and promoting. - PATCHES PROPOSED TO BACKPORT FROM TRUNK: [ New proposals should be added at the end of the list ] diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 3ca53ac4e8f..e363b1ce1c3 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -567,7 +567,8 @@ expressions ProxyPass Maps remote servers into the local server URL-space -ProxyPass [path] !|url [key=value key=value ...]] [nocanon] +ProxyPass [path] !|url [key=value +key=value ...]] [nocanon] [interpolate] server configvirtual host directory @@ -853,6 +854,14 @@ expressions removes the normal limited protection against URL-based attacks provided by the proxy.

+

The optional interpolate keyword, in combination with + ProxyPassInterpolateEnv causes the ProxyPass + to interpolate environment variables, using the syntax + ${VARNAME}. Note that many of the standard CGI-derived + environment variables will not exist when this interpolation happens, + so you may still have to resort to mod_rewrite + for complex rules.

+

When used inside a Location section, the first argument is omitted and the local directory is obtained from the ProxyPassReverse Adjusts the URL in HTTP response headers sent from a reverse proxied server -ProxyPassReverse [path] url +ProxyPassReverse [path] url +[interpolate] server configvirtual host directory @@ -955,6 +965,11 @@ proxied server because it doesn't depend on a corresponding ProxyPass directive.

+

The optional interpolate keyword, used together with + ProxyPassInterpolateEnv, enables interpolation + of environment variables specified using the format ${VARNAME}. +

+

When used inside a Location section, the first argument is omitted and the local directory is obtained from the ProxyPassReverseCookieDomain Adjusts the Domain string in Set-Cookie headers from a reverse- proxied server -ProxyPassReverseCookieDomain internal-domain public-domain +ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate] server configvirtual host directory @@ -981,7 +997,8 @@ string in Set-Cookie headers.

ProxyPassReverseCookiePath Adjusts the Path string in Set-Cookie headers from a reverse- proxied server -ProxyPassReverseCookiePath internal-path public-path +ProxyPassReverseCookiePath internal-path +public-path [interpolate] server configvirtual host directory @@ -1388,4 +1405,34 @@ header for proxied requests
+ +ProxyPassInterpolateEnv +Enable Environment Variable interpolation in Reverse Proxy configurations +ProxyPassInterpolateEnv On|Off +ProxyPassInterpolateEnv Off +server config +virtual host +Location + +Available in trunk only + + +

This directive, together with the interpolate argument to + ProxyPass, ProxyPassReverse, + ProxyPassReverseCookieDomain and + ProxyPassReverseCookiePath + enables reverse proxies to be dynamically + configured using environment variables, which may be set by + another module such as mod_rewrite. + It affects the ProxyPass, + ProxyPassReverse, + ProxyPassReverseCookieDomain, and + ProxyPassReverseCookiePath directives, + and causes them to substitute the value of an environment + variable varname for the string ${varname} + in configuration directives.

+

Keep this turned off (for server performance) unless you need it!

+
+
+ diff --git a/include/ap_mmn.h b/include/ap_mmn.h index c5f023c1d6e..2c226b6cc62 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -126,6 +126,8 @@ * 20051115.14(2.2.9) Add ap_proxy_ssl_connection_cleanup and * add *scpool, *r and need_flush to proxy_conn_rec * structure + * 20051115.15(2.2.9) Add interpolate_env to proxy_dir_conf and + * introduce proxy_req_conf. * */ @@ -134,7 +136,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20051115 #endif -#define MODULE_MAGIC_NUMBER_MINOR 14 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 15 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 4ead5e9e0cf..00bcfebafe6 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -441,6 +441,59 @@ static int proxy_detect(request_rec *r) return DECLINED; } +static const char *proxy_interpolate(request_rec *r, const char *str) +{ + /* Interpolate an env str in a configuration string + * Syntax ${var} --> value_of(var) + * Method: replace one var, and recurse on remainder of string + * Nothing clever here, and crap like nested vars may do silly things + * but we'll at least avoid sending the unwary into a loop + */ + const char *start; + const char *end; + const char *var; + const char *val; + const char *firstpart; + + start = ap_strstr_c(str, "${"); + if (start == NULL) { + return str; + } + end = ap_strchr_c(start+2, '}'); + if (end == NULL) { + return str; + } + /* OK, this is syntax we want to interpolate. Is there such a var ? */ + var = apr_pstrndup(r->pool, start+2, end-(start+2)); + val = apr_table_get(r->subprocess_env, var); + firstpart = apr_pstrndup(r->pool, str, (start-str)); + + if (val == NULL) { + return apr_pstrcat(r->pool, firstpart, + proxy_interpolate(r, end+1), NULL); + } + else { + return apr_pstrcat(r->pool, firstpart, val, + proxy_interpolate(r, end+1), NULL); + } +} +static apr_array_header_t *proxy_vars(request_rec *r, + apr_array_header_t *hdr) +{ + int i; + apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts, + sizeof (struct proxy_alias)); + struct proxy_alias *old = (struct proxy_alias *) hdr->elts; + + for (i = 0; i < hdr->nelts; ++i) { + struct proxy_alias *newcopy = apr_array_push(ret); + newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE) + ? proxy_interpolate(r, old[i].fake) : old[i].fake; + newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE) + ? proxy_interpolate(r, old[i].real) : old[i].real; + } + return ret; +} static int proxy_trans(request_rec *r) { void *sconf = r->server->module_config; @@ -448,6 +501,10 @@ static int proxy_trans(request_rec *r) (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); int i, len; struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_module); + const char *fake; + const char *real; ap_regmatch_t regm[AP_MAX_REG_MATCH]; ap_regmatch_t reg1[AP_MAX_REG_MATCH]; char *found = NULL; @@ -468,9 +525,18 @@ static int proxy_trans(request_rec *r) for (i = 0; i < conf->aliases->nelts; i++) { unsigned int nocanon = ent[i].flags & PROXYPASS_NOCANON; const char *use_uri = nocanon ? r->unparsed_uri : r->uri; + if ((dconf->interpolate_env == 1) + && (ent[i].flags & PROXYPASS_INTERPOLATE)) { + fake = proxy_interpolate(r, ent[i].fake); + real = proxy_interpolate(r, ent[i].real); + } + else { + fake = ent[i].fake; + real = ent[i].real; + } if (ent[i].regex) { if (!ap_regexec(ent[i].regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) { - if ((ent[i].real[0] == '!') && (ent[i].real[1] == '\0')) { + if ((real[0] == '!') && (real[1] == '\0')) { return DECLINED; } /* test that we haven't reduced the URI */ @@ -479,8 +545,7 @@ static int proxy_trans(request_rec *r) mismatch = 1; use_uri = r->uri; } - found = ap_pregsub(r->pool, ent[i].real, use_uri, - AP_MAX_REG_MATCH, + found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH, (use_uri == r->uri) ? regm : reg1); /* Note: The strcmp() below catches cases where there * was no regex substitution. This is so cases like: @@ -495,20 +560,20 @@ static int proxy_trans(request_rec *r) * * which may be confusing. */ - if (found && strcmp(found, ent[i].real)) { + if (found && strcmp(found, real)) { found = apr_pstrcat(r->pool, "proxy:", found, NULL); } else { - found = apr_pstrcat(r->pool, "proxy:", ent[i].real, + found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL); } } } else { - len = alias_match(r->uri, ent[i].fake); + len = alias_match(r->uri, fake); - if (len > 0) { - if ((ent[i].real[0] == '!') && (ent[i].real[1] == '\0')) { + if (len != 0) { + if ((real[0] == '!') && (real[1] == '\0')) { return DECLINED; } if (nocanon @@ -516,7 +581,7 @@ static int proxy_trans(request_rec *r) mismatch = 1; use_uri = r->uri; } - found = apr_pstrcat(r->pool, "proxy:", ent[i].real, + found = apr_pstrcat(r->pool, "proxy:", real, use_uri + len, NULL); } } @@ -600,6 +665,7 @@ static int proxy_map_location(request_rec *r) return OK; } + /* -------------------------------------------------------------- */ /* Fixup the filename */ @@ -610,6 +676,8 @@ static int proxy_fixup(request_rec *r) { char *url, *p; int access_status; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_module); if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; @@ -617,6 +685,17 @@ static int proxy_fixup(request_rec *r) /* XXX: Shouldn't we try this before we run the proxy_walk? */ url = &r->filename[6]; + if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) { + /* create per-request copy of reverse proxy conf, + * and interpolate vars in it + */ + proxy_req_conf *rconf = apr_palloc(r->pool, sizeof(proxy_req_conf)); + ap_set_module_config(r->request_config, &proxy_module, rconf); + rconf->raliases = proxy_vars(r, dconf->raliases); + rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths); + rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains); + } + /* canonicalise each specific scheme */ if ((access_status = proxy_run_canon_handler(r, url))) { return access_status; @@ -1040,6 +1119,7 @@ static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); new->cookie_path_str = apr_strmatch_precompile(p, "path=", 0); new->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0); + new->interpolate_env = -1; /* unset */ return (void *) new; } @@ -1062,6 +1142,8 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) = apr_array_append(p, base->cookie_domains, add->cookie_domains); new->cookie_path_str = base->cookie_path_str; new->cookie_domain_str = base->cookie_domain_str; + new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env + : add->interpolate_env; new->ftp_directory_charset = add->ftp_directory_charset ? add->ftp_directory_charset : base->ftp_directory_charset; @@ -1178,6 +1260,9 @@ static const char * else if (!strcasecmp(word,"nocanon")) { flags |= PROXYPASS_NOCANON; } + else if (!strcasecmp(word,"interpolate")) { + flags |= PROXYPASS_INTERPOLATE; + } else { char *val = strchr(word, '='); if (!val) { @@ -1275,31 +1360,41 @@ static const char * } -static const char * - add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f, const char *r) +static const char * add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *i) { proxy_dir_conf *conf = dconf; struct proxy_alias *new; - - if (r!=NULL && cmd->path == NULL ) { - new = apr_array_push(conf->raliases); - new->fake = f; - new->real = r; - } else if (r==NULL && cmd->path != NULL) { - new = apr_array_push(conf->raliases); - new->fake = cmd->path; - new->real = f; - } else { - if ( r == NULL) + const char *fake; + const char *real; + const char *interp; + + if (cmd->path == NULL) { + fake = f; + real = r; + interp = i; + if (r == NULL || !strcasecmp(r, "interpolate")) { return "ProxyPassReverse needs a path when not defined in a location"; - else + } + } + else { + fake = cmd->path; + real = f; + if (r && strcasecmp(r, "interpolate")) { return "ProxyPassReverse can not have a path when defined in a location"; + } + interp = r; } + new = apr_array_push(conf->raliases); + new->fake = fake; + new->real = real; + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; + return NULL; } -static const char* - cookie_path(cmd_parms *cmd, void *dconf, const char *f, const char *r) +static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *interp) { proxy_dir_conf *conf = dconf; struct proxy_alias *new; @@ -1307,11 +1402,12 @@ static const char* new = apr_array_push(conf->cookie_paths); new->fake = f; new->real = r; + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; return NULL; } -static const char* - cookie_domain(cmd_parms *cmd, void *dconf, const char *f, const char *r) +static const char* cookie_domain(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *interp) { proxy_dir_conf *conf = dconf; struct proxy_alias *new; @@ -1319,7 +1415,7 @@ static const char* new = apr_array_push(conf->cookie_domains); new->fake = f; new->real = r; - + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; return NULL; } @@ -1940,15 +2036,18 @@ static const command_rec proxy_cmds[] = "a scheme, partial URL or '*' and a proxy server"), AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF, "a regex pattern and a proxy server"), + AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env), + RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") , AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL"), AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL"), - AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, + AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL for reverse proxy behaviour"), - AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL, + AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL, RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"), - AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL, + AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL, RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"), AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, "A list of names, hosts or domains to which the proxy will not connect"), diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 67ace2e308a..7a27a95c173 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -110,6 +110,7 @@ struct proxy_remote { }; #define PROXYPASS_NOCANON 0x01 +#define PROXYPASS_INTERPOLATE 0x02 struct proxy_alias { const char *real; const char *fake; @@ -212,9 +213,19 @@ typedef struct { apr_array_header_t* cookie_domains; const apr_strmatch_pattern* cookie_path_str; const apr_strmatch_pattern* cookie_domain_str; + int interpolate_env; const char *ftp_directory_charset; } proxy_dir_conf; +/* if we interpolate env vars per-request, we'll need a per-request + * copy of the reverse proxy config + */ +typedef struct { + apr_array_header_t *raliases; + apr_array_header_t* cookie_paths; + apr_array_header_t* cookie_domains; +} proxy_req_conf; + typedef struct { conn_rec *connection; const char *hostname; diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 3e4a1712676..e8309e463f2 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1041,6 +1041,7 @@ PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char * PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *url) { + proxy_req_conf *rconf; struct proxy_alias *ent; int i, l1, l2; char *u; @@ -1049,9 +1050,18 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, * XXX FIXME: Make sure this handled the ambiguous case of the : * after the hostname */ + if (r->proxyreq != PROXYREQ_REVERSE) { + return url; + } l1 = strlen(url); - ent = (struct proxy_alias *)conf->raliases->elts; + if (conf->interpolate_env == 1) { + rconf = ap_get_module_config(r->request_config, &proxy_module); + ent = (struct proxy_alias *)rconf->raliases->elts; + } + else { + ent = (struct proxy_alias *)conf->raliases->elts; + } for (i = 0; i < conf->raliases->nelts; i++) { proxy_server_conf *sconf = (proxy_server_conf *) ap_get_module_config(r->server->module_config, &proxy_module); @@ -1119,6 +1129,8 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *str) { + proxy_req_conf *rconf = ap_get_module_config(r->request_config, + &proxy_module); struct proxy_alias *ent; size_t len = strlen(str); const char *newpath = NULL; @@ -1133,6 +1145,10 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, int pdiff = 0; char *ret; + if (r->proxyreq != PROXYREQ_REVERSE) { + return str; + } + /* * Find the match and replacement, but save replacing until we've done * both path and domain so we know the new strlen @@ -1143,7 +1159,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, pathe = ap_strchr_c(pathp, ';'); l1 = pathe ? (pathe - pathp) : strlen(pathp); pathe = pathp + l1 ; - ent = (struct proxy_alias *)conf->cookie_paths->elts; + if (conf->interpolate_env == 1) { + ent = (struct proxy_alias *)rconf->cookie_paths->elts; + } + else { + ent = (struct proxy_alias *)conf->cookie_paths->elts; + } for (i = 0; i < conf->cookie_paths->nelts; i++) { l2 = strlen(ent[i].fake); if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { @@ -1160,7 +1181,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, domaine = ap_strchr_c(domainp, ';'); l1 = domaine ? (domaine - domainp) : strlen(domainp); domaine = domainp + l1; - ent = (struct proxy_alias *)conf->cookie_domains->elts; + if (conf->interpolate_env == 1) { + ent = (struct proxy_alias *)rconf->cookie_domains->elts; + } + else { + ent = (struct proxy_alias *)conf->cookie_domains->elts; + } for (i = 0; i < conf->cookie_domains->nelts; i++) { l2 = strlen(ent[i].fake); if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {