Pages

Saturday, November 5, 2011

LDAP for Slackware Linux

When you work with Slackware Linux and try to authenticate with LDAP then you will figure out that it won't work. This has a simple reason: to authenticate with LDAP you need PAM, but Patrick Volkerding refuses to put PAM into Slackware because of security reason - AFAIK. Here is a small description how to setup Slackware for authentication with LDAP. But be aware: I am doing some things here that you shouldn't do under normal circumstances.

1. Don't use this description on a productive system unless you know exactly what you are doing
2. I use my LDAP server as a client too, that could get nasty if you try to boot and the LDAP server is not available

Keep in mind: always make sure that you can login via ssh. It may be the only possibility to get back the control of your server.
First we need to know which software we need:

OpenLDAP: the LDAP server and client software itself (already shipped with Slackware but without the OpenLDAP server so it has to be recompiled completely)
Linux-PAM: plugable authentication module to authenticate (need to be compiled)
pam_ldap: PAM support for LDAP (need to be compiled)
nss_ldap: NSS support for LDAP (need to be compiled)
shadow (Client): shadow with support for PAM (already shipped but need to be recompiled)

The OpenLDAP package in Slackware is not ready for use. So we need the source, recompile it and create a new package. First mount the CD (or DVD) and copy the source:

# mkdir -p /mnt/loop
# mount slackware-14.0-source-dvd.iso /mnt/loop/ -o loop,ro -t iso9660
# cp -r /mnt/loop/source/n/openldap-client/ /usr/src/
# umount /mnt/loop/


Then go to the copied source directory and create a new build script and a new slack-desc using sed:

# cd /usr/src/openldap-client/
# cat openldap-client.SlackBuild | sed 's/-client//g' > openldap.SlackBuild
# cat slack-desc | sed 's/-client//g' > slack-desc.new
# mv slack-desc.new slack-desc


Now edit the new build script and change the configure command like this (starting at line 67):

# vi openldap.SlackBuild
...
CFLAGS="$SLKCFLAGS" \
./configure \
  --prefix=/usr \
  --libdir=/usr/lib${LIBDIRSUFFIX} \
  --localstatedir=/var/lib \
  --sysconfdir=/etc \
  --mandir=/usr/man \
  --with-cyrus-sasl \
  --with-tls \
  --enable-crypt \
  --with-readline \
  --with-threads \
  --enable-debug \
  --enable-syslog \
  --enable-dynamic \
  --enable-bdb \
  --enable-local \
  --enable-proctitle \
  --disable-static \
  --enable-shared \
  --enable-slapd \
  --disable-slurpd
...


Exit vi and run the the script. It will create a new OpenLDAP package with all needed daemons, clients and libraries which were not all included within the shipped package:

# chmod 755 openldap.SlackBuild
# ./openldap.SlackBuild
...


When the package is created, remove the original package and raplace it with new one:

# removepkg openldap-client
...
# installpkg /tmp/openldap-2.4.31-*.txz
...


Go on and configure OpenLDAP. Start with the configuration file for the slapd - the OpenLDAP server daemon:

# mv /etc/openldap/slapd.conf /etc/openldap/slapd.conf.orig
# vi /etc/openldap/slapd.conf
# SCHEMES
include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/inetorgperson.schema
include         /etc/openldap/schema/nis.schema
# ACL
include         /etc/openldap/acl.conf
# DATABASE
database        bdb
directory       /var/lib/ldap/example.com
# GENERIC
pidfile         /var/run/slapd/slapd.pid
argsfile        /var/run/slapd/slapd.args
suffix          "dc=example,dc=com"
rootdn          "cn=ldapadmin,dc=example,dc=com"
rootpw          {SSHA}MWyKewM8KHu22/4SdiYYuyXEoAjrZEoQ
password-hash   {CRYPT}
loglevel        256
sizelimit       unlimited


The section SCHEMES will load some definitions for the informations you want to provide. There are various schemes available depending on the informations you want to provide in your environment.
The section ACL will load the access control list. You can put it directly into the configuration file or put in a seperate file. I like to store my ACL's in a seperate file:

# vi /etc/openldap/acl.conf
access to attrs=userPassword
  by self write
  by * auth
access to dn.base=""
  by * read
access to * by self write
  by * read


They are read from the bottom to the top, at first everybody gets read access to everything, but they are getting more granular when it's about passwords at the top.
The section DATABASE defines the database and where it will be stored, in this case a simple BDB. When you start slapd (the OpenLDAP daemon) then you may notice a message like this in the logs:

# cat /var/log/ldap.log
...
bdb_db_open: warning - no DB_CONFIG file found in directory /var/lib/ldap/example.com: (2). Expect poor performance for suffix "dc=example,dc=com".
...


You can get rid of this message when you create the DB_CONFIG file. Just copy the one found in the openldap package:

# mkdir -p /var/lib/ldap/example.com
# cp /var/lib/openldap-data/DB_CONFIG.example /var/lib/ldap/example.com/DB_CONFIG


The last section is the GENERIC section. It holds some informations for the LDAP daemon (slapd) itself like the pidfile, the suffix (eg. your domain) etc. For the above defined pid file you have to create a directory:

# mkdir /var/run/slapd/

The sections above are not for real - I just like to use them to make the configuration file easy to understand.
Very important is the rootpw. The rootpw has to be generated by slappasswd:

# slappasswd
New password:
Re-enter new password:
{SSHA}MWyKewM8KHu22/4SdiYYuyXEoAjrZEoQ


Put the complete generated string into the configuration file slapd.conf including the start of the string {SSHA}.

Befory you start slapd you should configure syslog for logging. Add the following lines to your /etc/syslog.conf. Important note: the spaces between local4.* and /var/log/ldap.log aren't spaces - they have to be tabs! Otherwise your syslog daemon not log correct:

# vi /etc/syslog.conf
...
# LDAP
local4.* <- tab -> /var/log/ldap.log
...


slapd will log on local4, with the loglevel 256 defined you will be able to see most information about connections etc. Finally create the logfile and restart the syslog daemon:

# touch /var/log/ldap.log
# chmod 644 /var/log/ldap.log
# /etc/rc.d/rc.syslog restart


Now the big moment has arrived. Try to start slapd (with your own IP of course):

# /usr/libexec/slapd -h ldap://192.168.1.73:389

You should see some information in ldap.log now:

# tail -f /var/log/ldap.log
Nov 18 15:46:31 dc01 slapd[30753]: @(#) $OpenLDAP: slapd 2.4.31 (Nov 18 2012 14:08:17) $ ^Iroot@dc01:/tmp/openldap-2.4.31/servers/slapd
Nov 18 15:46:31 dc01 slapd[30754]: bdb_monitor_db_open: monitoring disabled; configure monitor database to enable
Nov 18 15:46:31 dc01 slapd[30754]: slapd starting
...


Also check if the slapd is really running:

# pgrep -fl slapd
27641 /usr/libexec/slapd -h ldap://192.168.1.70:389


When the server is running then you can configure the client:

# mv /etc/openldap/ldap.conf /etc/openldap/ldap.conf.orig
# vi /etc/openldap/ldap.conf
BASE    dc=example,dc=com
URI     ldap://192.168.1.73:389


My experience with OpenLDAP is that different application may look for different ldap.conf files located at diferrent directories. So it is a wise choice to create the original file in /etc/openldap/ and link it to /etc:

# ln -s /etc/openldap/ldap.conf /etc/ldap.conf

Now we need to create a basic DIT (domain information tree) to store the users, passwords and groups:

# vi basic_dit.ldif
dn: dc=example,dc=com
o: example
dc: example
objectClass: dcObject
objectClass: organization

dn: ou=groups,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Groups

dn: cn=users,ou=groups,dc=example,dc=com
objectClass: top
objectClass: posixGroup
gidNumber: 100
cn: users

dn: ou=users,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Users

dn: uid=sneill,ou=users,dc=example,dc=com
cn: Sam Neill
sn: Neill
givenName: Sam
uid: sneill
uidNumber: 1000
gidNumber: 100
homeDirectory: /home/sneill
loginShell: /bin/bash
gecos: Normal User
mail: sam.neill@example.com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:


The basic dit above will add your domain (example.com), creates two organizational units (groups and users) and creates a group (users with gid 100) and a user (sneill with uid 1000). Remember that Slackware already has group called users with gid 100, so you probably want to remove it first from /etc/groups or use a different group name and gid. Now add the basic dit to your LDAP:

# ldapadd -x -W -D 'cn=ldapadmin,dc=example,dc=com' -f basic_dit.ldif
Enter LDAP Password:
adding new entry "dc=example,dc=com"
adding new entry "ou=groups,dc=example,dc=com"
adding new entry "cn=users,ou=groups,dc=example,dc=com"
adding new entry "ou=users,dc=example,dc=com"
adding new entry "uid=sneill,ou=users,dc=example,dc=com"


Next try to search for your recently provided dit:

# ldapsearch -x -W -D 'cn=ldapadmin,dc=example,dc=com' -b 'dc=example,dc=com' -LLL
...


When you did it so far then the LDAP server it self is running. Note that the password for the user is not set yet, you can do this easily with ldappasswd:

# ldappasswd -x -W -D 'cn=ldapadmin,dc=example,dc=com' -S 'uid=sneill,ou=users,dc=example,dc=com'
New password:
Re-enter new password:
Enter LDAP Password:


The first password you enter is for the user sneill, the second to verify the first password. The third password you have to enter is for the user ldapadmin to authenticate against the LDAP. To start and stop the slapd you can use this script (ignore the boot option this time):

#!/bin/bash
bin="/usr/libexec/slapd"
host="192.168.1.73"
port="389"
pid_file="/var/run/slapd/slapd.pid"

function boot_slapd {
  echo "Starting slapd: $bin"
  $bin -h "ldap://127.0.0.1:389"
}

function start_slapd {
  echo "Starting slapd: $bin"
  $bin -h "ldap://127.0.0.1:389 ldap://${host}:${port}"
}

function stop_slapd {
  if [[ -f $pid_file ]]; then
    kill -15 `cat $pid_file`
    sleep 2
  fi
}

case "$1" in
  'boot')
    boot_slapd
    ;;
  'start')
    start_slapd
    ;;
  'stop')
    stop_slapd
    ;;
  'restart')
    stop_slapd
    start_slapd
    ;;
  *)
    echo "usage $0 start|stop|restart|boot"
    ;;
esac


First make it executable before using it:

# chmod 755 /etc/rc.d/rc.slapd

Then it can be used like any other script in Slackware:

# /etc/rc.d/rc.slapd stop
# /etc/rc.d/rc.slapd start
Starting slapd: /usr/libexec/slapd
# /etc/rc.d/rc.slapd restart
Starting slapd: /usr/libexec/slapd


Before you can use a client with OpenLDAP support you need to compile and configure Linux-PAM. Linux-PAM can be found here: http://www.linux-pam.org/
Download a copy, extract it and compile it:

# cd /usr/src
# wget -c "http://www.linux-pam.org/library/Linux-PAM-1.1.6.tar.bz2"
...
# tar xf Linux-PAM-1.1.6.tar.bz2
# cd Linux-PAM-1.1.6
# ./configure --prefix=/usr --disable-static --enable-shared
...
# make && make install
...


After the compile finished you should have at least the directory /lib/security/ (or /lib64/security) with a few PAM libraries/modules in. To get PAM working with LDAP, you need the pam_ldap module which can be found on http://www.padl.com. Download and extract it:

# cd /usr/src
# wget http://www.padl.com/download/pam_ldap.tgz
...
# tar xfz pam_ldap.tgz
# cd pam_ldap-*


For 32bit use this configure command:

# ./configure --prefix=/ --libdir=/lib --mandir=/usr/man --disable-static --enable-shared
...


And for 64bit this one:

# ./configure --prefix=/ --libdir=/lib64 --mandir=/usr/man --disable-static --enable-shared
...


Now run make to compile and install pam_ldap:

# make && make install
...


Check that you have the LDAP module for PAM:

# ls /lib/security/*ldap*
/lib/security/pam_ldap.so


Next you need nss_ldap. Download it from http://www.padl.com and extract it. Don't forget to set the PATH variable otherwise the make script can not find the vers_string script which is shipped with the source:

# cd /usr/src
# wget -c "http://www.padl.com/download/nss_ldap.tgz"
...
# tar xfz nss_ldap.tgz
# cd nss_ldap-*
# export PATH=$PATH:./


For 32bit use this configure command:

# ./configure --prefix=/ --libdir=/lib --mandir=/usr/man --disable-static --enable-shared
...


And for 64bit this one:

# ./configure --prefix=/ --libdir=/lib64 --mandir=/usr/man --disable-static --enable-shared
...


Now run make to compile and install nss_ldap:

# make && make install
...


To make your system ready for usage with LDAP configure /etc/nsswitch.conf and add ldap to the passwd, shadow and group entrys like this:

# vi /etc/nsswitch.conf
...
passwd:         files ldap
shadow:         files ldap
group:          files ldap
...


If you have done everything right then you sould be able to see your available users and groups already:

# getent passwd
...
sneill:x:1000:100:Normal User:/home/sneill:/bin/bash
...
# getent group
...
users:x:100:
...
users:*:100:
...


If you don't have this user in your file based environment (/etc/passwd etc.) then the user should show up from your prior configured LDAP. If your LDAP user does not appear, try to figure out what went wrong und fix it. Don't do the final last steps if it does not work as expected. Also, the group 'users' shows up two times, one from /etc/group and one from your LDAP.

Finally you need to recompile the shadow package provided with Slackware. Download or copy the complete source tree for the shadow package (I copied it from the ISO):

# mkdir -p /mnt/loop
# mount slackware-14.0-source-dvd.iso /mnt/loop/ -o loop,ro -t iso9660
# cp -r /mnt/loop/source/a/shadow/ /usr/src/
# umount /mnt/loop/


Now go to the source directory. Edit the build script and add to the configure options the --with-libpam switch that it looks like this:

# cd /usr/src/shadow
# vi shadow.SlackBuild
...
./configure \
  --prefix=/usr \
  --sysconfdir=/etc \
  --mandir=/usr/man \
  --docdir=/usr/doc/shadow-$VERSION \
  --disable-shared \
  --without-libcrack \
  --with-libpam \
  --build=$ARCH-slackware-linux
...


Save the script and exit vi, then run the script:

# sh shadow.SlackBuild
...
Slackware package /tmp/shadow-4.1.4.3-x86_64-7.txz created.

When the compile finished, you should have a new shadow package under /tmp. Check that it contains the login and su binary:

# tar tfv /tmp/shadow-*.txz | grep bin
...
-rws--x--x root/root     36913 2011-11-12 14:00 bin/su
-rwxr-xr-x root/root     43720 2011-11-12 14:00 bin/login
...


Now remove the old shadow package and replace it with the new package with PAM support:

# removepkg shadow
...
# installpkg /tmp/shadow-*.txz
...


Next you need to configure each authentication mechanism you want to provide for PAM. If you want to use the normal console login, then you need to configure /etc/pam.d/login. If you want to use ssh, then you need to configure /etc/pam.d/sshd and so on. The new shadow package comes already with a few rule sets which can be found under /etc/pam.d. I don't like them so I always make a backup of them and use my own:

# mkdir /etc/pam.d/orig
# mv /etc/pam.d/* /etc/pam.d/orig


Here is my sample for /etc/pam.d/login:

# vi /etc/pam.d/login
auth            include         common-auth
account         include         common-account
session         include         common-session

# vi /etc/pam.d/common-auth
auth            sufficient      pam_unix.so
auth            sufficient      pam_ldap.so use_first_pass
auth            required        pam_deny.so

# vi /etc/pam.d/common-account
account         sufficient      pam_unix.so
account         sufficient      pam_ldap.so
account         required        pam_permit.so

# vi /etc/pam.d/common-password
password        sufficient      pam_unix.so
password        sufficient      pam_ldap.so
password        required        pam_deny.so

# vi /etc/pam.d/common-session
session         sufficient      pam_mkhomedir.so skel=/etc/skel umask=0022
session         sufficient      pam_unix.so
session         sufficient      pam_ldap.so
session         required        pam_deny.so


The first file is for the login mechanism it self. PAM provides 4 modules (auth, account, password and session). I created for each module a seperate file with their own mechanisms (common-auth, common-account, common-password and common-session). Each module can be controlled by 4 mechanism (requisite, required, sufficient and optional). The example above is only for the login on a console. You can try to log in now:

# login
configuration error - unknown item 'FAILLOG_ENAB' (notify administrator)
...
configuration error - unknown item 'ENVIRON_FILE' (notify administrator)
blog01 login: root
Password:


One thing: the configuration error messages that appear can be disabled by editing the file /etc/login.defs. Just comment them out:

# vi /etc/login.defs
...
#FAILLOG_ENAB           yes
...


When the root login worked, try to login as user sneill:

# login
blog01 login: sneill
Password:
Creating directory '/home/sneill'.
$


To allow your users to use passwd and su, you need two more pam rules for passwd and su:

# vi /etc/pam.d/passwd
auth            include         common-auth
account         include         common-account
password        include         common-password

# vi /etc/pam.d/su  
auth            sufficient      pam_rootok.so
auth            include         common-auth
account         include         common-account
session         include         common-session


With these rulesets root and normal users can change their password, root can su to a normal user and normal users may switch to root. To login via ssh you have to recompile the ssh package and create a pam ruleset for sshd. Before you recompile the ssh package make sure that you can login on a console or via telnet. You can recompile and reinstall the ssh package using ssh but when something goes wrong then you may loose your connection and your control about the server. To recompile ssh mount the CD (or DVD) again and copy the source:

# mkdir -p /mnt/loop
# mount slackware-14.0-source-dvd.iso /mnt/loop/ -o loop,ro -t iso9660
# cp -r /mnt/loop/source/n/openssh/ /usr/src/
# umount /mnt/loop/


Next edit the build script again and replace the --without-pam option with the --with-pam option:

# cd /usr/src/openssh/
# vi openssh.SlackBuild
...
# Compile package:
CFLAGS="$SLKCFLAGS" \
./configure \
  --prefix=/usr \
  --mandir=/usr/man \
  --sysconfdir=/etc/ssh \
  --with-pam \
  --with-md5-passwords \
  --with-tcp-wrappers \
  --with-default-path=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin \
  --with-privsep-path=/var/empty \
  --with-privsep-user=sshd \
  --build=$ARCH-slackware-linux
...


Start the script to recompile ssh with pam support:

# sh openssh.SlackBuild
...
Slackware package /tmp/openssh-6.1p1-x86_64-1.txz created.


Check that the package contains the ssh binaries:

# tar tfv /tmp/openssh-*.txz | grep "bin/ssh"
...
-rwxr-xr-x root/root    356828 2011-11-13 10:53 usr/bin/ssh
-rwxr-xr-x root/root    444684 2011-11-13 10:53 usr/sbin/sshd


Then remove the ssh package and install the new package with pam support:

# removepkg openssh
# installpkg /tmp/openssh-*.txz


Edit the sshd configuration and activate pam:

# vi /etc/ssh/sshd_config
...
#UsePAM no
UsePAM yes
...


Save your configuration and restart the ssh daemon:

# /etc/rc.d/rc.sshd restart
WARNING: killing listener process only.  To kill every sshd process, you must

...

The last step is to create a pam ruleset for ssh. If you are lazy (like me) then just copy the ruleset for login to sshd (don't forget the d at the end of sshd):

# cp /etc/pam.d/login /etc/pam.d/sshd

Finally you should be able to login as root and normal users via ssh and authenticate against ldap:

# ssh sneill@blog01
Password:
Linux 2.6.37.6-smp.
sneill@blog01:~$ id -a
uid=1000(sneill) gid=100(users) groups=100(users)
sneill@blog01:~$


If you want to use your OpenLDAP server as a OpenLDAP client too, then you have to adjust a few more things. The first thing is that you need to start slapd before any other server process tries to contact it. The first service that tries to communicate with slapd is udev (as far as I can tell). The issue is that the network hardware is initialized after udev. That is the reason why I have added the boot option to the rc.slapd script (the boot option starts slapd listening on 127.0.0.1 only). The correct boot order will be then like this:

1. Start slapd listening on 127.0.0.1 only
2. Start udev (takes a few seconds more then usual)
3. Initialize all other network hardware (eg. eth0)
4. Restart slapd listening on 127.0.0.1 and all other network hardware (eg. eth0)

To achive this last goal add rc.slapd before starting udev:

# vi /etc/rc.d/rc.M
...
# Start the openldap server:
if [ -x /etc/rc.d/rc.slapd ]; then
  . /etc/rc.d/rc.slapd boot
fi

# Run rc.udev again.  This will start udev if it is not already running
# (for example, upon return from runlevel 1), otherwise it will trigger it
# to look for device changes and to generate persistent rules if needed.
if grep -wq sysfs /proc/mounts && grep -q tmpfs /proc/filesystems ; then
  if ! grep -wq nohotplug /proc/cmdline ; then
    if [ -x /etc/rc.d/rc.udev ]; then
      /bin/sh /etc/rc.d/rc.udev start
    fi
  fi
fi
...


Next add the rc.slapd to rc.local:

# vi /etc/rc.d/rc.local
/etc/rc.d/rc.slapd restart


To make sure that your server can communicate to the slapd you need to change the host IP in /etc/openldap/ldap.conf to 127.0.0.1:

# vi /etc/openldap/ldap.conf
BASE    dc=example,dc=com
URI     ldap://127.0.0.1:389


Any other client keeps the usual server IP on your network - no need to change it to 127.0.0.1!
To stop slapd you need nothing to do. It will get killed by rc.K automatically. If you insist stopping slapd by /etc/rc.d/rc.slapd then add the following to rc.local_shutdown:

# vi /etc/rc.d./rc.local_shutdown
/etc/rc.d/rc.slapd stop


If you create rc.local_shutdown the first time then run chmod on it otherwise it will not be executed during shutdown:

# chmod 755 /etc/rc.local_shutdown

That's all - enjoy your new OpenLDAP!

Update 11/08/2011: I really thought that the original shipped OpenLDAP package included all daemons etc. That was totally wrong! I updated the article with a complete description how to rebuild the OpenLDAP package with all daemons etc, added DB_CONFIG for the Berkeley DB, removed invalid schemes in slapd.conf (nisdomainobject.schema and mozillaOrgPerson.schema), directory for the pid added (/var/run/slapd/)
Update 11/12/2011: removed the nis stuff from the dit, reworked the install instruction for nss_ldap, moved the pam configuration at the end, added ldappasswd for the sample user, added pam rulesets for passwd and su
Update 11/13/2011: added pam support for ssh, added rc.slapd, readded nis.schema - the removel of it was too much remove
Update 08/27/2012: removed an error in the Slackbuild for OpenSSH (--without-pam)
Update 11/18/2012: Updated everything to Slackware 14.0, fixed a few typos, commands etc, making differences between 32/64bit when necessary, added rc.slapd to rc.M, rc.local etc for booting and shutting down