¡Esta es una revisión vieja del documento!
Tabla de Contenidos
OpenLDAP
OpenLDAP es una implementación libre y de código abierto del protocolo Lightweight Directory Access Protocol (LDAP) desarrollada por el proyecto OpenLDAP. Está liberada bajo su propia licencia OpenLDAP Public License. LDAP es un protocolo de comunicación independiente de la plataforma. Muchas distribuciones GNU/Linux incluyen el software OpenLDAP para el soporte LDAP. Este software también corre en plataformas BSD, AIX, HP-UX, Mac OS X, Solaris, Microsoft Windows (NT y derivados, incluyendo 2000, XP, Vista), y z/OS.
El problema de la autenticación en Linux (y en Unix en general) esta dividido en dos partes, la primera es comprobar el usuario y password, y la segunda es obtener la información del usuario para poder completar el login (user ID, home directory, group membership, shell inicial). En la mayoría de las distribuciones de Linux esta problemática esta resuelta por dos componentes:
- PAM: que realiza la autenticación (comprueba el usuario y password, ademas de verificar la validez de la cuenta de usuario).
- NSS: que se encarga de obtener la información del usuario mencionada anteriormente.
Ambos componentes presentan una modularidad que les permite acceder a la información desde cualquier tipo de sistema de base de datos, como puede ser los archivos locales (/etc/passwd y / etc/group) o desde servidores como LDAP, Mysql, Windows.
Instalación
Deberemos bajar los siguientes paquetes
DB 4 http://www.oracle.com/database/berkeley-db/index.html
OpenLDAP http://www.openldap.org/
Para compilar DB4 solo deberemos ejecutar :
../dist/configure --enable-cxx
En cambio para compilar OpenLDAP deberemos correr :
CPPFLAGS="-I/usr/local/BerkeleyDB.4.8/include" LDFLAGS="-L/usr/local/lib -L/usr/local/BerkeleyDB.4.8/lib" LD_LIBRARY_PATH="/usr/local/BerkeleyDB.4.8/lib" ./configure --enable-crypt --enable-modules --enable-overlays --enable-bdb --enable-hdb --enable-ldap --enable-monitor --enable-passwd --enable-relay --enable-shell --enable-sock
Una vez hecho esto tendremos lista nuestra instalación para poder correr.
Replicación
REQUIERE Openldap 2.4
La replicación multimaster se puede hacer en 1 o más nodos, la única premisa es que todos deben estar iguales al momento de comenzar esta configuración (misma configuración y misma base).
Vamos a suponer que tenemos los servidores ldapba y ldap-miami.
En el servidor ldap1 agregar estas línas al final del slapd.conf:
serverID 1 overlay syncprov syncRepl rid=2 provider=ldap://ldap2.com binddn="cn=admin,ou=users,dc=dominio,dc=com" bindmethod=simple credentials=contraseña searchbase="dc=dominio,dc=com" type=refreshAndPersist retry="5 + 5 +" interval=00:00:00:05 starttls=yes tls_reqcert=never mirrormode true database monitor
En el servidor ldap2:
serverID 2 overlay syncprov syncRepl rid=1 provider=ldap://ldap1.com binddn="cn=admin,ou=users,dc=dominio,dc=com" bindmethod=simple credentials=contraseña searchbase="dc=dominio,dc=com" type=refreshAndPersist retry="5 + 5 +" interval=00:00:00:05 starttls=yes tls_reqcert=never mirrormode true database monitor
La configuración en ambos servidor es similar, solo cambia el ServerID, el rid y el provider, que van invertidos, como se observa en al configuración.
El usuario especificado en el binddn debe tener acceso completo a la base.
Politicas
Para poder manejar policies de passwords con LDAP y hacer que estos se bloqueen luego de determinados intentos se debe agregar lo siguiente:
RFC http://www.potaroo.net/ietf/old-ids/draft-behera-ldap-password-policy-00.txt RFC http://www.mozilla.org/directory/ietf-docs/draft-behera-ldap-password-policy-05.txt
/etc/openldap/slapd.conf
Despues de la definicion de la base de datos se deben agregar las siguientes lineas
overlay ppolicy ppolicy_default "cn=default,ou=policies,dc=pan-energy"
Registros a agregar en el arbol LDAP
Se debe agregar la “ou” Policies para que contenga todas la policies.
dn: ou=policies,dc=dominio,dc=com ou: policies objectClass: top objectClass: organizationalUnit structuralObjectClass: organizationalUnit
Ahora se deben agregar las politicas por defecto, en este ejemplo se bloquea el usuario por 120 segundos (2 minutos) luego de tres intentos fallidos.
dn: cn=default,ou=policies,dc=dominio,dc=com pwdFailureCountInterval: 30 pwdSafeModify: FALSE pwdGraceAuthNLimit: 5 pwdLockoutDuration: 120 objectClass: pwdPolicy objectClass: person objectClass: top objectClass: pwdPolicyChecker pwdMaxFailure: 3 pwdAllowUserChange: TRUE pwdMinLength: 5 cn: default pwdAttribute: userPassword pwdMinAge: 5 pwdLockout: TRUE pwdCheckQuality: 2 pwdInHistory: 5 sn: default policy pwdMustChange: FALSE pwdExpireWarning: 600
Modificado todo esto se reinicia el LDAP.
Chequeos de password
Este es un modulo para chequear o establecer dificultades a los passwords.
Hay que agregar el objectClass 'pwdPolicyChecker' con el atributo del modulo LDPA ej 'check_password.so' dentro de las politicas.
Parametros de configuración :
- useCracklib: integer. Usar o no CrackLib
- minPoints: integer. Puntaje minimo, cada punto se asigna por cada clase de caracter en el password.
- minUpper: integer. Cantidad minima de mayusculas
- minLower: integer. Cantidad minima de minusculas
- minDigit: integer. Cantidad minima de digitos
- minPunct: integer. Cantidad minima de signos
Default : check_password.conf
useCracklib 1 minPoints 3 minUpper 0 minLower 0 minDigit 0 minPunct 0
Mensaje de error ldappasswd
Result: Constraint violation (19) Additional info: Password fails quality checking policy
Ref.:
http://ltb-project.org/wiki/documentation/openldap-ppolicy-check-password
En los clientes
El NSS (Name Service Switch) provee una interface para configurar y acceder a diferentes bases de datos de cuentas de usuarios y claves como /etc/passwd, /etc/group, /etc/hosts, LDAP , etc.
nsswitch.conf
Un sistema configurado por defecto generalmente usa NSS y su archivo de configuración etc/nsswitch.conf para resolver nombres de usuario o grupo, este archivo se compone de llamados a bases de datos como passwd, shadow y group, y de otras fuentes como archivos locales, directorios de datos OpenLDAP, nis (Network Information Service), nisplus (NIS+), bases de datos PostgreSQL o MySQL.
passwd: files ldap shadow: files ldap group: files ldap hosts: files ldap
En el mismo orden en que son listadas seran utilizadas al momento de resolver las peticiones.
NSS es la implementación de una Biblioteca estándar de C que llama a las funciones getpwent o getgrent dependiendo del modulo NSS, exiten modulos LDAP, PostgreSQL, MySQL o MDNS.
Diagrama de funcionamiento :
Diagrama de funcionamiento con NSCD :
NSCD es un demonio que gestiona las búsquedas en «passwd», «group» y «host» para los programas en ejecución y guarda los resultados para la siguiente consulta, es para servicios lentos como LDAP, NIS o NIS+.
Ref.: http://code.google.com/p/nsscache/wiki/BackgroundOnNameServiceSwitch
Notas y extras
Para configurar el HASH por default del servidor, deberemos agregar dentro del archivo slapd.conf
password-hash {crypt}
Script para chequear vencimientos de passwords avisar antes de tiempo, o bloquear cuando esta vencido. Con modificaciones hechas por mi.
El original esta aca : http://ltb-project.org
#!/bin/sh #==================================================================== # Configuration #==================================================================== # # LDAP host URI # eg: ldap://localhost:389 # MY_LDAP_HOSTURI="ldap://10.1.1.1:389" # # LDAP root DN (optional) # eg: cn=Manager,dc=example,dc=com # MY_LDAP_ROOTDN="uid=ldapadmin,ou=users,dc=dominio-mio" # # LDAP root password (optional) # MY_LDAP_ROOTPW="password-mio" # # LDAP default password policy DN # eg: ou=defaultPasswordPolicy,dc=example,dc=com # If commented, we suppose there are no default, and only per-user policies # MY_LDAP_DEFAULTPWDPOLICYDN="cn=default,ou=policies,dc=dominio-mio" # # LDAP search base for users # eg: ou=People,dc=example,dc=com # MY_LDAP_SEARCHBASE="ou=users,dc=pan-energy" # # LDAP search filter to use to get all users # MY_LDAP_SEARCHFILTER="(&(uid=*)(objectClass=posixAccount))" # # Path to LDAP search binary # MY_LDAP_SEARCHBIN="/usr/local/openldap/bin/ldapsearch" # # Delay to begin sending adverts # Comment to use the pwdExpireWarning value of the user's Password Policy # #MY_MAIL_DELAY=1296000 # # LDAP attributes storing user's information # NAME: Display name of the user # LOGIN: Account ID of the user # MAIL: Email of the user # MY_LDAP_NAME_ATTR=ou MY_LDAP_LOGIN_ATTR=uid MY_LDAP_MAIL_ATTR=mail # # Mail body message, with particular variables : # %name : user name # %login : user login # MY_MAIL_BODY="From: support@example.com\n\n \ Hi %name,\n\n \ please change your password.\n\nThe LDAP team." # # Mail subject # MY_MAIL_SUBJECT="Your account will expire soon" # # Mail command binary # Replace mailx by mail for RedHat # MY_MAIL_BIN="/usr/local/sbin/envia_mail.pl" # # Log header format # Could include unix commands # #MY_LOG_HEADER="`date +\"%b %e %T\"` `hostname` $0[$$]:" MY_LOG_HEADER="" # # Path to GAWK (GNU awk) binary # MY_GAWK_BIN="/usr/bin/gawk" #==================================================================== # Functions #==================================================================== # Funciones de tiempo # Pongo segundos y me lo traduce en dias y horas function timerLengthString() { local days=$((0)) local hour=$((0)) local mins=$((0)) local secs=$1 local text="" # convert seconds to days, hours, etc days=$((secs / 86400)) secs=$((secs % 86400)) hour=$((secs / 3600)) secs=$((secs % 3600)) mins=$((secs / 60)) secs=$((secs % 60)) # build full string from unit strings text="$text$(timerLengthStringPart $days "dia")" text="$text$(timerLengthStringPart $hour "hora")" text="$text$(timerLengthStringPart $mins "minuto")" text="$text$(timerLengthStringPart $secs "segundo")" # trim leading and trailing whitespace text=${text## } text=${text%% } # special case for zero seconds if [ "$text" == "" ]; then text="0 seconds" fi # echo output for the caller echo -ne ${text} } # formats a time unit into a string # $1: integer count of units: 0, 6, etc # $2: unit name: "hour", "minute", etc function timerLengthStringPart() { local unit=$1 local name=$2 if [ $unit -ge 2 ]; then echo -ne " ${unit} ${name}s" elif [ $unit -ge 1 ]; then echo -ne " ${unit} ${name}" else echo -ne "" fi } # # Retrieves date in seconds. # This function could take one parameter, a time returned by the command # `date +"%Y %m %d %H %M %S"`. Without parameter, it returns GMT time. # getTimeInSeconds() { date=0 os=`uname -s` if [ "$1" ]; then date=`${MY_GAWK_BIN} 'BEGIN { \ if (ARGC == 2) { \ print mktime(ARGV[1]) \ } \ exit 0 }' "$1"` else if [ "${os}" = "SunOS" ]; then # Under Sun Solaris, there is no simple way to # retrieve epoch time. # TODO: manage zulu time (GMT) date=`/usr/bin/truss /usr/bin/date 2>&1 | nawk -F= \ '/^time\(\)/ {gsub(/ /,"",$2);print $2}'` else now=`date +"%Y %m %d %H %M %S" -u` date=`getTimeInSeconds "$now"` fi fi echo ${date} } #==================================================================== # Script #==================================================================== ## Variables initialization tmp_dir="/tmp/$$.checkldap.tmp" result_file="${tmp_dir}/res.tmp.1" buffer_file="${tmp_dir}/buf.tmp.1" ldap_param="-LLL -H ${MY_LDAP_HOSTURI} -x" nb_users=0 nb_expired_users=0 nb_warning_users=0 ## Some tests if [ -d ${tmp_dir} ]; then echo "Error : temporary directory exists (${tmp_dir})" exit 1 fi mkdir ${tmp_dir} if [ ${MY_LDAP_ROOTDN} ]; then ldap_param="${ldap_param} -D ${MY_LDAP_ROOTDN} -w ${MY_LDAP_ROOTPW}" fi ## Performs global search ${MY_LDAP_SEARCHBIN} ${ldap_param} -s one -b "${MY_LDAP_SEARCHBASE}" \ "${MY_LDAP_SEARCHFILTER}" "dn" > ${result_file} ## Loops on results while read dnStr do # Do not use blank lines if [ ! "${dnStr}" ]; then continue fi # Process ldap search dn=`echo ${dnStr} | cut -d : -f 2` # Increment users counter nb_users=`expr ${nb_users} + 1` ${MY_LDAP_SEARCHBIN} ${ldap_param} -s base -b "${dn}" \ ${MY_LDAP_NAME_ATTR} ${MY_LDAP_LOGIN_ATTR} ${MY_LDAP_MAIL_ATTR} pwdChangedTime pwdPolicySubentry \ > ${buffer_file} login=`grep -w "${MY_LDAP_LOGIN_ATTR}:" ${buffer_file} | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` name=`grep -w "${MY_LDAP_NAME_ATTR}:" ${buffer_file} | cut -d : -f 2\ | sed "s/^ *//;s/ *$//"` mail=`grep -w "${MY_LDAP_MAIL_ATTR}:" ${buffer_file} | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` pwdChangedTime=`grep -w "pwdChangedTime:" ${buffer_file} \ | cut -d : -f 2 | cut -c 0-15 | sed "s/^ *//;s/ *$//"` pwdPolicySubentry=`grep -w "pwdPolicySubentry:" ${buffer_file} \ | cut -d : -f 2 | sed "s/^ *//;s/ *$//"` # Go to next entry if no pwdChangedTime if [ ! "${pwdChangedTime}" ]; then echo "${MY_LOG_HEADER} No hay fecha de cambio para ${login}" >&2 continue fi # Go to next entry if no pwdPolicySubEntry and no default policy if [ ! "${pwdPolicySubentry}" -a ! "${MY_LDAP_DEFAULTPWDPOLICYDN}" ]; then echo "${MY_LOG_HEADER} No hay politica de password para ${login}" >&2 continue fi # Retrieves user policy pwdMaxAge and pwdExpireWarning attributes ldap_search="${MY_LDAP_SEARCHBIN} ${ldap_param} -s base" if [ "${pwdPolicySubentry}" ]; then ldap_search="${ldap_search} -b ${pwdPolicySubentry}" else ldap_search="${ldap_search} -b ${MY_LDAP_DEFAULTPWDPOLICYDN}" fi ldap_search="$ldap_search pwdMaxAge pwdExpireWarning" pwdMaxAge=`${ldap_search} | grep -w "pwdMaxAge:" | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` pwdExpireWarning=`${ldap_search} | grep -w "pwdExpireWarning:" | cut -d : -f 2 \ | sed "s/^ *//;s/ *$//"` # Replace MAIL_DELAY by pwdExpireWarning if exists MY_MAIL_DELAY=${MY_MAIL_DELAY:=$pwdExpireWarning} # Retrieves time difference between today and last change. if [ "${pwdChangedTime}" ]; then s=`echo ${pwdChangedTime} | cut -c 13-14` m=`echo ${pwdChangedTime} | cut -c 11-12` h=`echo ${pwdChangedTime} | cut -c 9-10` d=`echo ${pwdChangedTime} | cut -c 7-8` M=`echo ${pwdChangedTime} | cut -c 5-6` y=`echo ${pwdChangedTime} | cut -c 0-4` currentTime=`getTimeInSeconds` pwdChangedTime=`getTimeInSeconds "$y $M $d $h $m $s"` diffTime=`expr ${currentTime} - ${pwdChangedTime}` fi # Go to next user if password already expired expireTime=`expr ${pwdChangedTime} + ${pwdMaxAge}` if [ ${currentTime} -gt ${expireTime} ]; then # Sending mail... ${MY_MAIL_BIN} ${mail} ${login} 'bloqueado' \'${EXPIROEN}\' >&2 # echo "${logmsg}" | ${MY_MAIL_BIN} ${mail} ${expireTime} ${login} 'bloqueado' >&2 if [ "${login}" != "ldapadmin" ]; then echo " dn: uid=${login},ou=users,dc=pan-energy changetype: modify add: pwdAccountLockedTime pwdAccountLockedTime: 000001010000Z"| ldapmodify -x -D "uid=ldapadmin,ou=users,dc=pan-energy" -w ..xtechpae -v # Print debug information on STDERR nb_expired_users=`expr ${nb_expired_users} + 1` fi echo "${MY_LOG_HEADER} Password expirado para ${login} mail enviado a <${mail}>" >&2 continue fi # ALL LDAP attributes should be there, else continue to next user # if [ "${mail}" -a "${name}" -a "${login}" -a "${diffTime}" -a "${pwdMaxAge}" ] if [ "${mail}" -a "${login}" -a "${diffTime}" -a "${pwdMaxAge}" ] then EXPIRACION=`expr ${pwdMaxAge} - ${diffTime}` EXPIROEN="$(timerLengthString ${EXPIRACION})" # Ajusts time with delay diffTime=`expr ${diffTime} + ${MY_MAIL_DELAY}` if [ ${diffTime} -gt ${pwdMaxAge} ]; then logmsg="${MY_MAIL_BODY}" logmsg=`echo ${logmsg} | sed "s/%name/${name}/; \ s/%login/${login}/"` # Sending mail... #echo "${logmsg}" | ${MY_MAIL_BIN} ${mail} \'${EXPIROEN}\' ${login} 'warning' >&2 #echo "${logmsg}" | ${MY_MAIL_BIN} \'${mail}\' \'${EXPIROEN}\' \'${login}\' 'warning' >&2 ${MY_MAIL_BIN} ${mail} ${login} 'warning' \'${EXPIROEN}\' >&2 #echo ${MY_MAIL_BIN} ${mail} ${login} 'warning' \'${EXPIRO}\' >&2 # Print debug information on STDERR #echo "${MY_LOG_HEADER} Mail de advertencia ${login} <${mail}>" >&2 echo "${MY_LOG_HEADER} Password para "${login}" a punto de expirar en" ${EXPIROEN} >&2 # Increment warning counter nb_warning_users=`expr ${nb_warning_users} + 1` fi fi done < ${result_file} # Print statistics on STDOUT echo "${MY_LOG_HEADER} --- Statistics ---" echo "${MY_LOG_HEADER} Users checked: ${nb_users}" echo "${MY_LOG_HEADER} Account expired: ${nb_expired_users}" echo "${MY_LOG_HEADER} Account in warning: ${nb_warning_users}" # Delete temporary files rm -rf ${tmp_dir} # Exit exit 0
Script que envia mails
#!/usr/bin/perl use strict; use MIME::Lite; my $usuario = $ARGV[1]; if(!$usuario) { print "Falta nombre de usuario"; exit(1); } my $tipo_mail = $ARGV[2]; if(!$tipo_mail) { print "Falta tipo de mail, warning o bloqueo"; exit(1); } my $email = $ARGV[0]; #my $expiracion = localtime($ARGV[1]); my $expiracion = $ARGV[3]." ".$ARGV[4]." ".$ARGV[5]." ".$ARGV[6]." ".$ARGV[7]." ".$ARGV[8]." ".$ARGV[9]; if($tipo_mail eq "warning") { my $asunto = "Su cuenta de usuario (".$usuario.") para equipos de Telecomunicaciones está por vencer"; my $asuntohtml = "Su cuenta de usuario ( ".$usuario." ) para equipos de Telecomunicaciones está por vencer en <br> ".$expiracion.""; my $mensaje = "<font size='+1'><i><ul> <li>Su usuario de autenticación de Radius vencerá en ".$expiracion.".</li><br> <li>Dirigirse a https://servidor-ldap/cambia_password para cambiarla.</li><br> <li>Leer atentamente las características que debe tener el password.</li><br> <li>Una vez vencida su cuenta solamente podrá ser cambiada por</li> <ul><li>Nombre int. 3994</li></ul> <ul><li>Nombre int. 4491</li></ul> </ul></i></font>"; my $msg = MIME::Lite->new ( Subject => $asunto, From => 'Administracion LDAP ldapadmin@servidor-ldap', To => $email, Type => 'text/html', Data => "<H1>".$asuntohtml."</H1><IMG SRC='http://servidor-ldap/logo.gif'><hr>".$mensaje."" ); $msg->send(); print " $0 : Mail de warning enviado a <".$email.">\n"; } if($tipo_mail eq 'bloqueado') { my $asunto = "Su cuenta de usuario (".$usuario.") para equipos de Telecomunicaciones a sido bloqueada"; my $asuntohtml = "Su cuenta de usuario ( ".$usuario." ) para equipos de Telecomunicaciones a sido bloqueada"; my $mensaje = "<font size='+1'><i><ul> <li>Su usuario de autenticación de Radius vencio.</li><br> <li>Una vez vencida su cuenta solamente podrá ser cambiada por</li> </ul></i></font>"; my $msg = MIME::Lite->new ( Subject => $asunto, From => 'Administracion LDAP ldapadmin@servidor-ldap', To => $email, Type => 'text/html', Data => "<H1>".$asuntohtml."</H1><IMG SRC='http://servidor-ldap/logo.gif'><hr>".$mensaje."" ); $msg->send(); print " $0 : Mail de bloqueo enviado a <".$email.">\n"; }
Autocambio de contraseña
En el caso de que necesitemos obviamente que antes de que se les caduque la contraseña a los usuarios, ellos mismos puedan cambiar su contraseña, podremos utilizar un script via web
En este apartado del archivo slapd.conf deberemos tener activado el self write
<code> access to *
by self write
<code>