OpenLDAP

De gacq wiki
Saltar a: navegación, buscar

Filters

Operators

& = and
| = or
! = not
~= = approx equal
>= = greater than or equal
<= = less than or equal
* = any 
Examples
(objectclass=posixAccount)
(cn=Mickey M*)
(|(uid=fred)(uid=bill))
(&(|(uid=jack)(uid=jill))(objectclass=posixAccount))

How do I match 3 attributes?

This gets a little tricky:

(&(&(objectClass=user)(objectClass=top))(objectClass=person))

Notice how we weave one query into another. For 4 attributes, this would be:

(&(&(&(objectClass=top)(objectClass=person))(objectClass=organizationalPerson))(objectClass=user))

And so on.

Matching Components of Distinguished Names

You may want to match part of a DN, for instance when you need to look for your groups in two subtrees of your server.

(&(objectClass=group)(|(ou:dn:=Chicago)(ou:dn:=Miami)))

will find groups with an OU component of their DN which is either 'Chicago' or 'Miami'.

Using 'not'

To exclude entities which match an expression, use '!'. Note that this must be represented as the entity '!' in your XML file.

(&(objectClass=group)(&(ou:dn:=Chicago)(!(ou:dn:=Wrigleyville))))

will find all Chicago groups except those with a Wrigleyville OU component.

Note the extra parentheses:

(!(<expression>))

LDAP URL

    <ldapurl> ::= "ldap://" [ <hostport> ] "/" <dn> [ "?" <attributes>
                            [ "?" <scope> "?" <filter> ] ]
    <hostport> ::= <hostname> [ ":" <portnumber> ]
    <dn> ::= a string as defined in RFC 1485
    <attributes> ::= NULL | <attributelist>
    <attributelist> ::= <attributetype>
                      | <attributetype> [ "," <attributelist> ]
    <attributetype> ::= a string as defined in RFC 1777
    <scope> ::= "base" | "one" | "sub"
    <filter> ::= a string as defined in RFC 1558

Explanations

DN
  • Distinguished name
Attribute list
  • List of attributes you want returned
Scope
  • base = base object search
  • one = one level search
  • sub = subtree search
Filter
  • Standard LDAP search filter
Examples
  • ldap://foo.bar.com/dc=bar,dc=com
  • ldap://argle.bargle.com/dc=bar,dc=com??sub?uid=barney
  • ldap://ldap.bedrock.com/dc=bar,dc=com?cn?sub?uid=barney

Definition taken from RFC1959

Procedimiento de instalaccion de servidor OpenLDAP

Instalación

apt-get install slapd ldap-utils

En la configuración del paquete use las opciones estandares.

Configuracion

geaceku:/home/admin/ldap# more addressbook.ldif
dn: ou=addressbook,dc=otrasyerbas,dc=com,dc=ar
objectClass: top
objectClass: organizationalUnit
ou: addressbook

geaceku:/home/admin/ldap# ldapadd -x -D 'cn=admin,dc=otrasyerbas,dc=com,dc=ar' -f addressbook.ldif -W
Enter LDAP Password:
adding new entry "ou=addressbook,dc=otrasyerbas,dc=com,dc=ar"

Base de datos

La base puede estar en dos formatos

  • BDB
  • HDB
  • ldbm?

La base de datos se instala inicialmente en /var/lib/ldap En este directorio vamos a encontrar tambien al archivo con el cual se configuran los parametros de la base: DB_CONFIG

Clientes de LDAP

El objetivo de esta parte es poder hacer un slapcat desde una máquina que no sea el servidor y funcione correctamente.

Para conseguir eso tendremos que intalar al menos los paquetes libldap2, ldap-utils.

Una vez instalados estos paquetes, editaremos el fichero /etc/ldap/ldap.conf. Este fichero es el de configuración del cliente LDAP, no lo tenemos que confundir confundir con el fichero slapd.conf, que es de la configuración del servidor.

El fichero podemos dejarlo así:

host 192.168.1.2
base dc=pinux,dc=info

De esa forma le decimos donde tiene que conectarse y el dc.

Ahora desde la máquina cliente podemos hacer uso del slapcat igual que antes. La configuración por defecto permite la lectura de varios campos por "todo el mundo", así que aunque no veremos la contraseña podremos ver otra información del usuario.

nsswitch

Para esta parte necesitaremos instalar el paquete libnss-ldap.

apt-get install libnss-ldap

Cuando trabajamos con el sistema (ls -l, p. ej.) normalmente vemos los nombres de los usuarios propietarios de los ficheros. En cambio guardado en el disco hay el "número" (UID) del usuario.

Para que los programas sepan el nombre que corresponde a los UID's (y otras cosas, como grupos, hosts, etc.) hacen unas llamadas a funciones de la librería GLIBC, y es esta quien "averigua" la relación.

Es en el fichero /etc/nsswitch.conf donde le decimos al sistema de donde averiguar el propietario sabiendo el UID. Normalmente contiene algo como:

passwd:         compat
group:          compat
shadow:         compat

hosts:          files dns
networks:       files

Lo que más nos interesa es la parte de passwd, group y shadow. Ahora lo dejaremos así:

passwd:         compat ldap
group:          compat ldap
shadow:         compat ldap

Por tanto cuando un programa le pide a la GLIBC "dime el nombre del usuario 1005", la GLIBC primero mira en /etc/passwd y sinó hará la consula al servidor LDAP.

Para que el nsswitch pueda hacer las consultas en el LDAP tendremos el fichero /etc/libnss-ldap.conf algo como:

host 192.168.1.2
base dc=pinux,dc=info

Es decir, la información necesaria para llegar a nuestro servidor LDAP y lanzar la consulta. Si hacemos man libnss-ldap.conf veremos las opciones que podemos ponerle (p. ej. port, ldap_versions, etc.)

Entre otras cosas, a veces necesitaremos que se haga una conexión autentificada contra el servidor. Para eso se usará la contraseña que encuentre en /etc/ldap.secret (tiene que estar con permisos 600, propietario y grupo root) Create /etc/ldap.secret with the password for rootbindn with the password on one line and then press <Enter>. There must be a blank second line. Then protect it with: chmod 0600 /etc/ldap.secret. Este password puede estar encriptado. Para hacerlo ejecutaremos slappasswd y la que nos diga la pondremos en el archivo. Por ejemplo: {SSHA}xAR4MvR0AByRx0gYCZGKeWUFAhNGZIud.

nscd

Para evitar que sea consultado el servidor LDAP cada vez que es ejecutado un comando como ls -l dentro de nuestra organización, es una buena idea configurar en nuestras estaciones de trabajo un sistema de cache para algunos datos de usuario. Mientras los datos en la cache sean lo suficiente recientes, las estaciones de trabajo utilizarán estos en vez de preguntar al servidor LDAP otra vez. El demonio servidor de cacheo de nombres (nscd) cumple exactamente esta tarea.

# apt-get install nscd

El archivo de configuración de nscd es /etc/nscd.conf.

enable-cache passwd yes
positive-tive-to-live passwd 600
negative-time-to-live passwd 20
suggested-size passwd 211
check-files passwd yes

Configurando el cliente PAM

OJO ALGO MAL HAY ACA PORQUE QUEDA MAL CONFIGURADO, PIDE EL PASSWD 2 VECES, REVISAR Y CORREGIR

La idea es que el cliente se autentifique en nuestro LDAP para eso tenemos que confirgurar el PAM de cada cliente.

Para poder configurar el cliente PAM tendremos que instalar el paquete libpam-ldap.

apt-get install libpam-ldap

Hay varios programas que pueden usar (y usan por defecto) un método de autentificación "centralizado" y por módulos llamado PAM (Pluggable Authentication Modules). Eso son unas librerias que los programas pueden soportar que sirven de "interfaz" contra varios métodos de autentificación (p. ej. LDAP)

La configuración en Debian es en el directorio /etc/pam.d/ y tenemos un fichero de configuración por cada servicio.

Si es necesario que la conexión sea con privilegios, se usará la contraseña que se encuentre en /etc/ldap.secret, y que estará con permisos 600 (como el punto anterior).

Tener la contraseña para autentificarse es necesario, con la configuración por defeto del slapd.conf, cuando el usuario root quiere cambiar la contraseña de otro usuario: si no es conexión autentificada, el servidor LDAP no le deja cambiarla. De otra forma cualquier usuario podría cambiar la contraseña de cualquier otro, solo lanzando la consulta al servidor LDAP.

Seguidamente, dejaremos el fichero /etc/pam_ldap.conf de forma parecida:

host 192.168.1.2
base dc=pinux,dc=info
ldap_version 3

rootbinddn cn=admin,dc=pinux,dc=info
# don't forget /etc/ldap.secret

Ahora ya tenemos la configuración general de PAM para funcionar con LDAP. Pasemos a la parte específica de cada servicio (ssh, su, passwd, etc.). Estaremos tocando los ficheros de configuración del cliente para que se autentifique contra el servidor.

ssh

Tenemos que ir al fichero /etc/pam.d/ssh y al menos añadir esas líneas:

auth       sufficient   pam_ldap.so
account    sufficient   pam_ldap.so
session    sufficient   pam_ldap.so
password   sufficient   pam_ldap.so

al inicio del fichero.

Hace falta reinicir el servicio ssh

/etc/init.d/ssh restart

Lo dejo por las dudas, pero ya no hace falta

  • En el caso del ssh tendremos seguramente que modificar en el fichero /etc/ssh/sshd_configel parámetro PAMAuthenticationViaKbdInt a yes. De otra forma no autentificaría de forma correcta.

su

Sirve para poder ejecutar el su con usuarios que están dados de alta en el LDAP.

En el fichero /etc/pam.d/su lo dejaremos parecido a:

auth       sufficient pam_rootok.so
auth       sufficient pam_ldap.so
auth       required   pam_unix.so use_first_pass

account    sufficient pam_ldap.so
account    required   pam_unix.so

session    sufficient pam_ldap.so
session    required   pam_unix.so

passwd

Este es para permitir cambiar las contraseñas de los usuarios. Lo podemos dejar así:

password        sufficient      pam_ldap.so
password        required        pam_unix.so nullok obscure min=4 max=8

Es bastante útil dar de alta a los usuarios de forma normal y cambiarles la contraseña con el mismo passwd como root (si son pocos usuarios de pruebas, claro)

login

Lo dejaremos parecido a este:

auth        required      pam_nologin.so
auth        sufficient    pam_ldap.so
auth        sufficient    pam_unix.so shadow use_first_pass
auth        required      pam_deny.so

Hay otras maneras de dejarlo, pero tenemos que ir con cuidado con el use_first_pass: si no lo ponemos los usuarios que estan dados de alta en LDAP se les pediría dos veces la contraseña (una validaría con /etc/passwd, y al no encontrar el usuario se lo pediría otra vez para validarlo contra LDAP.

Con los ejemplos vistos hasta ahora sería fácil hacer lo mismo en otros servicios (p. ej. proftpd, xlock, etc.)

También es relativamente fácil modificar los ficheros common-account common-auth common-password common-session para no tener que tocar el fichero de cada servicio, a costa de tener menos "personalización" por servicio.


Integracion con otros sistemas

Palm

http://www.yolinux.com/TUTORIALS/LinuxTutorialLDAPandPalmPilotAddressBook.html


Migracion a LDAP

migrationtools

The MigrationTools are a set of Perl scripts for migrating users, groups, aliases, hosts, netgroups, networks, protocols, RPCs, and services from existing nameservices (flat files, NIS, and NetInfo) to LDAP.

Website: http://www.padl.com/OSS/MigrationTools.html

dbd and hdb backends comparison

  • hdb as sibling of bdb with the only significant difference of being able to rename (sub)trees, and thus move them around.
  • Then there is one config difference: idlcachesize shoud be 4 x cachesize when using hdb. For bdb it can be way smaller than cachesize.
  • One of main issues with hdb compared to bdb I see is the initial caching that needs to be done on large databases for it to get performant.


Update scripts

Add group member

If "net rpc vampire" does not import group member use this procedure to fix it

Create on NT4 a member.bat script with

net group group1
net group group2
net group groupN

Execute

member.bat > members.txt

Copy members.txt to linux box and run:

import fileinput
import ldap
import ldap.modlist as modlist

dom1 = ldap.initialize("ldap://127.0.0.1:389/")
dom1.simple_bind_s("cn=admin,dc=midominio,dc=com,dc=ar","mipassword")

miembros_zone = 0

for line in fileinput.input( "members.txt" ):

  if ( line.startswith("Group name") ):
    grupo = line[15:].rstrip()
    print "Procesando grupo: %s" % grupo

  elif ( line.startswith("--------------") ):
    miembros_zone = 1

  elif ( line.startswith("The command completed successfully") ):
    miembros_zone = 0

  elif (miembros_zone == 1):
    line_miembros = line.split()

    for miembro in line_miembros:

      if not miembro.endswith("$"):
        dn="cn=%s,ou=groups,dc=midominio,dc=com,dc=ar" % grupo

        try:
          print "  Agregando: %s" % miembro
          modlist = [(ldap.MOD_ADD,'memberuid',miembro)]
          dom1.modify_s(dn,modlist)

        except ldap.TYPE_OR_VALUE_EXISTS:
          print "  Ya existe %s" % miembro

dom1.unbind_s()

Create givenName, sn and cn from displayName

import ldap
import ldap.modlist as modlist

dom1 = ldap.initialize("ldap://127.0.0.1:389/")
dom1.simple_bind_s("cn=admin,dc=midominio,dc=com,dc=ar","mipassword")

baseDN = "ou=people,dc=midominio,dc=com,dc=ar"
searchScope = ldap.SCOPE_SUBTREE
ldap_result_id = dom1.search(baseDN, searchScope)

result_set = []

while 1:
  result_type, result_data = dom1.result(ldap_result_id, 0)

  if (result_data == []):
    break

  else:
    if result_type == ldap.RES_SEARCH_ENTRY:

      if 'displayName' in result_data[0][1].keys():

        dn = result_data[0][0]
        cn = result_data[0][1]['cn'][0]
        sn = result_data[0][1]['sn'][0]
        displayName = result_data[0][1]['displayName'][0]
        givenName = result_data[0][1]['givenName'][0]
        uid = result_data[0][1]['uid'][0]

        if cn == uid :
          nombres = displayName.split()
          cant_nom = len(nombres)

          if (cant_nom == 1):
            newGivenName = nombres[0]
            newSn = nombres[0]
          elif (cant_nom == 2):
            newGivenName = nombres[0]
            newSn = nombres[1]
          elif (cant_nom == 3):
            newGivenName = "%s %s" % (nombres[0], nombres[1])
            newSn = nombres[2]
          elif (cant_nom == 4):
            newGivenName = "%s %s" % (nombres[0], nombres[1])
            newSn = "%s %s" % (nombres[2], nombres[3])
          else:
            newGivenName = "%s %s" % (nombres[0], nombres[1])
            newSn = "%s %s %s" % (nombres[2], nombres[3], nombres[4])

          old = {'cn':cn, 'givenName':givenName, 'sn':sn}
          new = {'cn':displayName, 'givenName':newGivenName, 'sn':newSn}

          ldif = modlist.modifyModlist(old,new)

          dom1.modify_s(dn,ldif)

        else:
          print "No se cambiara %s" % displayName

Create ou using description

Useful for NT4 migrated domains

import ldap
import ldap.modlist as modlist

dom1 = ldap.initialize("ldap://127.0.0.1:389/")
dom1.simple_bind_s("cn=admin,dc=midominio,dc=com,dc=ar","pass")

baseDN = "ou=people,dc=midominio,dc=com,dc=ar"
searchScope = ldap.SCOPE_SUBTREE
ldap_result_id = dom1.search(baseDN, searchScope)

result_set = []

while 1:
  result_type, result_data = dom1.result(ldap_result_id, 0)

  if (result_data == []):
    break

  else:
    if result_type == ldap.RES_SEARCH_ENTRY:

      if 'description' in result_data[0][1].keys():

        dn = result_data[0][0]
        description = result_data[0][1]['description'][0]

        old = {}
        new = {'ou':description}
        ldif = modlist.modifyModlist(old,new)

        dom1.modify_s(dn,ldif)

Problemas

Error: invalid structural object class chain (inetOrgPerson/account)

slapadd: dn="uid=PC001$,ou=computers,dc=midominio,dc=com,dc=ar" (line=5838): (65) invalid structural object class chain (inetOrgPerson/account)

Me apareció este error al querer importar un ldif sacado de un servidor que contiene una base de datos de un NT importado usando "net rpc vampire", se debe a que las "ou=computers" no puede tener las clases inetOrgPerson a la vez que la account:

Para resolver arme un pequeño script en python que remueve estas clases:

import fileinput
computer = 0

for line in fileinput.input( "slapcat.ldif" ):
  if ( "ou=computers,dc=midominio,dc=com,dc=ar" in line ):
    computer = 1
  elif ( line.startswith("# id=") ):
    computer = 0
  elif ((computer == 1) & (("objectClass: inetOrgPerson" in line) | ("objectClass: organizationalPerson" in line))):
    continue

  print line,

Mensajes de error

geaceku:/home/admin/ldap# ldapadd -x -D 'cn=admin,dc=otrasyerbas,dc=com,dc=ar' -f directory.ldif -W
Enter LDAP Password:
adding new entry "dc=otrasyerbas,dc=com,dc=ar"
ldapadd: update failed: dc=otrasyerbas,dc=com,dc=ar
ldap_add: Insufficient access (50)

geaceku:/home/admin/ldap# ldapwhoami
ldap_sasl_interactive_bind_s: No such attribute (16)
geaceku:/home/admin/ldap# ldapwhoami -x
anonymous
geaceku:/home/admin/ldap#

En sarge arrancando slapd

geacequ:/# /etc/init.d/slapd restart
Stopping OpenLDAP: slapd.
Starting OpenLDAP: (db4.2_recover not found),  slapd.

Es un problema de dependencia de sarge, se arregla con:

apt-get install db4.2-util

Evolution

Error loading addressbook

We were unable to open this addressbook.  This either means you have entered an incorrect URI, or the LDAP server is unreachable.

Aparentemente es un bug de la version slapd version 2.1.30 que solo se manifiesta con Evolution :( aparentemente la unica solicion es: upgrade/downgrade

Referencias

Main references

Other references

Introducción a LDAP en español

Introducción en Ingles

Procedimientos

Como Addressbook

Configuracion

Integracion Open LDAP y Samba

Replicación

Performance

Herramientas