En uno de los últimos artículos hablábamos de cómo podemos hacer que nuestra Ubuntu sea más segura siguiendo las buenas prácticas reconocidas por los estándares internacionales. Si bien en ese mismo texto explicábamos como gestionar los usuarios locales, no valoramos cómo realizar dicha gestión cuando disponemos de una red con más de un servidor.
Con el objetivo de tener un mayor control sobre los usuarios definidos y una política de contraseñas homogénea, lo ideal en una red es disponer de un servicio centralizado de autenticación. Imaginaros que disponemos de una red con 20 servidores y necesitásemos dar de alta un nuevo usuario, deberíamos ejecutar las pertinentes modificaciones en los 20 diferentes entornos con el riesgo de errores e inconsistencias que ésto implica. Por otra parte, desde la perspectiva del usuario nos encontramos con 20 contraseñas que no tienen porque estar sincronizadas, diferentes políticas, etc… al final es más que probable que el usuario utilice contraseñas débiles o que las anote en un postit pegado al monitor.
Por esos motivos, desde el punto de vista de la seguridad, es recomendable disponer de un servicio centralizado de autenticación. Históricamente en el mundo Unix se ha utilizado NIS (Network Information Service), pero actualmente ya se encuentra en desuso y se recomienda LDAP por tratarse de un sistema más moderno y seguro.
Entre las implementaciones de LDAP más conocidas tenemos el Directorio Activo de Windows y OpenLDAP para sistemas Linux/Unix (también puede integrarse con clientes Windows). Veamos como podemos montar un servicio centralizado de autenticación con OpenLDAP que cumpla los siguientes requisitos:
- Autenticación contra un único punto centralizado de la red.
- Conexiones cifradas con TLS.
- Identificación de estaciones mediante certificados digitales (únicamente podrán conectarse al servidor los clientes que dispongan de un certificado creado por nosotros y viceversa)
- Política de contraseñas homogénea y robusta.
- Los usuarios definidos en OpenLDAP dispondrán de campos donde se especificará a que máquinas tienen acceso. La autorización a que servicios de cada máquina se delegará a cada servidor para no perder flexibilidad.
- Cuando el usuario se autentica por primera vez en un sistema, se almacenan sus credenciales en una cache local, de forma que si el servidor de LDAP cae temporalmente, el usuario puede seguir conectándose.
Entidad certificadora SSL
Cómo comentabamos en la introducción, vamos a configurar LDAP para que se realicen conexiones cifradas. Para ésto tenemos la opción de generar un certificado SSL auto-firmado:
mkdir /etc/ldap/ssl cd /etc/ldap/ssl openssl req -newkey rsa:1024 -x509 -nodes -out server.pem -keyout server.pem -days 3650
Con este certificado únicamente podemos cifrar las conexiones pero no identificar los clientes o el servidor. Por tanto, cualquiera ordenador de la red puede conectarse al servidor sin que éste pueda verificar si se trata de un cliente autorizado, o a la inversa, los clientes no pueden verificar que se estan conectando al servidor autorizado y por ejemplo podrían enviar información confidencial (p.ej. contraseñas).
Para evitar esa situación, la configuración más segura consiste en crear nuestra propia Autoridad Certificadora (CA) y emitir tantos certificados como necesitamos (1 por cliente + el servidor) firmados por la misma. Así podremos especificar que el servidor y el cliente solo acepten conexiones con certificados firmados por nuestra Autoridad Certificadora:
mkdir /etc/ssl/marblestationCA mkdir /etc/ssl/marblestationCA/certs mkdir /etc/ssl/marblestationCA/private echo "01" > /etc/ssl/marblestationCA/serial touch /etc/ssl/marblestationCA/index.txt
Creamos “/etc/ssl/marblestationCA/caconfig.cnf”:
# My sample caconfig.cnf file. # # Default configuration to use when one is not provided on the command line. # [ ca ] default_ca = local_ca # # # Default location of directories and files needed to generate certificates. # [ local_ca ] dir = /etc/ssl/marblestationCA certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial # # # Default expiration and encryption policies for certificates. # default_crl_days = 3650 default_days = 1825 default_md = md5 # policy = local_ca_policy x509_extensions = local_ca_extensions # # # Default policy to use when generating server certificates. The following # fields must be defined in the server certificate. # [ local_ca_policy ] commonName = supplied stateOrProvinceName = supplied countryName = supplied emailAddress = supplied organizationName = supplied # # # x509 extensions to use when generating server certificates. # [ local_ca_extensions ] #subjectAltName = DNS:host.ejemplo.com,DNS:xenhost.ejemplo.com basicConstraints = CA:false #nsCertType = server # # # The default root certificate generation policy. # [ req ] default_bits = 2048 default_keyfile = /etc/ssl/marblestationCA/private/cakey.pem default_md = md5 # prompt = no distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions # # # Root Certificate Authority distinguished name. Change these fields to match # your local environment! # [ root_ca_distinguished_name ] commonName = Marble Station Root Certificate Authority stateOrProvinceName = Catalunya countryName = ES emailAddress = root@host.ejemplo.com organizationName = Marble Station organizationalUnitName = Host # [ root_ca_extensions ] basicConstraints = CA:true
A continuación creamos las claves que usará el CA, la contraseña que establezcamos ahora será solicitada cada vez que queramos firmar un nuevo certificado:
OPENSSL_CONF=/etc/ssl/marblestationCA/caconfig.cnf openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 3650
Este comando generará el certificado “/etc/ssl/marblestationCA/cacert.pem” y la clave privada de CA “/etc/ssl/marblestationCA/private/cakey.pem”.
A continuación crearemos una clave para el servidor que ofrece el servicio LDAP y otro para un cliente, para ello creamos el fichero “/etc/ssl/marblestationCA/host-server.cnf”:
# # desktop-server.cnf # [ req ] prompt = no distinguished_name = server_distinguished_name [ server_distinguished_name ] commonName = host.ejemplo.com stateOrProvinceName = Catalunya countryName = ES emailAddress = root@host.ejemplo.com organizationName = Marble Station organizationalUnitName = Host
NOTA: Es muy importante tener en cuenta que el dominio que indiquemos en “commonName” debe coincidir con el dominio de la máquina que va a utilizar el certificado, de lo contrario las conexiones serán rechazadas. Por ejemplo, algo tan sencillo como un servidor con 2 dominios ‘uno.com’ y ‘dos.com’ apuntando a la misma IP, si el certificado se genera indicando ‘uno.com’, los clientes que se conecten deben hacerlo mediante ese dominio y no ‘dos.com’. A pesar de apuntar a la misma IP, OpenLDAP verificará el dominio utilizado.
Generamos la clave sin contraseña (argumento “-nodes”) dado que de lo contrario el servicio no podría arrancar de forma automática:
OPENSSL_CONF=/etc/ssl/marblestationCA/host-server.cnf openssl req -newkey rsa:1024 -keyout host-server_key.pem -keyform PEM -out host-server_req.pem -outform PEM -nodes
Utilizamos la petición de firma para el CA que acabamos de crear (“host-server_req.pem”) para generar el certificado final firmado:
OPENSSL_CONF=/etc/ssl/marblestationCA/caconfig.cnf openssl ca -in host-server_req.pem -out host-server_crt.pem
A continuación podemos repetir el proceso para generar más certificados firmados por el CA, creando un nuevo fichero de configuración (p.ej. “/etc/ssl/marblestationCA/desktop-server.cnf”) y repitiendo los pasos anteriores:
OPENSSL_CONF=/etc/ssl/marblestationCA/desktop-server.cnf openssl req -newkey rsa:1024 -keyout desktop-server_key.pem -keyform PEM -out desktop-server_req.pem -outform PEM -nodes OPENSSL_CONF=/etc/ssl/marblestationCA/caconfig.cnf openssl ca -in desktop-server_req.pem -out desktop-server_crt.pem
Servidor OpenLDAP
En el apartado anterior hemos preparado los certificados que vamos a utilizar, ahora ha llegado la hora de instalar el servidor OpenLDAP:
apt-get install slapd ldap-utils db4.2-util apt-get install libpam-ldap
Los certificados de la anterior sección, concretamente el certificado que será utilizado por el servidor y el certificado del CA los copiaremos a su lugar correspondiente:
sudo -s mkdir /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/host-server_key.pem /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/host-server_crt.pem /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/cacert.pem /etc/ldap/ssl/ chown openldap:openldap /etc/ldap/ssl/* chmod 640 /etc/ldap/ssl/*
Copiamos el esquema que nos permitirá indicar a que hosts se pueden conectar los usuarios (atributos ‘host’, que pueden contener el hostname de las máquinas o un ‘*’ para permitir acceso a todas):
cp /usr/share/doc/libpam-ldap/ldapns.schema /etc/ldap/schema/
A continuación editamos la configuración del servidor ‘/etc/ldap/slapd.conf’:
# Schema and objectClass definitions include /etc/ldap/schema/core.schema include /etc/ldap/schema/cosine.schema include /etc/ldap/schema/nis.schema include /etc/ldap/schema/inetorgperson.schema include /etc/ldap/schema/misc.schema include /etc/ldap/schema/ppolicy.schema include /etc/ldap/schema/ldapns.schema # Where the pid file is put. The init.d script # will not stop the server if you change this. pidfile /var/run/slapd/slapd.pid # List of arguments that were passed to the server argsfile /var/run/slapd/slapd.args # Read slapd.conf(5) for possible values loglevel none # Where the dynamically loaded modules are stored modulepath /usr/lib/ldap moduleload back_hdb moduleload ppolicy.la # The maximum number of entries that is returned for a search operation sizelimit 500 # The tool-threads parameter sets the actual amount of cpu's that is used # for indexing. tool-threads 1 backend hdb database hdb # The base of your directory in database #1 suffix "dc=marblestation,dc=com" overlay ppolicy ppolicy_default "cn=Standard,ou=Policies,dc=marblestation,dc=com" ppolicy_use_lockout # rootdn directive for specifying a superuser on the database. This is needed # for syncrepl. rootdn "cn=admin,dc=marblestation,dc=com" # Where the database file are physically stored for database #1 directory "/var/lib/ldap" index uid eq # For the Debian package we use 2MB as default but be sure to update this # value if you have plenty of RAM dbconfig set_cachesize 0 2097152 0 # Sven Hartge reported that he had to set this value incredibly high # to get slapd running at all. See http://bugs.debian.org/303057 for more # information. # Number of objects that can be locked at the same time. dbconfig set_lk_max_objects 1500 # Number of locks (both requested and granted) dbconfig set_lk_max_locks 1500 # Number of lockers dbconfig set_lk_max_lockers 1500 # Indexing options for database #1 index objectClass eq # Save the time that the entry gets modified, for database #1 lastmod on # Checkpoint the BerkeleyDB database periodically in case of system # failure and to speed slapd shutdown. checkpoint 512 30 # The userPassword by default can be changed # by the entry owning it if they are authenticated. # Others should not be able to see it, except the # admin entry below # These access lines apply to database #1 only # #,shadowLastChange access to attrs=userPassword by dn="cn=admin,dc=marblestation,dc=com" write by anonymous auth by self write by * none access to attrs=shadowLastChange by self write by * read # Ensure read access to the base for things like # supportedSASLMechanisms. Without this you may # have problems with SASL not knowing what # mechanisms are available and the like. # Note that this is covered by the 'access to *' # ACL below too but if you change that as people # are wont to do you'll still need this if you # want SASL (and possible other things) to work # happily. access to dn.base="" by * read # The admin dn has full write access, everyone else # can read everything. access to * by dn="cn=admin,dc=marblestation,dc=com" write by * read TLSCertificateFile /etc/ldap/ssl/host-server_crt.pem TLSCertificateKeyFile /etc/ldap/ssl/host-server_key.pem TLSCACertificateFile /etc/ldap/ssl/cacert.pem TLSVerifyClient demand
En este fichero, respecto al original, se han realizado la siguientes modificaciones:
- Se han añadido nuevos esquemas mediante el comando ‘include’.
- Se realiza la carga del módulo ‘ppolicy’ y se establece la configuración básica.
- Se especifica el sufijo que será utilizado para la identificación de los datos que contendrá LDAP (“dc=marblestation,dc=com”)
- Se establece el usuario administrador mediante el comando ‘rootdn’ (teniendo en cuenta el mismo sufijo que hemos especificado)
- Se configuran los certificados TLS para que estos sean requeridos y validados.
Por otra parte, en el fichero ‘/etc/default/slapd’ activaremos el servicio SSL y Unix sockets:
SLAPD_SERVICES="ldaps:/// ldapi:///"
Con esto ya podemos reiniciar nuestro servidor OpenLDAP:
/etc/init.d/slapd restart
Ahora nos falta definir la estructura base de los datos internos.
Estructura del directorio
La información en LDAP se almacena mediante objetos que son construidos a partir de clases definidas en los esquemas (éstos se cargan automáticamente en base a las ‘includes’ del fichero de configuración de slapd). Veamos una definición de ejemplo:
dn: cn=admin,dc=marblestation,dc=com objectClass: organizationalRole objectClass: simpleSecurityObject cn: admin description: LDAP administrator userPassword: {SSHA}HHWw45HYVamEIUFHp1emGejTkbY1bntB
Elementos de la definición anterior:
- En primer lugar tenemos el nombre unívoco (Distinguished Name) del objeto: “cn=admin,dc=marblestation,dc=com”. Siempre acompañado del sufijo que hemos especificado en la configuración (en este caso “dc=marblestation,dc=com”).
- El objeto tendrá los atributos de las clases ‘organizationalRole’ y ‘simpleSecurityObject’. Estas clases se encuentran definidos en los esquemas ‘/etc/ldap/schema/’ que son cargados por LDAP al iniciarse. Un objeto puede estar compuesto por tantas clases auxiliares (p.ej. simpleSecurityObject) y abstractas (p.ej. top) como se requiera pero solo puede haber una de tipo estructural (organizationalRole en este caso).
- Finalmente se listan los atributos, en este caso ‘cn’, ‘description’ y ‘userPassword’.
Todos los objetos en LDAP siguen siempre esa estructura, la cual dista bastante de las típicas bases de datos relacionales (SQL). Otra diferencia importante respecto a esas BBDD es que LDAP está optimizado para realizar consultas de lectura muy rápidas, por eso LDAP se utiliza en entornos que utilizan datos que no van a ser modificados frecuentemente (a diferencia de una BBDD relacional como MySQL).
Pasemos a construir la estructura básica de nuestro servidor LDAP, nos situamos en un directorio temporal de trabajo y creamos el fichero ‘structure.ldif’:
##### Raiz dn: dc=marblestation,dc=com objectClass: dcObject objectClass: organizationalUnit dc: marblestation ou: marblestation Dot Com ##### Administrador de LDAP dn: cn=admin,dc=marblestation,dc=com objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword: {SSHA}HHWw45HYVamEIUFHp1emGejTkbY1bntB ## Password genered with slappasswd ##### Usuarios dn: ou=People,dc=marblestation,dc=com objectClass: organizationalUnit ou: people ##### Grupos dn: ou=Group,dc=marblestation,dc=com objectClass: organizationalUnit ou: group ##### Politicas de contraseñas dn: ou=Policies,dc=marblestation,dc=com objectClass: organizationalUnit ou: policies
En ese fichero hemos definido la raiz donde colgarán todos los datos, el administrador del sistema LDAP y unidades organizativas donde añadiremos los usuarios, los grupos y las políticas de contraseña.
Cabe destacar que todas las contraseñas que necesitemos para definir la estructura las generaremos mediante el comando ‘slappasswd’ (por ejemplo, la del usuario administrador anterior):
$ slappasswd New password: Re-enter password: {SSHA}d2BamRTgBuhC6SxC0vFGWol31ki8iq5m
Continuemos con el contenido del servidor LDAP. Añadiremos una política de contraseñas que definiremos en el fichero ‘policy.ldif’:
##### Política estándard dn: cn=Standard,ou=Policies,dc=marblestation,dc=com objectClass: top objectClass: device objectClass: pwdPolicy cn: Standard # Field were password is stored: pwdAttribute: userPassword #### Expiration rules # Number of seconds that must elapse between modifications allowed to the password: # 1 day pwdMinAge: 86400 # 60 days pwdMaxAge: 5184000 # 7 days pwdExpireWarning: 604800 # if its value is zero (0), users with expired passwords will not be allowed to authenticate to the directory. pwdGraceAuthnLimit: 0 # User must change its password after an admin has set it pwdMustChange: TRUE # Users can change their password pwdAllowUserChange: TRUE # It is needed the old password to set a new one # NOTA: Si lo activamos no podremos cambiar la contraseña mediante pam_ldap pwdSafeModify: FALSE ##### Password quality # Maximum number of used passwords that will be stored in the pwdHistory attribute: pwdInHistory: 12 # If its value is one (1), the server will check the syntax, and if the # server is unable to check the syntax, whether due to a client-side # hashed password or some other reason, it will be accepted. If its value # is two (2), the server will check the syntax, and if the server is # unable to check the syntax it will return an error refusing the password. pwdCheckQuality: 2 # Minimum number of characters pwdMinLength: 8 ##### Automatic locks # Lock user accounts after 5 failed authentications pwdMaxFailure: 5 pwdLockout: TRUE # 10 minutes pwdLockoutDuration: 600 # if its value is zero (0), the failure counter will only be reset by a successful authentication pwdFailureCountInterval: 0
Ésta política será la que aplicará a todos los usuarios (excepto al administrador LDAP) y el propio fichero es auto-explicativo.
Vamos a crear un usuario de prueba en el fichero ‘users.ldif’:
dn: uid=marble,ou=people,dc=marblestation,dc=com objectClass: account objectClass: posixAccount objectClass: top objectClass: shadowAccount objectClass: inetLocalMailRecipient objectClass: hostObject objectClass: authorizedServiceObject #objectClass: kerberosSecurityObject ####################################### ### Posix Account uid: marble cn: Sergi Blanco Cuaresma gecos: Sergi Blanco Cuaresma uidNumber: 1000 gidNumber: 1000 loginShell: /bin/bash homeDirectory: /home/marble ####################################### ### Password userPassword: {SSHA}PFIJb4fTH4klVKd5nE4J0WDUs3Rtmxtd pwdReset: TRUE #pwdPolicySubEntry: cn=Standard,ou=Policies,dc=marblestation,dc=com #pwdPolicySubEntry: cn=No Policy, ou=Policies, dc=example, dc=com ## Discarted #shadowExpire: -1 #shadowFlag: 0 #shadowMin: 1 shadowWarning: 7 shadowMax: 60 shadowLastChange: 0 #shadowMax: 99999 #shadowLastChange: 14156 ####################################### ### Kerberos #krbname: marble@EJEMPLO.COM ####################################### ### Authorization (e.g. host: desktop, authorizedService: sshd) host: * authorizedService: * ### Mail mailRoutingAddress: marble@host.ejemplo.com mailHost: host.ejemplo.com
Ésta sera la definición habitual de los usuarios de los sistemas, la mayoría de atributos corresponden con la información que habitualmente se almacena en sistemas Unix. Adicionalmente, tenemos otros atributos como ‘host’, que define a que máquinas podrá conectarse (el ‘*’ significa “a todas”, si quisiéramos especificar máquinas tendríamos que repetir el atributo tantas veces como máquinas queramos especificar).
Cabe destacar que utilizaremos los atributos shadow para replicar la política global que hemos establecido con ‘ppolicy’. Si bien podríamos ignorarlos, no nos cuesta nada indicar la misma política de contraseña para asegurarnos que se cumple bajo cualquier circunstancia (p.ej. si tenemos conexiones SSH con autenticación por claves RSA/DSA, PAM únicamente comprueba los campos shadow y por tanto ‘ppolicy’ no tiene efecto hasta que el usuario no realiza una conexión utilizando su clave normal).
Finalmente nos falta definir los grupos del sistema, añadamos un par de ejemplo en un fichero ‘groups.ldif’:
dn: cn=marble,ou=group,dc=marblestation,dc=com objectClass: posixGroup cn: marble gidNumber: 1000 dn: cn=users,ou=group,dc=marblestation,dc=com objectClass: posixGroup cn: users memberUid: marble gidNumber: 100
Ahora que ya tenemos todos los ficheros con la estructura y los datos iniciales, ha llegado el momento de cargarlos en el servidor. Para ello borraremos todos los datos actuales de LDAP (no hemos introducido nada todavia, por tanto no debería haber nada de valor) y cargamos la información:
sudo /etc/init.d/slapd stop sudo rm -rf /var/lib/ldap/* sudo slapadd -l structure.ldif sudo slapadd -l policy.ldif sudo slapadd -l users.ldif sudo slapadd -l groups.ldif sudo chown -R openldap:openldap /var/lib/ldap sudo /etc/init.d/slapd start
Si todo ha ido bien, ya tenemos nuestro servidor LDAP en marcha con la estructura base y un usuario de prueba.
Consultas locales a OpenLDAP
Para poder hacer consultas desde el propio servidor donde hemos instalado LDAP editamos ‘/etc/ldap/ldap.conf’:
BASE dc=marblestation,dc=com URI ldapi:///
De esta forma los comandos ‘ldap’ se conectaran al servidor utilizando Unix sockets y no necesitaremos configurar TLS dado que los datos no viajan por ninguna red.
Podemos probar a realizar una consulta conectando como el administrador de LDAP (nos solicitará la contraseña que le hayamos asignado en la definición del mismo) y preguntando sobre los objetos con el atributo ‘uid=marble’:
ldapsearch -x -D "cn=admin,dc=marblestation,dc=com" -W uid=marble
Si queremos evitar tener que escribir siempre el nombre del administrador, podemos crear el archivo ‘~/.ldaprc’:
BASE dc=marblestation,dc=com URI ldapi:/// BINDDN cn=admin,dc=marblestation,dc=com
Así simplemente bastará con ejecutar:
ldapsearch -x -W uid=marble
Administración LDAP y gestión de usuarios
Comandos LDAP
OpenLDAP proporciona diversos comandos para la administración del servicio:
-
ldapdelete: Borra entradas.
ldapmodrdn: Modifica RDN (Relative Distinguised Names), por ejemplo permite mover “uid=marble,ou=people,dc=marblestation,dc=com” a “uid=marble,ou=gente,dc=marblestation,dc=com”
ldapsearch: Realiza consultas.
ldapcompare: Comparaciones.
ldapmodify: Permite añadir entradas o realizar modificaciones.
ldappasswd: Establece la contraseña del usuario.
ldapwhoami: Indica el usuario con el que nos conectamos a LDAP.
Su uso no es sencillo y para aprender a utilizarlas lo mejor es consultar sus respectivos manuales. No obstante, disponemos de otras interfícies que nos facilitaran la gestión de LDAP.
phpLDAPadmin
Para la administración general del LDAP, si tenemos ya configurado Apache2, una de las opciones más cómodas es la instalación de phpldapadmin:
apt-get install phpldapadmin
Editamos ‘/etc/phpldapadmin/config.php’ y establecemos los siguientes parámetros (acceso local por Unix sockets, etc.):
... $ldapservers->SetValue($i,'server','host','ldapi://'); ... $ldapservers->SetValue($i,'server','base',array('dc=marblestation,dc=com')); ... $ldapservers->SetValue($i,'login','dn','cn=admin,dc=marblestation,dc=com'); ...
Podremos acceder a la aplicación mediante ‘http://localhost/phpldapadmin/’ y desde allí la aplicación nos permitirá realizar consultar, modificaciones, duplicar objetos, etc.
CPU – Change Passowrd Utility
Si bien con phpLDAPadmin podremos realizar casi cualquier operación sobre LDAP vía web, CPU nos proporciona comandos de consola análogos a los tradicionales ‘useradd’, ‘usermod’, ‘userdel’, ‘groupadd’, etc. Ésto puede facilitar al administrador la tarea de gestión de usuarios:
apt-get install cpu
Editamos ‘/etc/cpu/cpu.conf’ para establecer las opciones de conexión (vía local por Unix Socket), usuario administrador LDAP (no indicamos la contraseña, haremos que nos la pregunte cada vez que ejecutemos cpu) y otros parámetros:
# See cpu.conf(5) for documentation [GLOBAL] DEFAULT_METHOD = ldap CRACKLIB_DICTIONARY = /var/cache/cracklib/cracklib_dict [LDAP] LDAP_URI = ldapi:/// BIND_DN = "cn=admin,dc=marblestation,dc=com" BIND_PASS = "" USER_BASE = "ou=People,dc=marblestation,dc=com" GROUP_BASE = "ou=Group,dc=marblestation,dc=com" USER_OBJECT_CLASS = account,posixAccount,shadowAccount,top,inetLocalMailRecipient,hostObject,authorizedServiceObject #kerberosSecurityObject GROUP_OBJECT_CLASS = posixGroup,top USER_FILTER = (objectClass=posixAccount) GROUP_FILTER = (objectClass=posixGroup) USER_CN_STRING = uid GROUP_CN_STRING = cn SKEL_DIR = /etc/skel DEFAULT_SHELL = /bin/bash HOME_DIRECTORY = /home MAX_UIDNUMBER = 10000 MIN_UIDNUMBER = 1000 MAX_GIDNUMBER = 10000 MIN_GIDNUMBER = 1000 ID_MAX_PASSES = 1000 # Whether each user should have its own group created or not USERGROUPS = yes # If you change usergroup set this to the default group a user should have #USERS_GID = 100 RANDOM = "false" PASSWORD_FILE = "/etc/passfile" SHADOW_FILE = "/etc/shadowfile" HASH = "clear" SHADOWLASTCHANGE = 11192 SHADOWMAX = 99999 SHADOWWARING = 7 SHADOWEXPIRE = -1 SHADOWFLAG = 134538308 SHADOWMIN = -1 SHADOWINACTIVE = -1
Adicionalmente, crearemos el fichero ‘/etc/cpu/attributes.ldif’:
pwdReset: TRUE host: * authorizedService: *
De esta forma, cuando creemos usuarios se añadirán los atributos anteriores por defecto (el usuario esta obligado a resetear la contraseña y tiene acceso a todos los sistemas). Hagamos una prueba añadiendo un nuevo usuario (la primera contraseña que nos solicita corresponde a la del administrador LDAP):
cpu -w -a /etc/cpu/attributes.ldif useradd -p test01
El anterior comando generará el usuario ‘test01’ y el grupo ‘test01’.
Si queremos listar los usuarios definidos:
cpu -w cat
Bloqueo y desbloqueo de usuarios:
cpu -w usermod -L test01 cpu -w usermod -U test01
Borrado de usuarios:
cpu -w userdel prueba22
Para consultar el listado completo de comandos que podemos ejecutar con ‘cpu’ podemos consultar el manual:
man cpu-ldap
Clientes OpenLDAP
A continuación vamos a configurar una máquina que se conectará contra el servidor OpenLDAP para autenticar a sus usuarios:
apt-get install libpam-ldap libnss-ldap nss-updatedb libnss-db ldap-utils libpam-ccreds
Para la conexión, necesitaremos disponer de un certificado firmado por la CA que hemos generado tal y como se especificaba al inicio del artículo. Una vez generados los certificados (clave privada, clave pública firmada y certificado CA), los copiaremos mediante sftp y los moveremos al lugar correspondiente:
sudo -s mkdir /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/desktop-server_key.pem /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/desktop-server_crt.pem /etc/ldap/ssl/ cp /etc/ssl/marblestationCA/cacert.pem /etc/ldap/ssl/ chmod 644 /etc/ldap/ssl/* chown root:root /etc/ldap/ssl/*
A continuación vamos a configurar LDAP como cliente, editamos ‘/etc/ldap/ldap.conf’:
BASE dc=marblestation,dc=com URI ldaps://host.ejemplo.com TLS_CACERT /etc/ldap/ssl/cacert.pem TLS_REQCERT demmand #TLS_REQCERT never # Useful to check if "Can't contact LDAP server" because of certificate problems
Utilizando el usuario que vayamos a utilizar para conectarnos al servidor, creamos ‘~/.ldaprc’:
BASE dc=marblestation,dc=com URI ldaps://host.ejemplo.com BINDDN cn=admin,dc=marblestation,dc=com TLS_CACERT /etc/ldap/ssl/cacert.pem TLS_REQCERT demmand #TLS_REQCERT never # Useful to check if "Can't contact LDAP server" because of certificate problems #### User only (need to copy /etc/ldap/ldap.conf to ~/.ldaprc) # Client certificate and key # Use these, if your server requires client authentication. TLS_CERT /etc/ldap/ssl/desktop-server_crt.pem TLS_KEY /etc/ldap/ssl/desktop-server_key.pem
En esos dos ficheros hemos especificado:
- El nombre base “dc=marblestation,dc=com”.
- El servidor al que nos vamos a conectar y el protocolo que vamos a utilizar, en este caso nos conectaremos mediante SSL al puerto 636 LDAP del servidor ‘host.ejemplo.com’. Es muy importante que este dominio coincida con el ‘commonName’ especificado en el servidor al cual nos vamos a conectar, de lo contrario la conexión no se establecerá.
- El usuario con el que vamos a realizar la conexión (corresponde al administrador).
- Certificado privado, público firmado y CA, así como la solicitud de validación del certificado del servidor.
A continuación podemos probar la conexión realizando una consulta al servidor:
ldapsearch -x -W uid=marble
Ahora que ya hemos podido comprobar que podemos conectarnos sin problemas desde el cliente, vamos a configurar el sistema para que autentique los usuarios contra LDAP.
Lo primero será editar el fichero ‘/etc/ldap.conf’ para establecer los parámetros de conexión utilizados por PAM (sistema habitual de autenticación):
# Your LDAP server. Must be resolvable without using LDAP. uri ldaps://host.ejemplo.com # The distinguished name of the search base. base dc=marblestation,dc=com ldap_version 3 # The distinguished name to bind to the server with # if the effective user ID is root. Password is # stored in /etc/ldap.secret (mode 600) # NOTA: No activar o dejará de ser efectiva la politica de contraseñas (ppolicy) #rootbinddn cn=admin,dc=marblestation,dc=com # Search timelimit timelimit 5 # Bind/connect timelimit bind_timelimit 5 # Reconnect policy: hard (default) will retry connecting to # the software with exponential backoff, soft will fail # immediately. bind_policy soft # Filter to AND with uid=%s #pam_filter objectclass=posixAccount #pam_filter gidNumber=1000 # check for login rights on the host pam_check_host_attr yes #pam_filter (&(objectClass=posixAccount)(authorizedService=site-admin)) #pam_filter |(host=desktop)(host=\*) #pam_check_service_attr yes pam_lookup_policy yes # Specify a minium or maximum UID number allowed #pam_min_uid 1000 #pam_max_uid 0 # Do not hash the password at all; presume # the directory server will do it, if # necessary. This is the default. #pam_password md5 pam_password clear # OpenLDAP SSL mechanism # start_tls mechanism uses the normal LDAP port, LDAPS typically 636 ssl start_tls ssl on # OpenLDAP SSL options # Require and verify server certificate (yes/no) tls_checkpeer yes # tls_checkpeer no # Useful to check if "Can't contact LDAP server" because of certificate problems # CA certificates for server certificate verification #tls_cacertdir /etc/ssl/certs tls_cacertfile /etc/ldap/ssl/cacert.pem # Client certificate and key tls_cert /etc/ldap/ssl/desktop-server_crt.pem tls_key /etc/ldap/ssl/desktop-server_key.pem use_sasl off rootuse_sasl off sasl_secprops maxssf=0 idle_timelimit 3600 nss_reconnect_tries 1 nss_reconnect_sleeptime 1 nss_reconnect_maxsleeptime 8 nss_reconnect_maxconntries 2 nss_paged_results yes nss_base_passwd ou=people,dc=marblestation,dc=com?one nss_base_shadow ou=people,dc=marblestation,dc=com?one nss_base_group ou=group,dc=marblestation,dc=com?one nss_initgroups_ignoreusers avahi,avahi-autoipd,backup,bin,chipcard,daemon,dhcp,ftp,games,gdm,gnats,haldaemon,hplip,irc,klog,landscape,libuuid,list,lp,mail,man,messagebus,news,nx,polkituser,proxy,pulse,root,snmp,sshd,sync,sys,syslog,uucp,www-data
En este fichero hemos especificado:
- Host al que nos conectaremos. Como siempre, es importante que coincida con el ‘commonName’ del certificado del servidor, de lo contrario la conexión no se establecerá.
- Opciones de conexión varias (el puerto 636 corresponde a ldaps://).
- Configuraciones PAM:
- Validación del atributo ‘host’. Si el usuario dispone de un atributo ‘host’ con valor ‘*’ o con el nombre de la máquina (debe coincidir con el resultado del comando ‘hostname’), entonces podrá logearse en el sistema. De lo contrario, se deniega el acceso.
- Validación de la política de contraseñas.
- Envío de contraseñas en claro. Este requisito es necesario para que la política de contraseñas pueda ser validada en el servidor y por otra parte, tampoco supone un riesgo de seguridad dado que la conexión será cifrada.
- Configuración de los certificados a utilizar.
- Rutas donde encontrar la información de usuarios, grupos, etc.
A continuación vamos a preparar el sistema para que si perdemos conectividad, podamos seguir asociando uid y gid con los nombres asignados. Para esto se copiaran todos los usuarios y grupos (solo uid/gid y nombres) en local mediante el comando:
sudo nss_updatedb ldap
Es necesario que esta actualización se haga de forma periódica mediante cron (al menos 1 vez al día) por si hay cambios en el servidor:
echo '#!/bin/sh' | sudo tee /etc/cron.daily/upd-local-nss-db echo `which nss_updatedb` ldap | sudo tee -a /etc/cron.daily/upd-local-nss-db sudo chmod +x /etc/cron.daily/upd-local-nss-db
En este punto estamos preparados para indicarle al sistema de donde debe obtener la información sobre usuarios, contraseñas y grupos, para ello editamos ‘/etc/nsswitch.conf’ y cambiamos:
#passwd: compat #group: compat #shadow: compat
Por:
passwd: files ldap [NOTFOUND=return] db group: files ldap [NOTFOUND=return] db shadow: files ldap
En estas líneas estamos indicando que el sistema debe buscar la información de usuarios y grupos primero en los archivos locales (/etc/passwd y /etc/group) y después en LDAP, pero en caso de que este último falle entonces debe consultar la copia local que hemos realizado con ‘nss_updatedb’. En el caso de las contraseñas, únicamente consultará el fichero local (‘/etc/shadow’) y el servidor (el cacheo de credenciales lo realizaremos por otra vía).
Con esta configuración tendremos máxima flexibilidad dado que podremos optar por tener usuario/grupos locales y usuarios/grupos centralizados de forma simultanea. O bien, podemos migrar todas las cuentas a LDAP y dejar en blanco los archivos locales.
Para validar la configuración, podemos comprobar el listado de usuarios/grupos actuales de nuestro sistema (combinará los definidos localmente con los usuarios creados en LDAP):
sudo getent passwd sudo getent group
Finalmente nos falta configurar los archivos de autenticación PAM.
NOTA: Si estamos accediendo remotamente al servidor que estamos modificando, es importante tener en cuenta que estos cambios pueden bloquearnos futuros accesos y por tanto debemos probar cada cambio que hagamos.
Comenzaremos editando ‘/etc/pam.d/common-auth’ para que quede con el siguiente contenido:
auth [success=done default=ignore] pam_unix.so nullok_secure try_first_pass # If LDAP is unavailable, go to next line. If authentication via LDAP is successful, skip 1 line. # If LDAP is available, but authentication is NOT successful, skip 2 lines. auth [authinfo_unavail=ignore success=1 default=2] pam_ldap.so use_first_pass auth [default=done] pam_ccreds.so action=validate use_first_pass auth [default=done] pam_ccreds.so action=store auth [default=bad] pam_ccreds.so action=update
En este fichero hemos indicado que primero debe intentar autenticar los usuarios utilizando la información local, de lo contrario trata de autenticar contra el servidor LDAP (utilizando la configuración ‘/etc/ldap.conf’ que ya hemos preparado). Si este último falla, comprobará si las credenciales se encuentran cacheadas en local e intentará autenticarlo (antepenúltima línea).
Por otra parte, cada vez que se realice una autenticación exitosa contra LDAP, se ejecutará la penúltima línea y se guardarán las credenciales en local (concretamente en /var/cache/.security.db). En caso que la cuenta haya sido bloqueada/eliminada de LDAP, se ejecutará la última línea con la cual se borraran las credenciales cacheadas del usuario.
Editemos ahora ‘/etc/pam.d/common-account’ para que quede con el siguiente contenido:
account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_ldap.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_unix.so account required pam_permit.so
Editamos ‘/etc/pam.d/common-session’ y lo dejamos así:
session required pam_limits.so #session required pam_mkhomedir.so skel=/etc/skel/ session required pam_unix.so session optional pam_ldap.so
Editamos ‘/etc/pam.d/common-password’ para que contenga:
password required pam_ldap.so ignore_unknown_user password optional pam_unix.so shadow md5 try_first_pass
A continuación podemos probar a autenticarnos en el sistema (login normal, ssh o ‘su – usuario’) utilizando usuarios locales y usuarios definidos en LDAP (p.ej. el que hemos creado de prueba).
Cuando conectemos con el usuario de LDAP, las credenciales han debido ser cacheadas y podemos validarlo mediante:
sudo cc_dump Credential Type User Service Cached Credentials ---------------------------------------------------------------------------------- Salted SHA1 fred any 4a985b233701cf106ed450a0168fa8e0aa86ef5d
En caso de que queramos borrar manualmente alguna credencial cacheada, podemos ejecutar:
sudo cc_test -update any usuario -
Con esto ya tenemos montado el sistema de autenticación centralizado cliente-servidor 🙂
calla, calla, aún en las noches de tormenta tengo pesadillas con la configuración, la estabilidad y la integración del openldap usando mysql de backend…como escarpias tengo los pelos sólo de recordarlo…
Excelente… pero una consulta.. como autentificas clientes windows??, con un cliente ldap, parecido al cliente novell???
La verdad que te lo agradezco enormemente!!
Muy muy buen tutorial.
Si la agregas sobre como configurar mit krb5 sería un excelente tutorial de SSO en linux.
Saludos!.
Muy buena la web, la explicacion esta muy bien descrita.
Hay un error… para que funcione correctamente el cpu, en el cpu.conf hay que añadir:
USER_OBJECT_CLASS = account,posixAccount,shadowAccount,top,hostObject,authorizedServiceObject
Esos dos ultimos objetos, pq si no falla el authorizedService, que he tenido que verlo con un strace pq no salian los errores por ningun sitio jeje.
Hola, he segudio éste documento para practicar con LDAP y me ha parecido muy bueno, el problema es que me encuentro atascado en la parte de cargar la información en ldap, concretamente me da el siguiente error:
/etc/ldap/ldif# slapadd -l policy.ldif
str2entry: invalid value for attributeType objectClass #2 (syntax 1.3.6.1.4.1.1466.115.121.1.38)
slapadd: could not parse entry (line=1)
_#################### 100.00% eta none elapsed none fast!
root@antonio-desktop:/etc/ldap/ldif# slapadd -l users.ldif
str2entry: invalid value for attributeType objectClass #4 (syntax 1.3.6.1.4.1.1466.115.121.1.38)
slapadd: could not parse entry (line=1)
_#################### 100.00% eta none elapsed none fast!
Closing DB…
Le he dado muchas vueltas pero no encuentro dónde está el problema. ¿ Se te ocuerre dónde puede estar el error ?
Muchas gracias,
enhorabuena por los documentos puestos en la página.
Hola de nuevo, he conseguido cargar datos al ldap, el problema con el que me enfrento ahora es que no me puedo conectar en modo seguro. Haciendo a mano me da el siguiente error:
openssl s_client -connect localhost:636 -showcerts -state -CAfile /etc/ldap/ssl/host-server_crt.pem
139667950499488:error:0200100D:system library:fopen:Permission denied:bss_file.c:169:fopen(‘/etc/ldap/ssl/host-server_crt.pem’,’r’)
139667950499488:error:2006D002:BIO routines:BIO_new_file:system lib:bss_file.c:174:
139667950499488:error:0B084002:x509 certificate routines:X509_load_cert_crl_file:system lib:by_file.c:274:
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv3 write client hello A
SSL_connect:failed in SSLv3 read server hello A
139667950499488:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:591:
—
no peer certificate available
—
No client certificate CA names sent
—
SSL handshake has read 0 bytes and written 0 bytes
—
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : SSLv3
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
Start Time: 1331591171
Timeout : 7200 (sec)
Verify return code: 0 (ok)
—
Si lo intento con phpldapadmin también me lo dice
Could not start TLS. (antonio-desktop LDAP Server)
Error: No se ha podido iniciar TLS. Por favor, revise su configuración LDAP
Una duda sobre el servidor LDAP es posible tener dos admins uno digamos es el master tiene acceso a todo el directorio ldap y otro digamos delegado que solo pueda acceder a gestionar una sola OU y no pueda salir de ese nivel.
Saludos.