Back to Home

Automatically import user names from Active Directory into Lightsquid

squid · lightsquid · system administration · proxy server

Automatically import user names from Active Directory into Lightsquid

Many people who manage the Squid proxy server in the enterprise have a need to periodically show the statistics of Internet use by employees to the management. To provide statistics, in addition to Squid, a log analyzer such as SARG, Lightsquid, etc. is installed. At the same time, the organization often has a directory service (it is assumed that this is Active Directory), in which all employees have accounts and authorization in the proxy server is based on the accounts. Naturally, for management, when it looks at the report, it is more convenient to identify the employee by name and surname. Surprisingly, in many forums and IT portals, it is proposed to solve this problem manually by hammering names in the configuration files of the log analyzer.
This article describes a method for automatically retrieving employee last name and first name data from ActiveDirectory and inserting it into Lightsquid reports.

Objective: to provide output of reports through Lightsquid for each Active Directory user who visited the Internet, indicating his name and surname (looking ahead, I will say that in AD this field corresponds to the "displayed name", in LDAP-queries the variable displayName refers to it )


Initial data:

  • -Customized and correctly working Squid 3.3.8 with ntlm authorization
  • -the following lines are present in the Squid config:
    	logfile_rotate N
    	debug_options rotate=M
    	

    where N and M are the maximum number of log files that Squid can create
    logfile_rotate for access.log, debug_options for cache.log
  • -Configured and working correctly Apache 2.4.7
  • -Configured and working Lightsquid 1.8 () correctly
  • -In the Lightsquid config there is a variable $ lang = "ru"


Decision:

First, I will describe the mechanism for transferring name data to Lightsquid from ActiveDirectory, then I will give its implementation.

Information about the full name is presented in AD in the properties of the domain user, in the "Display name" field. To obtain information from AD, you must interact with it through LDAP queries. At the same time, you can interact only on behalf of an authorized domain user. Because Lightsquid is written in Perl, the Net :: LDAP module is required to complete these queries. And for automatic output of information from AD in the report, it is necessary to replace the simple receipt of the login from squid by the execution of the LDAP request.

First, you need to create an account in AD with the most restricted rights, which will be used to perform LDAP queries. To do this, launch the Active Directory Users and Computers snap-in and create a new user. Give it a name that expresses its purpose. For example, LightSquidAgent. Then create a new GPO and enter its properties (or the properties of an existing object). Next Computer Configuration-> Windows Configuration-> Security Settings-> Local Policies-> Assign User Rights. In the "Deny access to the computer from the network" option, enter LightSquidAgent. In the Reject Local Login option, also enter LightSquidAgent.

Now install the Net :: LDAP module in Perl. Run bash or a similar command shell and execute
perl -MCPAN -e shell
. After entering the cpan interpreter, execute
install Net::LDAP
. Next, the installer will ask if we want to allow it to perform auto-configuration. Just hit Enter. At the end you should see
LDAP module was installed successfully
.

After that, you can edit the code that generates reports. We go into the folder with LightSquid installed, go to the ip2name folder and open the ip2name.squidauth file. It should look like this:
#contributor: esl
#specialy for squid with turned on user authentication
#simple version
sub StartIp2Name() {
}
sub Ip2Name($$$) {
  # $Lhost,$user,$Ltimestamp
  my $Lhost=shift;
  my $user =shift;
  $user    =URLDecode($user); #decode user name
  return $user if ($user ne "-");
  return $Lhost;
}
sub StopIp2Name() {
}
#warning !!!
1;

UPD: The recommendations of the author of Lightsquid regarding caching user names have been taken into account.

UPD2: Fixed bug with script freezing when recognizing unauthorized users

UPD3: Fixed bug with script freezing when the domain controller is unavailable and when it is not possible to log in under the LightSquidAgent account

In the header of the file, you need to register namespaces in which the functions we need lie and declare 3 new variables:
#contributor: esl
#specialy for squid with turned on user authentication
#simple version
use strict;
use warnings;
use Net::LDAP;
use Encode;
my $ldap;
my $message;
my %hDisplayName;


We replace the empty StartIp2Name definition with ours, in which the connection to the domain controller is established

sub StartIp2Name() {
my $server = "ldap://ourserver.domain.com";
$ldap = Net::LDAP->new( $server );
return if(!defined $ldap);
$message = $ldap->bind(q(domain\LightSquidAgent), password => "passwd"); 
}


We replace the definition of the Ip2Name function, our version of the function will be taken from the domain controller by the name of the employees

In the branch of the conditional statement, skipping of repeated logins is provided, for each user only one LDAP request will be made.

Instead
sub Ip2Name($$$) {
  # $Lhost,$user,$Ltimestamp
  my $Lhost=shift;
  my $user =shift;
  $user    =URLDecode($user); #decode user name
  return $user if ($user ne "-");
  return $Lhost;
}

insert
sub Ip2Name($$$) {
  # $Lhost,$user,$Ltimestamp
  my $Lhost=shift;
  my $user =shift;
  $user    =URLDecode($user); #decode user name  
  return $Lhost if ($user eq "-");
  return $user if (!defined $ldap);
  return $user if ($message->code());
  if (!defined $hDisplayName{$user})
  {
    my $result = $ldap->search(
	base	=> "dc=domain,dc=com",
	filter	=> "(&(objectCategory=person)(objectClass=user)(sAMAccountName=" . $user . "))",
    );
my $first_entry = $result->entry(0);
if (!defined $first_entry)
  {
    return $Lhost;
  }
my $pure_displayName = $first_entry->get_value("displayName");
$pure_displayName =~ s/ /_/g;
Encode::from_to($pure_displayName, 'utf-8', 'windows-1251');
  $hDisplayName{$user}=$pure_displayName;
  }
  return $hDisplayName{$user};
}


Last: In the StopIp2Name function, disconnect from the domain controller

sub StopIp2Name() {
return if (!defined $ldap);
$message = $ldap->unbind;
}


Objective criticism is welcome

Read Next