Skip to content

Commit 634b6a4

Browse files
Add initial support for trusted proxy headers, starting with protocol scheme forwarding.
1 parent 4e53323 commit 634b6a4

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

src/server/__init__.py

+14
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ def find_mimetypes():
369369
WSGIChunkedRequest On
370370
</IfDefine>
371371
372+
<IfDefine WSGI_WITH_PROXY_HEADERS>
373+
WSGITrustedProxyHeaders %(trusted_proxy_headers)s
374+
</IfDefine>
375+
372376
<IfDefine WSGI_WITH_HTTPS>
373377
<IfModule !ssl_module>
374378
LoadModule ssl_module ${HTTPD_MODULES_DIRECTORY}/mod_ssl.so
@@ -1791,6 +1795,11 @@ def check_percentage(option, opt_str, value, parser):
17911795
help='Proxy any requests for the specified host name to the '
17921796
'remote URL.'),
17931797

1798+
optparse.make_option('--trust-proxy-header', action='append', default=[],
1799+
dest='trusted_proxy_headers', metavar='HEADER-NAME',
1800+
help='The name of any trusted HTTP header providing details '
1801+
'of the front end client request when proxying.'),
1802+
17941803
optparse.make_option('--keep-alive-timeout', type='int', default=0,
17951804
metavar='SECONDS', help='The number of seconds which a client '
17961805
'connection will be kept alive to allow subsequent requests '
@@ -2463,6 +2472,9 @@ def _cmd_setup_server(command, args, options):
24632472

24642473
options['httpd_arguments_list'] = []
24652474

2475+
options['trusted_proxy_headers'] = ' '.join(
2476+
options['trusted_proxy_headers'])
2477+
24662478
if options['startup_log']:
24672479
if not options['log_to_terminal']:
24682480
options['startup_log_file'] = os.path.join(
@@ -2602,6 +2614,8 @@ def _cmd_setup_server(command, args, options):
26022614
options['httpd_arguments_list'].append('-DWSGI_WITH_PHP5')
26032615
if options['proxy_url_aliases'] or options['proxy_virtual_hosts']:
26042616
options['httpd_arguments_list'].append('-DWSGI_WITH_PROXY')
2617+
if options['trusted_proxy_headers']:
2618+
options['httpd_arguments_list'].append('-DWSGI_WITH_PROXY_HEADERS')
26052619

26062620
options['httpd_arguments_list'].extend(
26072621
_mpm_module_defines(options['modules_directory'],

src/server/mod_wsgi.c

+146
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ static void *wsgi_merge_server_config(apr_pool_t *p, void *base_conf,
184184
else
185185
config->map_head_to_get = parent->map_head_to_get;
186186

187+
if (child->trusted_proxy_headers)
188+
config->trusted_proxy_headers = child->trusted_proxy_headers;
189+
else
190+
config->trusted_proxy_headers = parent->trusted_proxy_headers;
191+
187192
if (child->enable_sendfile != -1)
188193
config->enable_sendfile = child->enable_sendfile;
189194
else
@@ -219,6 +224,8 @@ typedef struct {
219224
int chunked_request;
220225
int map_head_to_get;
221226

227+
apr_array_header_t *trusted_proxy_headers;
228+
222229
int enable_sendfile;
223230

224231
WSGIScriptFile *access_script;
@@ -251,6 +258,8 @@ static WSGIDirectoryConfig *newWSGIDirectoryConfig(apr_pool_t *p)
251258
object->chunked_request = -1;
252259
object->map_head_to_get = -1;
253260

261+
object->trusted_proxy_headers = NULL;
262+
254263
object->enable_sendfile = -1;
255264

256265
object->access_script = NULL;
@@ -338,6 +347,11 @@ static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
338347
else
339348
config->map_head_to_get = parent->map_head_to_get;
340349

350+
if (child->trusted_proxy_headers)
351+
config->trusted_proxy_headers = child->trusted_proxy_headers;
352+
else
353+
config->trusted_proxy_headers = parent->trusted_proxy_headers;
354+
341355
if (child->enable_sendfile != -1)
342356
config->enable_sendfile = child->enable_sendfile;
343357
else
@@ -398,6 +412,8 @@ typedef struct {
398412
int chunked_request;
399413
int map_head_to_get;
400414

415+
apr_array_header_t *trusted_proxy_headers;
416+
401417
int enable_sendfile;
402418

403419
WSGIScriptFile *access_script;
@@ -833,6 +849,11 @@ static WSGIRequestConfig *wsgi_create_req_config(apr_pool_t *p, request_rec *r)
833849
config->map_head_to_get = 2;
834850
}
835851

852+
config->trusted_proxy_headers = dconfig->trusted_proxy_headers;
853+
854+
if (!config->trusted_proxy_headers)
855+
config->trusted_proxy_headers = sconfig->trusted_proxy_headers;
856+
836857
config->enable_sendfile = dconfig->enable_sendfile;
837858

838859
if (config->enable_sendfile < 0) {
@@ -4971,6 +4992,40 @@ static const char *wsgi_set_map_head_to_get(cmd_parms *cmd, void *mconfig,
49714992
return NULL;
49724993
}
49734994

4995+
static char *wsgi_http2env(apr_pool_t *a, const char *w);
4996+
4997+
static const char *wsgi_set_trusted_proxy_headers(cmd_parms *cmd,
4998+
void *mconfig,
4999+
const char *args)
5000+
{
5001+
apr_array_header_t *headers = NULL;
5002+
5003+
if (cmd->path) {
5004+
WSGIDirectoryConfig *dconfig = NULL;
5005+
dconfig = (WSGIDirectoryConfig *)mconfig;
5006+
5007+
headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5008+
dconfig->trusted_proxy_headers = headers;
5009+
}
5010+
else {
5011+
WSGIServerConfig *sconfig = NULL;
5012+
sconfig = ap_get_module_config(cmd->server->module_config,
5013+
&wsgi_module);
5014+
5015+
headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5016+
sconfig->trusted_proxy_headers = headers;
5017+
}
5018+
5019+
while (*args) {
5020+
const char **entry = NULL;
5021+
5022+
entry = (const char **)apr_array_push(headers);
5023+
*entry = wsgi_http2env(cmd->pool, ap_getword_conf(cmd->pool, &args));
5024+
}
5025+
5026+
return NULL;
5027+
}
5028+
49745029
static const char *wsgi_set_enable_sendfile(cmd_parms *cmd, void *mconfig,
49755030
const char *f)
49765031
{
@@ -5424,6 +5479,7 @@ static int wsgi_hook_intercept(request_rec *r)
54245479
/* Handler for the response handler phase. */
54255480

54265481
static void wsgi_drop_invalid_headers(request_rec *r);
5482+
static void wsgi_process_proxy_headers(request_rec *r);
54275483

54285484
static void wsgi_build_environment(request_rec *r)
54295485
{
@@ -5459,6 +5515,8 @@ static void wsgi_build_environment(request_rec *r)
54595515
ap_add_cgi_vars(r);
54605516
ap_add_common_vars(r);
54615517

5518+
wsgi_process_proxy_headers(r);
5519+
54625520
/*
54635521
* Mutate a HEAD request into a GET request. This is
54645522
* required because WSGI specification doesn't lay out
@@ -12297,6 +12355,91 @@ static void wsgi_drop_invalid_headers(request_rec *r)
1229712355
}
1229812356
}
1229912357

12358+
static const char *wsgi_proxy_headers[] = {
12359+
"HTTP_X_FORWARDED_HTTPS",
12360+
"HTTP_X_FORWARDED_PROTO",
12361+
"HTTP_X_FORWARDED_SCHEME",
12362+
"HTTP_X_FORWARDED_SSL",
12363+
"HTTP_X_SCHEME",
12364+
NULL,
12365+
};
12366+
12367+
static void wsgi_process_proxy_headers(request_rec *r)
12368+
{
12369+
WSGIRequestConfig *config = NULL;
12370+
12371+
apr_array_header_t *trusted_proxy_headers = NULL;
12372+
12373+
int i = 0;
12374+
12375+
config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
12376+
&wsgi_module);
12377+
12378+
trusted_proxy_headers = config->trusted_proxy_headers;
12379+
12380+
/* Nothing to do if no trusted headers have been specified. */
12381+
12382+
if (!trusted_proxy_headers)
12383+
return;
12384+
12385+
/*
12386+
* Check for any special processing required for each trusted
12387+
* header which has been specified.
12388+
*/
12389+
12390+
for (i=0; i<trusted_proxy_headers->nelts; i++) {
12391+
const char *name = NULL;
12392+
const char *value = NULL;
12393+
12394+
name = ((const char**)trusted_proxy_headers->elts)[i];
12395+
value = apr_table_get(r->subprocess_env, name);
12396+
12397+
if (value) {
12398+
if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
12399+
!strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
12400+
!strcmp(name, "HTTP_X_SCHEME")) {
12401+
12402+
/* Value can be either 'http' or 'https'. */
12403+
12404+
if (!strcasecmp(value, "https"))
12405+
apr_table_setn(r->subprocess_env, "HTTPS", "1");
12406+
else if (!strcasecmp(value, "http"))
12407+
apr_table_unset(r->subprocess_env, "HTTPS");
12408+
}
12409+
else if (!strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
12410+
!strcmp(name, "HTTP_X_FORWARDED_SSL")) {
12411+
12412+
/*
12413+
* Value can be a boolean like flag such as 'On',
12414+
* 'Off', 'true', 'false', '1' or '0'.
12415+
*/
12416+
12417+
if (!strcasecmp(value, "On") ||
12418+
!strcasecmp(value, "true") ||
12419+
!strcasecmp(value, "1")) {
12420+
12421+
apr_table_setn(r->subprocess_env, "HTTPS", "1");
12422+
}
12423+
else if (!strcasecmp(value, "Off") ||
12424+
!strcasecmp(value, "false") ||
12425+
!strcasecmp(value, "0")) {
12426+
12427+
apr_table_unset(r->subprocess_env, "HTTPS");
12428+
}
12429+
}
12430+
}
12431+
}
12432+
12433+
/*
12434+
* Remove all trusted proxy headers from request environment
12435+
* so not reinterpreted by the WSGI applicaton.
12436+
*/
12437+
12438+
12439+
for (i=0; wsgi_proxy_headers[i]; i++)
12440+
apr_table_unset(r->subprocess_env, wsgi_proxy_headers[i]);
12441+
}
12442+
1230012443
static char *wsgi_http2env(apr_pool_t *a, const char *w)
1230112444
{
1230212445
char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w));
@@ -14428,6 +14571,9 @@ static const command_rec wsgi_commands[] =
1442814571
AP_INIT_TAKE1("WSGIMapHEADToGET", wsgi_set_map_head_to_get,
1442914572
NULL, OR_FILEINFO, "Enable/Disable mapping of HEAD to GET."),
1443014573

14574+
AP_INIT_RAW_ARGS("WSGITrustedProxyHeaders", wsgi_set_trusted_proxy_headers,
14575+
NULL, OR_FILEINFO, "Specify a list of trusted proxy headers."),
14576+
1443114577
#ifndef WIN32
1443214578
AP_INIT_TAKE1("WSGIEnableSendfile", wsgi_set_enable_sendfile,
1443314579
NULL, OR_FILEINFO, "Enable/Disable support for kernel sendfile."),

src/server/wsgi_server.h

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ typedef struct {
104104
int chunked_request;
105105
int map_head_to_get;
106106

107+
apr_array_header_t *trusted_proxy_headers;
108+
107109
int enable_sendfile;
108110

109111
apr_hash_t *handler_scripts;

0 commit comments

Comments
 (0)