Novell Home

Seamless Authentication with the Squid Proxy

Novell Cool Solutions: Feature
By Simon Tideswell

Digg This - Slashdot This

Posted: 4 Oct 2006
 

Problem

We used to use BorderManager proxy with the handy Client Trust key that allowed the user to authenticate to the proxy seamlessly without any need for user intervention (e.g., being prompted to enter a password). I wanted to achieve the same thing with the Squid proxy.

Solution

This solution uses two authentication schemes. The first method uses the IP address of the client that is requesting a site from the proxy. If a user has logged into eDirectory using the Novell client, the Network Address attribute of the user object in eDirectory should be populated with the user's IP address. A Perl script does an LDAP search for the user that "owns" that IP address and checks for membership of one of the groups that grant Internet access (Full_Internet_Access and Restricted_Internet_Access).

This works very well with maybe a two-second delay, when the user first accesses the web while the LDAP search is performed. To reduce the impact of the small delay on the user and to reduce load on the LDAP server, the time-to-live of the authentication information is set to two hours. This means a user could log out after first using the web and that IP address would continue to be granted Internet access for a further two hours (regardless of who is logged on). Consequently, in sites with very stringent Internet policies or with users that move from workstation to workstation frequently during the day, this might not be an appropriate solution. Of course, the TTL can be adjusted to fit to company needs, too.

Second Method: In the event that the IP address attribute of the user is not populated (which happens very rarely and is always the result of a broken Novell client), the Squid proxy will fall back to standard username/password authentication. This also uses eDirectory and the same group membership as the first method.

Note: This solution should work with any version of Linux, but as we are a Novell customer our proxies are running on SLES9.

WAN Site Example

For the purposes of this discussion let's assume that I have 3 WAN sites in Brisbane, Melbourne and Sydney, and that my eDirectory tree is partitioned by WAN site. My top level tree design looks like this:

o=NTL---ou=Brisbane
     |--ou=Melbourne
     |--ou=Sydney

Under each site's main ou there is an ou for Groups and two Internet access groups (e.g., cn=Full_Internet_Access,ou=Groups,ou=Brisbane,o=NTL and cn=Restricted_Internet_Access,ou=Groups,ou=Brisbane,o=NTL). This is important when understanding the script and Squid configuration details. Assume that the IP address of the LDAP host is 192.168.2.45.

Edit the file /etc/squid/squid.conf and add the following to allow it to use this authentication method (all other configuration details are also added to /etc/squid/squid.conf) -

external_acl_type IPUser ttl=7200 %SRC /usr/sbin/squid_ip_user.pl

I have set a long time-to-live of two hours (7200 seconds) to avoid unnecessary LDAP traffic. The script, modified to suit your own site, must be copied to /usr/sbin.

Occasionally due to workstation problem the IP address attribute will not be populated when the user logs into eDirectory so a fail-safe username/password authentication scheme is also used.

Basic Authentication Program

auth_param basic program /usr/sbin/squid_ldap_auth -Z -D cn=squid_ldap_user,o=ntl -w password -b o=ntl -s sub -f "(&(objectclass=User)(cn=%s))" -h 192.168.2.45

external_acl_type ldap_group %LOGIN /usr/sbin/squid_ldap_group -Z -D cn=squid_ldap_user,o=ntl -w password -b o=ntl -s sub -f "(&(objectclass=User)(cn=%u)(groupMembership=%g))" -h 192.168.2.45

# Hosts that are not required to authenticate
acl Auth_Not_Required src "/etc/squid/auth_not_required.txt"

# Domains accessible to all PC's
acl Open_Domains dstdomain "/etc/squid/open_domains.txt"

# Hosts & domains that are denied to restricted users
acl Banned_Hosts dst "/etc/squid/banned_hosts.txt"
acl Banned_Domains dstdomain "/etc/squid/banned_domains.txt"
acl Banned_URLs url_regex "/etc/squid/banned_urls.txt"
acl Banned_Extensions url_regex "/etc/squid/banned_extensions.txt"

# Seemless automatic access based on IP address
# Access through the "IP User" external helper
acl Full_Access external IPUser Full_Internet_Access
acl Restricted_Access external IPUser Restricted_Internet_Access

# Access to users prompted with username/password dialogue
# Full access
acl Melbourne_Full external ldap_group cn=Full_Internet_Access,ou=Groups,ou=Melbourne,o=NTL
acl Sydney_Full external ldap_group cn=Full_Internet_Access,ou=Groups,ou=Sydney,o=NTL
acl Brisbane_Full external ldap_group cn=Full_Internet_Access,ou=Groups,ou=Brisbane,o=NTL

# Restricted access
acl Melbourne_ Restricted external ldap_group cn= Restricted _Internet_Access,ou=Groups,ou=Melbourne,o=NTL
acl Sydney_ Restricted external ldap_group cn= Restricted _Internet_Access,ou=Groups,ou=Sydney,o=NTL
acl Brisbane_Full external ldap_group 

In amongst your normal Squid HTTP_ACCESS lines add the following:

# Domains free to all users without needing to authenticate.
http_access allow Open_Domains
# IP addresses of hosts that don?t need to authenticate (usually automation hosts #performing automated downloads and without a Novell client.
http_access allow Auth_Not_Required
# Let users with full Internet access discovered by the IPUser method go anywhere.
http_access allow Full_Access
# Let users with restricted Internet access discovered by the IPUser method go #anywhere except for the ban list
http_access allow Restricted_Access !Banned_Hosts !Banned_Domains !Banned_URLs !Banned_Extensions
# If the IPUser method fails then we need to revert to username/password #authentication
# Let users with full access who entered username/password go anywhere
http_access allow Brisbane_Full
http_access allow Melbourne_Full
http_access allow Sydney_Full
# Ban list again
http_access deny Banned_Hosts
http_access deny Banned_Domains
http_access deny Banned_URLs
http_access deny Banned_Extensions
# Let users with restricted access who entered username/password go anywhere (that # hasn?t been already excluded in the ban list).
http_access allow Brisbane_Restricted
http_access allow Melbourne_Restricted
http_access allow Sydney_Restricted
# Add other stuff below

squid_ip_user.pl File

The contents of the squid_ip_user.pl file are shown below. The script makes an LDAP bind as user cn=squid_ldap_user,o=ntl (password = password) that you must create and give appropriate rights in eDirectory (to read all user?s group membership, CN and Network Address attributes). The script requires perl and the perl-ldap and perl-ldap-ssl modules to be installed.

#!/usr/bin/perl
use Net::LDAP;
use Net::LDAP::LDIF;
use File::Path qw(rmtree);
use File::Basename qw(basename);

$HOST = '192.168.2.45;
$PORT = 389;
$|=1;
@SITES = qw(Brisbane Melbourne Sydney);

START: while (<>) {

  ($IP,$GROUP) = split(/ /,$_);
#  $SITE =~ tr/\n//d;
  $GROUP =~ tr/\n//d;
  $group_filter_string="";
  for $site (@SITES) {
    $group_filter_string=$group_filter_string."(groupMembership=cn=$GROUP,ou=Groups,ou=${site},o=NTL)";
  }

  $PASSWD = "password";
  $BASEDN = "o=ntl";
  $ADMIN = "cn=squid_ldap_user,o=ntl";

  $netaddress = "1\#";
  @octets = split(/\./,$IP);
  foreach $octet (@octets) {
#      if (($octet >= 40) && ($octet <= 42)) {
# The IP address is stored in eDirectory as four unsigned chars. ASCII 40, 41, 42 and # 92 are characters ( ) *\ which are known tokens in LDAP search filters If you don?t # escape these with a backslash they will cause LDAP errors and he script will fail.
      if ((($octet >= 40) && ($octet <= 42)) || ($octet == 92)) {
           $netaddress = $netaddress.sprintf("\\%c",$octet)
      } else {
           $netaddress= $netaddress.sprintf("%c",$octet);
      }
  }
  $filter="(&(objectclass=user)(|$group_filter_string)\
           (networkAddress=$netaddress))";
  $attnames=['CN'];

#connect to the server
  until($ldap = Net::LDAP->new($HOST, port => $PORT)) {
    die "Can not connect to ldap://$HOST:$PORT/" if ++$count > 10;
    sleep 1;
  }

  $r = $ldap->start_tls();

  $r = $ldap->bind($ADMIN, password => $PASSWD, version=>2);
  die $r->error if $r->code;

  $r = $ldap->search(base => $BASEDN,
                     scope => 'sub',
                     filter => $filter,
                     attrs => $attnames);

  $count = $r->count;
  if ($count == 0) {
      print "ERR\n";
  } else {
      foreach my $entry ($r->entries){
              my @values = $entry->get_value(CN);
              foreach $value (@values) {
# Many users in eDirectory have multiple CN values - usually from the user template # used to create them - sometimes their maiden name is noted in the Other Name
# attribute in ConsoleOne we want to report the proper CN to squid not these bogus
# values.
                      if ($value =~ m/template|previously/i) {
                         next;
                      } else {
                         $value =~ tr/- //d;
                         print "OK user=$value\n";
                         next START;
                      }
              }
      }
  }
$ldap->unbind;
}

The perl script can be tested independently of squid by running it from the command line. The script loops indefinitely and waits for two arguments (IP address and the name of the group you are testing). If the IP is owned by a user who is in that group then the script will return "OK" and the username otherwise it will return "ERR". The username returned by the script is used by squid to populate the access logs (for your reporting solution - sarg, squint, webalizer).

px01:~ # /usr/sbin/squid_ip_user.pl
10.20.4.15 Full_Internet_Access
ERR
10.20.4.15 Restricted_Internet_Access
OK user=Stideswe
10.20.4.11 Full_Internet_Access
OK user=Dzivic
10.20.1.190 Full_Internet_Access
ERR
10.20.1.190 Restricted_Internet_Access
ERR


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell