summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Config/Advanced.pm13
-rw-r--r--Bugzilla/Config/Common.pm11
-rw-r--r--Bugzilla/Util.pm35
-rw-r--r--t/013remote_ip.t81
-rw-r--r--template/en/default/admin/params/advanced.html.tmpl4
5 files changed, 112 insertions, 32 deletions
diff --git a/Bugzilla/Config/Advanced.pm b/Bugzilla/Config/Advanced.pm
index 4b57df24d..893213b02 100644
--- a/Bugzilla/Config/Advanced.pm
+++ b/Bugzilla/Config/Advanced.pm
@@ -47,7 +47,7 @@ use constant get_param_list => (
name => 'inbound_proxies',
type => 't',
default => '',
- checker => \&check_ip
+ checker => \&check_inbound_proxies
},
{
@@ -108,4 +108,15 @@ use constant get_param_list => (
},
);
+sub check_inbound_proxies {
+ my $inbound_proxies = shift;
+
+ return "" if $inbound_proxies eq "*";
+ my @proxies = split(/[\s,]+/, $inbound_proxies);
+ foreach my $proxy (@proxies) {
+ validate_ip($proxy) || return "$proxy is not a valid IPv4 or IPv6 address";
+ }
+ return "";
+}
+
1;
diff --git a/Bugzilla/Config/Common.pm b/Bugzilla/Config/Common.pm
index 35a70994b..4c9994e0e 100644
--- a/Bugzilla/Config/Common.pm
+++ b/Bugzilla/Config/Common.pm
@@ -48,7 +48,7 @@ use base qw(Exporter);
qw(check_multi check_numeric check_regexp check_url check_group
check_sslbase check_priority check_severity check_platform
check_opsys check_shadowdb check_urlbase check_webdotbase
- check_user_verify_class check_ip
+ check_user_verify_class
check_mail_delivery_method check_notification check_utf8
check_bug_status check_smtp_auth check_theschwartz_available
check_maxattachmentsize check_email
@@ -130,15 +130,6 @@ sub check_sslbase {
return "";
}
-sub check_ip {
- my $inbound_proxies = shift;
- my @proxies = split(/[\s,]+/, $inbound_proxies);
- foreach my $proxy (@proxies) {
- validate_ip($proxy) || return "$proxy is not a valid IPv4 or IPv6 address";
- }
- return "";
-}
-
sub check_utf8 {
my $utf8 = shift;
# You cannot turn off the UTF-8 parameter if you've already converted
diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm
index d80ab9569..f793ed727 100644
--- a/Bugzilla/Util.pm
+++ b/Bugzilla/Util.pm
@@ -57,7 +57,7 @@ use DateTime;
use DateTime::TimeZone;
use Digest;
use Email::Address;
-use List::Util qw(first);
+use List::MoreUtils qw(none);
use Scalar::Util qw(tainted blessed);
use Text::Wrap;
use Encode qw(encode decode resolve_alias);
@@ -305,28 +305,23 @@ sub correct_urlbase {
}
}
+# Returns the real remote address of the client,
sub remote_ip {
- my $ip = $ENV{'REMOTE_ADDR'} || '127.0.0.1';
- my @proxies = split(/[\s,]+/, Bugzilla->params->{'inbound_proxies'});
-
- # If the IP address is one of our trusted proxies, then we look at
- # the X-Forwarded-For header to determine the real remote IP address.
- if ($ENV{'HTTP_X_FORWARDED_FOR'} && first { $_ eq $ip } @proxies) {
- my @ips = split(/[\s,]+/, $ENV{'HTTP_X_FORWARDED_FOR'});
- # This header can contain several IP addresses. We want the
- # IP address of the machine which connected to our proxies as
- # all other IP addresses may be fake or internal ones.
- # Note that this may block a whole external proxy, but we have
- # no way to determine if this proxy is malicious or trustable.
- foreach my $remote_ip (reverse @ips) {
- if (!first { $_ eq $remote_ip } @proxies) {
- # Keep the original IP address if the remote IP is invalid.
- $ip = validate_ip($remote_ip) || $ip;
- last;
- }
+ my $remote_ip = $ENV{'REMOTE_ADDR'} || '127.0.0.1';
+ my @proxies = split(/[\s,]+/, Bugzilla->params->{inbound_proxies});
+ my @x_forwarded_for = split(/[\s,]+/, $ENV{HTTP_X_FORWARDED_FOR} // '');
+
+ return $remote_ip unless @x_forwarded_for;
+ return $x_forwarded_for[0] if $proxies[0] eq '*';
+ return $remote_ip if none { $_ eq $remote_ip } @proxies;
+
+ foreach my $ip (reverse @x_forwarded_for) {
+ if (none { $_ eq $ip } @proxies) {
+ # Keep the original IP address if the remote IP is invalid.
+ return validate_ip($ip) || $remote_ip;
}
}
- return $ip;
+ return $remote_ip;
}
sub validate_ip {
diff --git a/t/013remote_ip.t b/t/013remote_ip.t
new file mode 100644
index 000000000..1cc832a9b
--- /dev/null
+++ b/t/013remote_ip.t
@@ -0,0 +1,81 @@
+#!/usr/bin/perl
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+use strict;
+use lib qw(. lib t);
+use Test::More qw(no_plan);
+use Bugzilla;
+use Bugzilla::Util qw(remote_ip);
+
+my $params = Bugzilla->params;
+
+{
+ local $params->{inbound_proxies} = '10.0.0.1,10.0.0.2';
+ local $ENV{REMOTE_ADDR} = '10.0.0.2';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '10.42.42.42';
+
+ is(remote_ip(), '10.42.42.42', "from proxy 2");
+}
+
+{
+ local $params->{inbound_proxies} = '10.0.0.1,10.0.0.2';
+ local $ENV{REMOTE_ADDR} = '10.0.0.1';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '10.42.42.42';
+
+ is(remote_ip(), '10.42.42.42', "from proxy 1");
+}
+
+{
+ local $params->{inbound_proxies} = '10.0.0.1,10.0.0.2';
+ local $ENV{REMOTE_ADDR} = '10.0.0.3';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '10.42.42.42';
+
+ is(remote_ip(), '10.0.0.3', "not a proxy");
+}
+
+{
+ local $params->{inbound_proxies} = '*';
+ local $ENV{REMOTE_ADDR} = '10.0.0.3';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '10.42.42.42,1.4.9.2';
+
+ is(remote_ip(), '10.42.42.42', "always proxy");
+}
+
+{
+ local $params->{inbound_proxies} = '';
+ local $ENV{REMOTE_ADDR} = '10.9.8.7';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '10.42.42.42,1.4.9.2';
+
+ is(remote_ip(), '10.9.8.7', "never proxy");
+}
+
+
+{
+ local $params->{inbound_proxies} = '10.0.0.1,2600:cafe::cafe:ffff:bf42:4998';
+ local $ENV{REMOTE_ADDR} = '2600:cafe::cafe:ffff:bf42:4998';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '2600:cafe::cafe:ffff:bf42:BEEF';
+
+ is(remote_ip(), '2600:cafe::cafe:ffff:bf42:BEEF', "from proxy ipv6");
+}
+
+{
+ local $params->{inbound_proxies} = '10.0.0.1,2600:cafe::cafe:ffff:bf42:4998';
+ local $ENV{REMOTE_ADDR} = '2600:cafe::cafe:ffff:bf42:DEAD';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '2600:cafe::cafe:ffff:bf42:BEEF';
+
+ is(remote_ip(), '2600:cafe::cafe:ffff:bf42:DEAD', "invalid proxy ipv6");
+}
+
+
+{
+ local $params->{inbound_proxies} = '*';
+ local $ENV{REMOTE_ADDR} = '2600:cafe::cafe:ffff:bf42:DEAD';
+ local $ENV{HTTP_X_FORWARDED_FOR} = '';
+
+ is(remote_ip(), '2600:cafe::cafe:ffff:bf42:DEAD', "always proxy ipv6");
+}
diff --git a/template/en/default/admin/params/advanced.html.tmpl b/template/en/default/admin/params/advanced.html.tmpl
index a2103c652..6cd13a49d 100644
--- a/template/en/default/admin/params/advanced.html.tmpl
+++ b/template/en/default/admin/params/advanced.html.tmpl
@@ -67,7 +67,9 @@
_ " user is the IP address of the proxy. If you enter a comma-separated"
_ " list of IPs in this parameter, then $terms.Bugzilla will trust any"
_ " <code>X-Forwarded-For</code> header sent from those IPs,"
- _ " and use the value of that header as the end user's IP address.",
+ _ " and use the value of that header as the end user's IP address."
+ _ " If set to a *, $terms.Bugzilla will trust the first value in the "
+ _ " X-Forwarded-For header.",
proxy_url =>
"$terms.Bugzilla may have to access the web to get notifications about"