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 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: , , 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;