Commit e1b79730 authored by Nick Kew's avatar Nick Kew
Browse files

Support environment variable interpolation in reverse proxy configuration


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@421686 13f79535-47bb-0310-9956-ffa450edef68
parent 1ca10bb9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2,6 +2,9 @@
Changes with Apache 2.3.0
  [Remove entries to the current 2.0 and 2.2 section below, when backported]
  *) mod_proxy: Support environment variable interpolation in reverse
     proxying directives [Nick Kew]
  *) mod_proxy_balancer: Workers can now be defined as "hot standby" which
     will only be used if all other workers are unusable (eg: in
     error or disabled). Also, the balancer-manager displays the election
+26 −0
Original line number Diff line number Diff line
@@ -1143,4 +1143,30 @@ header for proxied requests</description>
</usage>
</directivesynopsis>

<directivesynopsis>
<name>ProxyPassInterpolateEnv</name>
<description>Enable Environment Variable interpolation in Reverse Proxy configurations</description>
<syntax>ProxyPassInterpolateEnv On|Off</syntax>
<default>ProxyErrorOverride Off</default>
<contextlist><context>server config</context>
<context>virtual host</context>
<context>Location</context>
</contextlist>
<compatibility>Available in trunk only</compatibility>

<usage>
    <p>This directive enables reverse proxies to be dynamically
    configured using environment variables, which may be set by
    another module such as <module>mod_rewrite</module>.
    It affects the <directive>ProxyPass</directive>,
    <directive>ProxyPassReverse</directive>,
    <directive>ProxyPassReverseCookieDomain</directive>, and
    <directive>ProxyPassReverseCookiePath</directive> directives,
    and causes them to substitute the value of an environment
    variable <code>varname</code> for the string <code>${varname}</code>
    in configuration directives.</p>
    <p>Keep this turned off (for server performance) unless you need it!</p>
</usage>
</directivesynopsis>

</modulesynopsis>
+87 −5
Original line number Diff line number Diff line
@@ -410,6 +410,57 @@ 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(str, "${");
    if (start == NULL) {
        return str;
    }
    end = ap_strchr(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 = proxy_interpolate(r, old[i].fake);
        newcopy->real = proxy_interpolate(r, old[i].real);
    }
    return ret;
}
static int proxy_trans(request_rec *r)
{
    void *sconf = r->server->module_config;
@@ -417,6 +468,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;

    if (r->proxyreq) {
        /* someone has already set up the proxy, it was possibly ourselves
@@ -431,14 +486,22 @@ static int proxy_trans(request_rec *r)
     */

    for (i = 0; i < conf->aliases->nelts; i++) {
        len = alias_match(r->uri, ent[i].fake);
        if (dconf->interpolate_env == 1) {
            fake = proxy_interpolate(r, ent[i].fake);
            real = proxy_interpolate(r, ent[i].real);
        }
        else {
            fake = ent[i].fake;
            real = ent[i].real;
        }
        len = alias_match(r->uri, fake);

       if (len > 0) {
           if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
           if ((real[0] == '!') && (real[1] == 0)) {
               return DECLINED;
           }

           r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
           r->filename = apr_pstrcat(r->pool, "proxy:", real,
                                     r->uri + len, NULL);
           r->handler = "proxy-server";
           r->proxyreq = PROXYREQ_REVERSE;
@@ -506,6 +569,7 @@ static int proxy_map_location(request_rec *r)

    return OK;
}

/* -------------------------------------------------------------- */
/* Fixup the filename */

@@ -516,6 +580,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;
@@ -523,6 +589,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;
@@ -905,6 +982,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;
}
@@ -927,6 +1005,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;
    return new;
}

@@ -1052,7 +1132,7 @@ static const char *

    arr = apr_table_elts(params);
    elts = (const apr_table_entry_t *)arr->elts;
    /* Distinguish the balancer from woker */
    /* Distinguish the balancer from worker */
    if (strncasecmp(r, "balancer:", 9) == 0) {
        proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
        if (!balancer) {
@@ -1672,6 +1752,9 @@ 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, NULL, RSRC_CONF|ACCESS_CONF,
     "a virtual path and a URL"),
    AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
@@ -1913,7 +1996,6 @@ static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
    proxy_lb_workers = 0;
    return OK;
}

static void register_hooks(apr_pool_t *p)
{
    /* fixup before mod_rewrite, so that the proxied url will not
+10 −0
Original line number Diff line number Diff line
@@ -206,8 +206,18 @@ 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;
} 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;
+29 −3
Original line number Diff line number Diff line
@@ -1015,6 +1015,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;
@@ -1023,9 +1024,18 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
     * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
     * after the hostname
     */
    if (r->proxyreq != PROXYREQ_REVERSE) {
        return url;
    }

    l1 = strlen(url);
    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++) {
        l2 = strlen(ent[i].real);
        if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
@@ -1047,6 +1057,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;
@@ -1061,6 +1073,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
@@ -1071,7 +1087,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 ;
        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) {
@@ -1088,7 +1109,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;
        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) {