[Date Prev][Date Next] [Chronological] [Thread] [Top]

Benchmarks of LDAP for Authentication



Benchmarks of LDAP for Authentication

Disclaimer

These are the results of a quick set of benchmarks cooked up for a
feasibility test.  Your Mileage May Vary.

Author
Karl O. Pinc <kop@meme.com>
(aka <kpinc@artic.edu>)

The intention is to include this in an appendix of a distributed
authentication howto.


Software:
Linux 2.2.12.-6.2smp  (A VALinux kernel)
Redhat 6.0
nss_ldap 105 (rpm release 1)
openldap 1.2.9 (rpm release 5)

Hardware:
VALinux system Varstation MP (?)
dual cpu, 500Mhz PIII (999.42 combined BogoMIPS)
256MB ram
raw I/O seems about 10MB/sec
filesystem I/O seems about 3.5MB/sec (maybe 4MB/sec?)


Summary

The intention is to compare authentication performance using
/etc/passwd with authentication from a ldap based database containing
the same usernames and passwords.

4 test runs were made.  Each test run was made with 6221 users in the
password database.  A random 1000 users were chosen and then su-ed to.
The results were timed using the GNU "time" program.

The box was unloaded except for the benchmark.

Conclusions based on elapsed time, which include ldap server overhead,
show that ldap uses about 22% of the system resources used by pwdb.

Results show that, WHEN NOT INCLUDING LDAP SERVER OVERHEAD,
authentication using usernames and passwords stored in ldap uses about
10% of the cpu used by pwdb authenticating from /etc/passwd and
/etc/shadow. But ldap places about 20% _more_ load on on the memory
subsystem.


My comments on the results:

The memory load placed on the system by ldap may be able to be reduced
by tweaking the ldap server configuration file.

Note that the results reported below for the ldap tests don't include
the load placed on the system by the ldap server!

I expect the ldap performance to be O(log(n)) of the number of
passwords, and pwdb performance to be O(n), so the more users you have
the more you benefit from ldap.

I haven't looked into it, but I imagine there's a way to store
passwords in a database, which would achieve O(log(n)) performance
without ldap.  (Perhaps using pam_unix.so.)

pam_unix.so is supposed to be significantly faster than pam_pwdb.so,
I've other reasons for wanting to use pam_pwdb.so.

LDAP performance seemed disk bound (based on the size of the wavelets
in a coffee cup placed on the server :-) so expect the speed of your
I/O subsystem to significantly effect results.  (Also, disk bound
performace is indicated as there was nothing else running on the
server and the cpu was only at about 45% utilization.)  An unknown
amount of this disk activity is due to pwdb logging to syslog.  In
particular, each su results in 2 extra log records, "check pass; user
unknown" and "acct; pwdb: requested item was not found (<username>
from uid=xxx)".  It may be possible to use the "new style" control
flags in linux-pam to eliminate these messages.  Certainly the code
could be easily modified if that's what's needed.

Removing the groups from the ldap database would make it a lot smaller
and might speed things up.  I wanted a test that would reflect
something of how the system would work when groups were served up with
ldap.

With a separate client and server for ldap the network overhead would
be somewhat higher, as the loopback interface wouldn't be used.


Description of the tests:

In no case were nis or nisplus servers available or clients configured.


Test runs 1 and 2 (identical runs):

Authentication done using pam_ldap.so, the ldap authentication module
for pam.  Ldap server running on the same box as the benchmark
program.

The ldap database also contained shadow password information (in an
entry combined with the regular password database.)

The ldap database contained the group information, including one group
for each user, but the system was not configured reference the groups
in ldap in any way.

Notice that nsswitch.conf file is configured to get groups from files,
nisplus and nis, so perhaps there's additional overhead here.

Test run 3:

Authentication done using the default RedHat mechanism, /etc/passwd
and /etc/shadow.  The /etc/group file was _not_ loaded with the
corresponding groups.  (As test runs 1 and 2 also were driven off
/etc/groups without the corresponding groups loaded.)
/etc/nsswitch.conf file was configured as shipped (to check files,
nisplus, and nis, in that order.)

Test run 4:

Like test run 3 in all respects except that /etc/nsswitch.conf was
configured with password set only to check /etc/passwd.


Test results:

Test run 1:

46.80user 37.57system 3:10.35elapsed 44%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (1171457major+594760minor)pagefaults 0swaps

Test run 2:

48.07user 38.78system 3:10.48elapsed 45%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (1171457major+594090minor)pagefaults 0swaps

Test run 3:

759.48user 51.74system 14:51.07elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (1025458major+563650minor)pagefaults 0swaps

Test run 4:

737.85user 48.87system 14:23.06elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (924458major+535882minor)pagefaults 0swaps


Configuration files:

------------------------------<snip>----------------------------
# /etc/nsswitch.conf (fragment, test runs 1 and 2)
passwd:   files ldap
group:    files nisplus nis

------------------------------<snip>----------------------------
# /etc/nsswitch.conf (fragment, test run 3)
passwd:   files nisplus nis
group:    files nisplus nis

------------------------------<snip>----------------------------
# /etc/nsswitch.conf (fragment, test run 4)
passwd:   files 
group:    files

------------------------------<snip>----------------------------
# /etc/openldap/slapd.conf
#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
include		/etc/openldap/slapd.at.conf
include		/etc/openldap/slapd.oc.conf
schemacheck	off

loglevel 0

# Maximum number of records to return
# (very large arbitrary number, 0 doesn't work)
sizelimit 1000000

pidfile		/var/run/slapd.pid
argsfile	/var/run/slapd.args

#
# Access control
#
# The levels, which are cumulative, from less to more:
# none compare search read write delete

defaultaccess write

#######################################################################
# ldbm database definitions
#######################################################################

# More general suffixes _must_ come after the more specific suffixes!
database ldbm
suffix		"dc=school, dc=edu"
# The directory you choose must exist.
directory	/var/tmp/ldap/
rootdn          "cn=Manager, dc=school, dc=edu"
rootpw		something

# Indexes to speed things up.
# If objectclass isn't last, it breaks things.
index {dn,uid,memberUid,gidNumber,uidNumber,objectClass} eq

# Make sure there's enough room for the indexes.
dbcachesize 500000
cachesize 10000

------------------------------<snip>----------------------------
# /etc/ldap.conf
#
# This is the configuration file for the LDAP nameservice
# switch library and the LDAP PAM module.
#
# To contact the developers, mail support@padl.com.
#

# If the host and base aren't here, then the DNS RR
# _ldap._tcp.<defaultdomain>. will be resolved. <defaultdomain>
# will be mapped to a distinguished name and the target host
# will be used as the server.

# Your LDAP server. Must be resolvable without using LDAP.
host 127.0.0.1

# The distinguished name of the search base.
base dc=school,dc=edu

# The LDAP version to use (defaults to 2)
#ldap_version 3

# The distinguished name to bind to the server with.
# Optional: default is to bind anonymously.
#binddn cn=manager,dc=padl,dc=com

# The credentials to bind with. 
# Optional: default is no credential.
#bindpw secret

# The port.
# Optional: default is 389.
#port 389

# The search scope.
#scope sub
#scope one
#scope base

# The following options are specific to nss_ldap.

# The hashing algorith your libc uses. 
# Optional: default is des
#crypt md5
#crypt sha
#crypt des

# The following options are specific to pam_ldap.

# Filter to AND with uid=%s
#pam_filter objectclass=account

# The user ID attribute (defaults to uid)
#pam_login_attribute uid

# Search the root DSE for the password policy (works
# with Netscape Directory Server)
#pam_lookup_policy yes

# Group to enforce membership of
#pam_groupdn cn=PAM,ou=Groups,dc=padl,dc=com

# Group member attribute
#pam_member_attribute uniquemember

# Hash password locally; required for University of
# Michigan LDAP server, and works with Netscape
# Directory Server if you're using the UNIX-Crypt
# hash mechanism and not using the NT Synchronization
# service.
#pam_crypt local

------------------------------<snip>----------------------------
# /etc/pam.d/su
# Configured for ldap
#%PAM-1.0
auth       sufficient	/lib/security/pam_pwdb.so shadow nullok
auth       required     /lib/security/pam_ldap.so use_first_pass
account    sufficient	/lib/security/pam_pwdb.so
account    required     /lib/security/pam_ldap.so use_first_pass
password   required	/lib/security/pam_cracklib.so
password   required	/lib/security/pam_pwdb.so shadow use_authtok nullok
session    required	/lib/security/pam_pwdb.so
# Commented out for benchmarking
#session    optional	/lib/security/pam_xauth.so



------------------------------<snip>----------------------------
# /etc/pam.d/su
# Configured for pwdb (standard)
#%PAM-1.0
auth       required	/lib/security/pam_pwdb.so shadow nullok
account    required	/lib/security/pam_pwdb.so
password   required	/lib/security/pam_cracklib.so
password   required	/lib/security/pam_pwdb.so shadow use_authtok nullok
session    required	/lib/security/pam_pwdb.so
# Commented out for benchmarking
#session    optional	/lib/security/pam_xauth.so



Programs:

If you try to use these programs again, you're on your own.

For your information, I copied the passwd, shadow, and group files to
a working directory, where I kept all these programs as well as copies
of the migration tools from padl.com (part of the nss_ldap rpm.)

If you don't see a program here, it's probably part of the migration
tools.

------------------------------<snip>----------------------------
#!/usr/bin/perl
# logintest.pl

# Test program wacked together real fast from parts done by Rafe Jaffey.
# (Ugly, with lots of parts that aren't even used.)
# If you run this as root, you defeat the point.

# Tries to su lots of times.


#
# Configuration.
#

# Untaint.  Only the essentials.
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};


# Timeout for expect.
$timeout	= 15;

# Names of the file and the derivative hash files with user info.
$student_file		= "/var/local/acctmgt/student_email_data";
$student_hash_file	= "/var/local/acctmgt/student_email_data_hash";
$staff_file		= "/var/local/acctmgt/staff_email_data";
$staff_hash_file	= "/var/local/acctmgt/staff_email_data_hash";


#use Shadows;

require "Comm.pl";
&Comm::init;


#
# Main block.
#
$testruns = 1000; # Number of logins to do.

# Stick all the logins in an array.
    open(PASSWD,"passwd") or die "canot open";
    $passwdcnt = 0;
    while (! eof(PASSWD)) {
	$line = <PASSWD>;
	$users[$passwdcnt] = substr($line,0,index($line,':'));
	$passwdcnt ++;
    }
    close(PASSWD);
    $passwdcnt++;

# get a list of 1000 random logins
    $skipfirst = 99 ; # don't use the first 99 logins
$cnt = $passwdcnt - $skipfirst;
    for ($i = 1 ; $i <= $testruns ; $i++) {
	$testusers[$i] = $users[rand($cnt) + $skipfirst];
    }


# Try logging in with those logins.
for ($i = 1; $i <= $testruns ; $i++) {
    dologin( $testusers[$i]);
}
exit;




#
# Ugly, possibly dangerous code calls passwd and talks to it over a pty/tty
# pair.
#
sub dologin {
  (my $child_handle, $r, $err, $match, $ruid, $rtn);
  (my $user) = @_[0, 1];

  print "$user\n";

  $child_handle = open_proc("su $user" );

  ($match,$err,$before)=&expect($child_handle, $timeout, "Password: ");
  if ($err) {
    chomp($before);
    print STDERR "$0: Unknown response from su while waiting for \"password:\" prompt: $before.\n";
    exit;
  } else {
    print $child_handle "foo\n";
    ($match,$err,$before)=&expect($child_handle, $timeout, "[[]$user");
    if ($err) {
      chomp($before);
      print STDERR "$0: not logged in: $before.\n";
      exit;
    } else {

# Ok, we're in, logout
print $child_handle "exit\n"
    &close_it ($child_handle);
    }
  }

  return;
}

------------------------------<snip>----------------------------
#!/bin/bash
# preparetest.pl

# Program to setup for ldap testing.

# Replace all passwords with foo
cat shadow.orig | ./addpasswd > shadow

# Replace all the home dirs with /
# (some usernames are bad, contain space or ')
cat passwd.orig | ./munghomedir | grep -v "^[^ :]* [^ :]*:" | grep -v "^[^':]*'[^':]*:" > passwd

# Reload the sladp database
./setup

# Run the test
./logintest.pl

------------------------------<snip>----------------------------
#!/usr/bin/perl -w
# addpasswd

# makes all passwords "foo"

while (! eof(STDIN)) {
$line = <STDIN>;
$line =~ s/([^:]*):([^:]*):(.*)/$1:\$1\$9Lj2GYqR\$1vyGkJHgtWsuqCK4Add5l0:$3/;
print $line;
}

------------------------------<snip>----------------------------
#!/usr/bin/perl -w
# munghomedir

# sets all shells to /bin/bash

while (! eof(STDIN)) {
$line = <STDIN>;
$line =~ s\([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):(.*)\$1:$2:$3:$4:$5:$6:/bin/bash\;
print $line;
}

------------------------------<snip>----------------------------
#!/bin/bash

# Setup for ldap
#
# Karl O. Pinc <kop@meme.com>
#
# Mar 3, 2000
#
# Script that sets up the ldap database for the first time
# Does passwords and groups.

# Stop the ldap server.
/etc/rc.d/init.d/ldap stop

# Create the directories
[ -d /var/tmp/ldappeople ] || mkdir /var/tmp/ldappeople
[ -d /var/tmp/ldapgroup ] || mkdir /var/tmp/ldapgroup
[ -d /var/tmp/ldapgroup ] || mkdir /var/tmp/ldapgroup

# Erase the existing entires
rm -rf /var/tmp/ldappeople/*
rm -rf /var/tmp/ldapgroup/*
rm -rf /var/tmp/ldap/*

# The root nodes in each of the databases must be established.

# Start with the root node of the aic.

# When openldap 2.0 comes out --
# To link from the main database to the sub databases,
# the referrals in 'initialaic' should be changed to:
#
# dn: ou=People, dc=school, dc=edu
# objectclass: referral
# ref: "ldap://198.40.24.129/ou=People,dc=school,dc=edu";

#ldif2ldbm -n 3 -i initialaic
#ldif2ldbm -n 1 -i initialroot
cp initialtwo /tmp/junk
echo >>/tmp/junk

#(cat initialpeople ; echo ; export LDAP_BASEDN="dc=school,dc=edu" ; export LDAP_DEFAULT_MAIL_DOMAIN=school.edu; ./migrate_passwd.pl passwd) | grep -v krbname > /tmp/junk
(export LDAP_BASEDN="dc=school,dc=edu" ; export LDAP_DEFAULT_MAIL_DOMAIN=school.edu; ./migrate_passwd.pl passwd) | grep -v krbname >> /tmp/junk
#ldif2ldbm -n 1 -i /tmp/junk

# program patched so a group member can have the same name as a group.
#(cat initialgroup ; echo ; export LDAP_BASEDN="dc=school,dc=edu" ; export LDAP_DEFAULT_MAIL_DOMAIN=school.edu; ./migrate_group.pl group) > /tmp/junk
(export LDAP_BASEDN="dc=school,dc=edu" ; export LDAP_DEFAULT_MAIL_DOMAIN=school.edu; ./migrate_group.pl group) >> /tmp/junk
#ldif2ldbm -n 2 -i /tmp/junk
ldif2ldbm -n 1 -i /tmp/junk
#ldif2index -n 1 -i /tmp/junk uid
#ldif2index -n 1 -i /tmp/junk memberUid
#ldif2index -n 1 -i /tmp/junk dn

rm /tmp/junk

# Start the server back up
/etc/rc.d/init.d/ldap start

----------<snip, contents of initialtwo file>-------------------
dn: dc=school, dc=edu
dc: school.edu
o: The School
objectclass: organization
objectclass: dcObject

dn: ou=People, dc=school, dc=edu
objectclass: organizationalUnit

dn: ou=Group, dc=school, dc=edu
objectclass: organizationalUnit