133 lines
3.7 KiB
Perl
133 lines
3.7 KiB
Perl
package Koha::Middleware::RealIP;
|
|
|
|
# Copyright 2019 ByWater Solutions and the Koha Dev Team
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Koha; if not, see <https://www.gnu.org/licenses>.
|
|
|
|
use Modern::Perl;
|
|
|
|
use parent qw(Plack::Middleware);
|
|
|
|
use C4::Context;
|
|
|
|
use Net::Netmask;
|
|
use Plack::Util::Accessor qw( trusted_proxy );
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 prepare_app
|
|
|
|
This method generates and stores the list of trusted ip's as Netmask objects
|
|
at the time Plack starts up, obviating the need to regerenate them on each request.
|
|
|
|
=cut
|
|
|
|
sub prepare_app {
|
|
my $self = shift;
|
|
$self->trusted_proxy( get_trusted_proxies() );
|
|
}
|
|
|
|
=head2 call
|
|
|
|
This method is called for each request, and will ensure the correct remote address
|
|
is set in the REMOTE_ADDR environment variable.
|
|
|
|
=cut
|
|
|
|
sub call {
|
|
my $self = shift;
|
|
my $env = shift;
|
|
|
|
# If no reverse_proxy_ip_header, just use HTTP_X_FORWARDED_FOR as the header to check, simplifies logic and is a bit faster that way
|
|
my $reverse_proxy_ip_header = C4::Context->config('reverse_proxy_ip_header') || 'HTTP_X_FORWARDED_FOR';
|
|
|
|
# Check the env for the custom header, if the custom header is not set, fall back to HTTP_X_FORWARDED_FOR
|
|
my $header = $env->{$reverse_proxy_ip_header} || $env->{'HTTP_X_FORWARDED_FOR'};
|
|
if ($header) {
|
|
my $addr = get_real_ip( $env->{REMOTE_ADDR}, $header );
|
|
$ENV{REMOTE_ADDR} = $addr;
|
|
$env->{REMOTE_ADDR} = $addr;
|
|
}
|
|
|
|
return $self->app->($env);
|
|
}
|
|
|
|
=head2 get_real_ip
|
|
|
|
my $address = get_real_ip( $remote_addres, $x_forwarded_for_header );
|
|
|
|
This method takes the current remote address and the x-forwarded-for header string,
|
|
determines the correct external ip address, and returns it.
|
|
|
|
=cut
|
|
|
|
sub get_real_ip {
|
|
my ( $remote_addr, $header ) = @_;
|
|
|
|
my $enable_trusted_proxy_for_headers = C4::Context->config('enable_trusted_proxy_for_headers') // 1;
|
|
return $remote_addr unless $enable_trusted_proxy_for_headers;
|
|
|
|
my @forwarded_for = $header =~ /([^,\s]+)/g;
|
|
return $remote_addr unless @forwarded_for;
|
|
|
|
my $trusted_proxies = get_trusted_proxies();
|
|
|
|
#X-Forwarded-For: <client>, <proxy1>, <proxy2>
|
|
my $real_ip = shift @forwarded_for;
|
|
my @unconfirmed = ( @forwarded_for, $remote_addr );
|
|
|
|
while ( my $addr = pop @unconfirmed ) {
|
|
my $has_matched = 0;
|
|
foreach my $netmask (@$trusted_proxies) {
|
|
$has_matched++, last if $netmask->match($addr);
|
|
}
|
|
$real_ip = $addr, last unless $has_matched;
|
|
}
|
|
|
|
return $real_ip;
|
|
}
|
|
|
|
=head2 get_trusted_proxies
|
|
|
|
This method returns an arrayref of Net::Netmask objects for all
|
|
the trusted proxies given to Koha.
|
|
|
|
=cut
|
|
|
|
sub get_trusted_proxies {
|
|
my $proxies_conf = C4::Context->config('koha_trusted_proxies');
|
|
return unless $proxies_conf;
|
|
my @trusted_proxies_ip = split( / /, $proxies_conf );
|
|
my @trusted_proxies = ();
|
|
foreach my $ip (@trusted_proxies_ip) {
|
|
my $mask = Net::Netmask->new2($ip);
|
|
if ($mask) {
|
|
push( @trusted_proxies, $mask );
|
|
} else {
|
|
warn "$Net::Netmask::error";
|
|
}
|
|
}
|
|
return \@trusted_proxies;
|
|
}
|
|
|
|
=head1 AUTHORS
|
|
|
|
Kyle M Hall <kyle@bywatersolutions.com>
|
|
|
|
=cut
|
|
|
|
1;
|