Transparent Squid Authentication to eDirectory

This post explains how to setup a Squid HTTP proxy to transparently authenticate users against a Novell eDirectory. In the Novell eco-system Border Manager is the venerable choice for an internal firewall and proxy but it is showing its age. This guide is based on this Novell Cool Solution. Unlike Border Manager, which requires the CLNTRUST client-side tool, the setup described works without the need for any desktop client software.

How it works

Within a Novell managed network the eDirectory stores authenticated user's I.P. addresses. Squid performs an LDAP search against eDirectory using the incoming I.P. address of the client. If successful the authenticated username is returned and a proxy session established. If the search comes up empty Squid prompts the client to manually enter their credentials for authentication against the eDirectory. If this too fails the proxy request is denied.

eDirectory 8.8 incompatability

This solution currently only works with eDirectory < 8.8 because Novell has slightly changed the format they store network addresses in newer versions. At the time of writing I have not been able to test against eDirectory 8.8 so I cannot determine the required code changes or test results. Hopefully in the near future this situation will change.

Squid's external_acl_type option

Transparent authentication is made possible thanks to Squid's external_acl_type configuration option. This allows external identities and groups to be identified via any external script. Once Squid is installed setting up transparent eDirectory authentication is a two step process:

  1. Create and tweak the squid_edir_iplookup.pl file.
  2. Edit the squid.conf configuration file

The engine room: squid_edir_iplookup.pl

To begin create a file named squid_edir_iplookup.pl. This file can be anywhere on your system as long as the Squid process can access it. In this example I have created the file in /usr/lib/squid/ as this is where Red Hat stores all of Squid's authentication related scripts.

/usr/lib/squid/squid_edir_iplookup.pl

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

$HOST = 'your.edirectory.server';
$PORT = 389;
$ADMIN = "cn=squid,ou=tech,o=company";
$PASSWD = "squidpassword";
$BASEDN = "o=company";
@SITES = qw(ou=groups);

$|=1;

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,${site},$BASEDN)";
}


$netaddress = "1\#";
@octets = split(/\./,$IP);
foreach $octet (@octets) {
# 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 dont
# 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;
}

At the beginning of this file you want to change the $HOST, $PORT, $ADMIN, $PASSWD and $BASEDN parameters to ones that are relevant for your internal network. The @SITES array is not important as this is used in the Cool Solution example to define different groups of users (something we are not concerned about here).

Once you have saved the file make it executable:

chmod a+x /usr/lib/squid/squid_edir_iplookup.pl

The trickiest step is making sure you have the correct Perl libraries installed on your system for the script to run. These libraries are defined at the top of the file (the USE statements). The easiest way of testing whether the script is working is to run it:

/usr/lib/squid/squid_edir_iplookup.pl

The Perl interpreter will soon tell you if something is not right. If a required library is missing you will need to install it using your system's package management tool or directly from source (CPAN is useful).

All going well when the script is run you will be presented with a new, blank line. Test the LDAP lookup by typing an I.P. address and pressing enter. After a brief delay a success/fail message will be returned:

/usr/lib/squid/squid_edir_iplookup.pl
192.168.1.10
ERR

In the above example no valid user was found in the eDirectory at this I.P. address. If the look-up was successful the relevant username will be returned. To quit the script press CTRL+C.

Editing squid.conf

With the squid_edir_iplookup.pl script in place and working it is now time to edit the Squid configuration. The exact location of this file will vary depending on your operating system, but in the case of Red Hat this is:

/etc/squid/squid.conf

http_port 3128
http_port 8080
hierarchy_stoplist cgi-bin ?
acl QUERY urlpath_regex cgi-bin \?
cache deny QUERY
acl apache rep_header Server ^Apache
broken_vary_encoding allow apache
access_log /var/log/squid/access.log squid

external_acl_type IPUser ttl=3600 %SRC /usr/lib/squid/squid_edir_iplookup.pl

auth_param basic program /usr/lib/squid/squid_ldap_auth -b "ou=users,o=company" -u cn your.edirectory.server
auth_param basic children 5
auth_param basic realm Squid Web Proxy
auth_param basic credentialsttl 1 hours


# Used to pull LDAP group membership based on a supplied username - not currently used
# external_acl_type ldap_group %LOGIN /usr/lib/squid/squid_ldap_group -D cn=squid,ou=tech,o=company -w squidpassword -b o=company -s sub -f "(&(objectclass=inetOrgPerson)(cn=%u)(groupMembership=%g))" -h your.edirectory.server

refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern . 0 20% 4320

acl all src 0.0.0.0/0.0.0.0
acl manager proto cache_object
acl localhost src 127.0.0.1/255.255.255.255
acl localnet src 192.168.1.0/255.255.255.0
acl to_localhost dst 127.0.0.0/8
acl SSL_ports port 443
acl Safe_ports port 80 # http
acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
acl Safe_ports port 70 # gopher
acl Safe_ports port 210 # wais
acl Safe_ports port 1025-65535 # unregistered ports
acl Safe_ports port 280 # http-mgmt
acl Safe_ports port 488 # gss-http
acl Safe_ports port 591 # filemaker
acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT

acl edirectory_users external IPUser Everyone
acl authenticated_users proxy_auth REQUIRED localnet

http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access deny to_localhost

http_access allow edirectory_users
http_access allow authenticated_users
http_access allow localhost
# http_access allow localnet
http_access deny all

http_reply_access allow all

icp_access allow all

coredump_dir /var/spool/squid

debug_options ALL,1
# For debugging ACLs
# debug_options ALL,1 33,2 28,9

You will need to change the relevant parts in this file to suit your eDirectory and internal network settings (i.e. the localnet setting).

The Squid configuration file is a tricky beast, but what is listed above is fairly standard. The first highlighted section is where the external squid_edir_iplookup.pl script is referenced. If this check fails an LDAP-backed HTTP BASIC authentication request is made.

The second highlighted area identifies two Access Control Lists, the first based on the result of the eDirectory I.P. lookup and the second on the HTTP BASIC authentication response. If the incoming request is from someone authenticated within the eDirectory OR by the HTTP BASIC process then outbound access is granted.

It is possible to use LDAP group information to form ACLs that further limit Web access. The above example does not utilise this functionality, but the Novell Cool Solution goes into it in further detail. My advice is get the Squid/eDirectory authentication working at a very basic level and then make it more complicated. Starting at the most complicated scenario will only lead to failure and frustration.

If you are encountering authentication issues then enable the debugging options at the bottom of the configuration file. This is fairly verbose but can be very useful in identifying configuration mistakes.

Reviewing HTTP access logs with Webmin & SARG

SARG (Squid Analysis Report Generator) is a handy tool for reviewing Squid access files. There is a very good Webmin module for this application, so rather than struggling with the command line install Webmin on the proxy. By the way, while you are at it install my Webmin theme, your eyes will thank you for it.