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 . 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 ( $env->{HTTP_X_FORWARDED_FOR} ) { my @trusted_proxy = $self->trusted_proxy ? @{ $self->trusted_proxy } : undef; if (@trusted_proxy) { my $addr = get_real_ip( $env->{REMOTE_ADDR}, $env->{HTTP_X_FORWARDED_FOR}, \@trusted_proxy ); $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 @forwarded_for = $header =~ /([^,\s]+)/g; return $remote_addr unless @forwarded_for; my $trusted_proxies = get_trusted_proxies(); #X-Forwarded-For: , , 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 =cut 1;