Mojolicious::Plugin::TrustedProxy - Mojolicious plugin to set the remote address, connection scheme, and more from trusted upstream proxies
Version 0.05
use Mojolicious::Lite;
plugin 'TrustedProxy' => {
enabled => 1,
ip_headers => ['x-forwarded-for', 'x-real-ip'],
scheme_headers => ['x-forwarded-proto', 'x-ssl'],
https_values => ['https', 'on', '1', 'true', 'enable', 'enabled'],
parse_rfc7239 => 1,
trusted_sources => ['127.0.0.0/8', '10.0.0.0/8'],
hide_headers => 0,
};
# Example of how you could verify expected functionality
get '/test' => sub {
my $c = shift;
$c->render(json => {
'tx.remote_address' => $c->tx->remote_address,
'tx.remote_proxy_address' => $c->tx->remote_proxy_address,
'req.url.base.scheme' => $c->req->url->base->scheme,
'req.url.base.host' => $c->req->url->base->host,
'is_trusted_source' => $c->is_trusted_source,
'is_trusted_source("1.1.1.1")' => $c->is_trusted_source('1.1.1.1'),
});
};
app->start;
Mojolicious::Plugin::TrustedProxy modifies every Mojolicious request transaction to override connecting user agent values only when the request comes from trusted upstream sources. You can specify multiple request headers where trusted upstream sources define the real user agent IP address or the real connection scheme, or disable either, and can hide the headers from the rest of the application if needed.
This plugin provides much of the same functionality as setting MOJO_REVERSE_PROXY=1
, but with more granular control over what headers to use and what upstream sources can send them. This is especially useful if your Mojolicious app is directly exposed to the internet, or if it sits behind multiple upstream proxies. You should therefore ensure your application does not enable the default Mojolicious reverse proxy handler when using this plugin.
This plugin supports parsing RFC 7239 compliant Forwarded
headers, validates all IP addresses, and will automatically convert RFC-4291 IPv4-to-IPv6 mapped values (useful for when your Mojolicious listens on both IP versions). Please be aware that Forwarded
headers are only partially supported. More information is available in "BUGS".
Debug logging can be enabled by setting the MOJO_TRUSTEDPROXY_DEBUG
environment variable. This plugin also adds a remote_proxy_address
attribute into Mojo::Transaction
. If a remote IP address override header is matched from a trusted upstream proxy, then tx->remote_proxy_address
will be set to the IP address of that proxy.
This allows you to load this plugin to access the helper functions without automatically running the around_dispatch
hook on each request. Default is 1
(enabled).
List of zero, one, or many HTTP headers where the real user agent IP address will be defined by the trusted upstream sources. The first matched header is used. An empty value will disable this and keep the original scheme value. Default is ['x-forwarded-for', 'x-real-ip']
.
If a header is matched in the request, then tx->remote_address
is set to the value, and tx->remote_proxy_address
is set to the IP address of the upstream source.
List of zero, one, or many HTTP headers where the real user agent connection scheme will be defined by the trusted upstream sources. The first matched header is used. An empty value will disable this and keep the original remote address value. Default is ['x-forwarded-proto', 'x-ssl']
.
This tests that the header value is "truthy" but does not contain the literal barewords http
, off
, or false
. If the header contains any other "truthy" value, then req->url->base->scheme
is set to https
.
Alias for "scheme_headers".
List of values to consider as "truthy" when evaluating the headers in "scheme_headers". Default is ['https', 'on', '1', 'true', 'enable', 'enabled']
.
Enable support for parsing RFC 7239 compliant Forwarded
HTTP headers. Default is 1
(enabled).
If a Forwarded
header is matched, the following actions occur with the first semicolon-delimited group of parameters found in the header value:
If the
for
parameter is found, thentx->remote_address
is set to the first matching value.If the
by
parameter is found, thentx->remote_proxy_address
is set to the first matching value, otherwise it is set to the IP address of the upstream source.If the
proto
parameter is found, thenreq->url->base->scheme
is set to the first matching value.If the
host
parameter is found, thenreq->url->base->host
is set to the first matching value.
Note! If enabled, the headers defined in "ip_headers" and "scheme_headers" will be overridden by any corresponding values found in the Forwarded
header.
Alias for "parse_rfc7239".
List of one or more IP addresses or CIDR classes that are trusted upstream sources. (Warning! An empty value will trust from all IPv4 sources!) Default is ['127.0.0.0/8', '10.0.0.0/8']
.
Supports all IP, CIDR, and range definition types from Net::CIDR::Lite.
Hide all headers defined in "ip_headers", "scheme_headers", and Forwarded
from the rest of the application when coming from trusted upstream sources. Default is 0
(disabled).
# From Controller context
sub get_page {
my $c = shift;
if ($c->is_trusted_source || $c->is_trusted_source('1.2.3.4')) {
...
}
}
Validate if an IP address is in the "trusted_sources" list. If no argument is provided, then this helper will first check tx->remote_proxy_address
then tx->remote_address
. Returns 1
if in the "trusted_sources" list, 0
if not, or undef
if the IP address is invalid.
# From Controller context
sub get_page {
my $c = shift;
my $matched_header = $c->process_ip_headers(1);
}
Finds the first matching header from "ip_headers" and sets tx->remote_address
to the value (if a valid IP address), sets tx->remote_proxy_address
to the IP address of the upstream proxy, and returns a hash of the name and value of the matched header. Otherwise returns 0
if no match is found.
If any "truthy" value is passed as a parameter to this helper, it will first run the "is_trusted_source" helper (no arguments passed) and will return undef
if it returns a false value.
WARNING! Calling this helper when "enabled" is set to true will likely produce unexpected results.
# From Controller context
sub get_page {
my $c = shift;
my $matched_header = $c->process_scheme_headers(1);
}
Finds the first matching header from "scheme_headers" and sets req->url->base->scheme
to https
if the header value matches any defined in "https_values", and returns a hash of the name and value of the matched header. Otherwise returns 0
if no match is found.
If any "truthy" value is passed as a parameter to this helper, it will first run the "is_trusted_source" helper (no arguments passed) and will return undef
if it returns a false value.
WARNING! Calling this helper when "enabled" is set to true will likely produce unexpected results.
Alias for "process_scheme_headers".
# From Controller context
sub get_page {
my $c = shift;
my @matched_params = $c->process_rfc7239_header(1);
}
Process an RFC 7239 ("Forwarded") HTTP header if found. See "parse_rfc7239" for more details. If the "Forwarded" header is found, this helper will return an array of hashes of the matched RFC 7239 parameters and values, or 0
if no matches are found.
If any "truthy" value is passed as a parameter to this helper, it will first run the "is_trusted_source" helper (no arguments passed) and will return undef
if it returns a false value.
WARNING! Calling this helper when "enabled" is set to true will likely produce unexpected results.
Alias for "process_rfc7239_header".
# From Controller context
sub get_page {
my $c = shift;
$c->hide_headers(1);
}
Remove all headers defined in "ip_headers", "scheme_headers", and Forwarded
from the request.
If any "truthy" value is passed as a parameter to this helper, it will first run the "is_trusted_source" helper (no arguments passed) and will return undef
if it returns a false value.
Mojolicious::Plugin::TrustedProxy is compatible with assumedly all third-party content delivery networks and cloud providers. Below is an incomplete list of some of the most well-known providers and the recommended config values to use for them.
- ip_headers
-
Set "ip_headers" to
['true-client-ip']
(unless you set this to a different value) and enable True Client IP in the origin server behavior for your site property. Akamai also supports['x-forwarded-for']
, which is enabled by default in Mojolicious::Plugin::TrustedProxy. - scheme_headers
-
There is no known way to pass this by default with Akamai. It may be possible to pass a custom header via a combination of a Site Property variable and a custom behavior that injects an outgoing request header based on that variable, but this has not been tested or confirmed.
- trusted_sources
-
This is only possible if you have the Site Shield product from Akamai. If so, set "trusted_sources" to the complete list of IPs provided in your Site Shield map.
- ip_headers
-
The AWS Elastic Load Balancer uses
['x-forwarded-for']
, which is enabled by default in Mojolicious::Plugin::TrustedProxy. - scheme_headers
-
The AWS Elastic Load Balancer uses
['x-forwarded-proto']
, which is enabled by default in Mojolicious::Plugin::TrustedProxy. - trusted_sources
-
Depending on your setup, this could be one of the
172.x.x.x
IP addresses or ranges within your Virtual Private Cloud, the IP address(es) of your Elastic or Application Load Balancer, or could be the public IP ranges for your AWS region. Go to https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html for an updated list of AWS's IPv4 and IPv6 CIDR ranges.
- ip_headers
-
Set "ip_headers" to
['cf-connecting-ip']
, or['true-client-ip']
if using an enterprise plan. Cloudflare also supports['x-forwarded-for']
, which is enabled by default in Mojolicious::Plugin::TrustedProxy. - scheme_headers
-
Cloudflare uses the
x-forwarded-proto
header, which is enabled by default in Mojolicious::Plugin::TrustedProxy. - trusted_sources
-
Go to https://www.cloudflare.com/ips/ for an updated list of Cloudflare's IPv4 and IPv6 CIDR ranges.
Caution should be taken that you set only the "CONFIG" values necessary for your application in a most-common-first order, and that your upstream proxies remove any headers you do not want passed through to your application.
For example, if you use Cloudflare and set "ip_headers" to ['x-real-ip', 'cf-connecting-ip']
and did not configure Cloudflare to remove x-real-ip
headers from requests, an attacker could use this trick your application into using whatever IP he or she defines due to being passed through your trusted proxy and the x-real-ip
header being the first to be evaluated.
Kage <kage AT kage DOT wtf>
Please report any bugs or feature requests on Github: https://github.com/Kage/Mojolicious-Plugin-TrustedProxy
- Hostnames not supported
-
Excluding the
host
parameter of RFC 7239, this plugin does not currently support hostnames or hostname resolution and there are no plans to implement this. If you have a use case that requires this, please feel free to submit a pull request. - HTTP 'Forwarded' only partially supported
-
Only partial support for RFC 7239 is currently implemented, but this should work with most common use cases. The full specification allows for complex structures and quoting that is difficult to implement safely. Full RFC support is expected to be implemented soon.
Mojolicious::Plugin::RemoteAddr, Mojolicious::Plugin::ClientIP::Pluggable, Mojolicious, Mojolicious::Guides, http://mojolicio.us.
MIT License
Copyright (c) 2019 Kage
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.