Page next
  • Apache Perl Access Module with IP check

With this Apache Perl Module it is possible to have a .htaccess authentification combined with a whitelist for IP-adresses. After a successful login the client IP is automatically whitelisted for 2 hours. This is helpful when a HTTP client (non-Browser) does not support HTTP Authentication. Then the user can login with his normal browser and after that the application can freely access the page.

The Apache Module Sourcecode:

package Apache::CachedLogin;
# file: Apache/CachedLogin.pm
use strict;
use Apache2::RequestRec();
use Apache2::Const qw(:common);
use Apache2::Log();
use Apache2::Connection;
use IPC::Shareable();
use Digest::SHA1  qw(sha1_base64);
use vars qw(%legalIPs);

# handler for access
sub handler {
        my $r = shift;
        my $ret = FORBIDDEN;

        my $ip = $r->connection->remote_ip;

        # get a list of always allowed IPs from the apache conf
        my @allowedIPs = split(/,/,$r->dir_config("allowedIPs"));
        foreach (@allowedIPs) {
                if ($ip =~ /$_/) {
                        # $r->log_error($r->uri, ": $ip is white-listed");
                        return OK;
                }
        }


        # $r->log_error($r->uri, ": remote ip: $ip");

        tie %legalIPs, 'IPC::Shareable', 'SPLM', {create => 1, mode => 0644} unless defined %legalIPs;

        tied(%legalIPs)->shlock;
        # $r->log_error($r->uri, ": time:".$legalIPs{$ip});
        if (defined $legalIPs{$ip} && (time() - $legalIPs{$ip} < 7200)) {
                # 7200 are 2 hours
                # the user has logged in or used the system from this ip in the last two hours
                $ret = OK;
                $legalIPs{$ip} = time();
                # $r->log_error($r->uri, ": legal connection");
        } else {
                delete $legalIPs{$ip} if (defined $legalIPs{$ip});
                # now we need the user to authenticate
                $r->push_handlers(PerlAuthenHandler => \&authenticate);
        }

        tied(%legalIPs)->shunlock;
        return $ret;
}

# handler for authentication
sub authenticate {
        my $r = shift;

        # $r->log_error($r->uri, ": authenticate called");

        my($res, $sent_pw) = $r->get_basic_auth_pw;
        return $res if $res != OK;

        my $user = $r->user;
        # get the configfile to use from apache conf
        my $htpwdfile = $r->dir_config("AuthUserFile");

        # $r->log_error($r->uri, ": auth with $user : $sent_pw");
        unless(checkUser($user, $sent_pw, $htpwdfile)) {
                $r->note_basic_auth_failure;
                return AUTH_REQUIRED;
        }

        tie %legalIPs, 'IPC::Shareable', 'SPLM', {create => 1, mode => 0644} unless defined %legalIPs;

        tied(%legalIPs)->shlock;
        my $ip = $r->connection->remote_ip;

        # the user made it so unlock his IP
        $legalIPs{$ip} = time();

        tied(%legalIPs)->shunlock;

        return OK;
}

sub checkUser {
        my $user = $_[0];
        my $digest = sha1_base64($_[1])."=";
        my $htpwdfile = $_[2];
        open(R,"<$htpwdfile");
        while(my $line = ) {
                chomp($line);
                my ($puser, $phash) = split(/\:\{SHA\}/, $line);
                return 1 if ($puser eq $user && $phash eq $digest);
        }
        return 0;
}

1;
__END__

A configuration example in the apache conf:

 

<VirtualHost *:80>
        ServerName test

        <Location "/test">
                AuthName "Server test"
                AuthType Basic

                # needed because otherwise apache would not get authentication from user
                Require valid-user

                Satisfy any
                PerlAccessHandler Apache::CachedLogin
                PerlSetVar AllowedIPs "10\.100\.0\.,10\.200\.1"
                PerlSetVar AuthUserFile /var/www/htpasswd
        </Location>

</VirtualHost>
Page next