Poblar un directorio LDAP desde un fichero CSV
Crear entre todos los alumnos de la clase que vayan a hacer esta tarea un fichero CSV
que incluya información personal de cada uno incluyendo los siguientes datos:
- Nombre
- Apellidos
- Dirección de correo electrónico
- Nombre de usuario
- Clave pública ssh
Añadir otro fichero con la información de las máquinas de los alumnos:
- Hostname
- IPv4
- clave pública ssh de la máquina
Añadir el esquema openssh-lpk
al directorio para poder incluir claves públicas ssh en un directorio LDAP.
Hacer un script en bash o en python que utilice el fichero como entrada y pueble el directorio LDAP con un objeto para cada alumno utilizando los ObjectClass posixAccount
e inetOrgPerson
.
Configurar el sistema para que sean válidos los usuarios del LDAP.
Configurar el servicio ssh para que permita acceder a los usuarios del LDAP utilizando las claves públicas que hay allí, en lugar de almacenarlas en .ssh/authorized_keys
, que sólo permita acceder a los equipos que estén en el LDAP en lugar del fichero .ssh/known_hosts
y que se cree el directorio "home"
al vuelo.
Fichero CSV
Debemos crear un fichero CSV con la información de los alumnos. Para ello, podemos utilizar la siguiente plantilla:
<!-- Nombre,Apellidos,Correo,Usuario,Clave pública -->
Benito,Martinez,benitoporfa@gmail.com,benito,ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDV+bGsINuRN5PH+ylMp6mHjNr7xIxqxza13dJDgzK2tUtotC1OTQJ7gH5pGpMRh90+8EwtLZUFl+pGNxPyFW6QuXW9IJygyHimrQtOrFUkSenkL/nBIVaRWErwdb7mgIpExpHMMwXDksFIUvsjD1TNVVV6mjFr9CGv2m44v9+laNSw9tRRdMQXVPsLIl52E4m035eUu3V7pjOHXB0HGQOMSRI6RYRLl3yrOaqYjq7uIuWeDsUD6krpEF+rW4DYpj5+C1exsKMlwQMAGB6EZ8e43NfDhrsgw7nGKRYbGRBHFhDmVS0AuHB0ewd18vVuyWaJGhu3qpNXimHiodEWcpmjjepOV2cY68G1RqMDVHY3DZ+V7nxkVb4VJlMUIkyixcULcNgmKDKji3bUuIvayEiVI+nnNHwYbGD8knaf8PhcE00PSZdNFihVaeiUzXJLUUXXl5E0YF5Ef0F/i+0gDt22ZJWkk0z1U7w4VDYWGKTa6qiMEensscLTpWRb9PhG0yc= benito@delta
Pepe,Ejemplo,pepeapruebame@gmail.com,pepe,ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCJSZC7mb/m9zJkKJ2OMABtEP1hWvUzRx/fjrRylzEME6DvdMmZYzGxmA+r4xbvBhpB0Ypx0Kk7hJXBciZdzHVxHdeSLNpGw+YyGmn3f7ygDvsWGwXi4xbPAthUQuEWswGu/N+9uAYkAKJTiOkuvVmdbq8zVApvlEsptVyaUG4RNhsgP0bCAD3STWdkBlW9NvZbkBvDIPktmuHncKshQmjd4ZjlS7ApzqgfjlM/hFnx6gaybIE2hFaeLS2yLkeeISUoWpSlDejh9+HC7WIc2mcxRypHwN3pDO9qOzGjwY3RDvttG7YxhUsu+t3dBU/rr7HqqGfbuf4ulhbh+dG0phH8HQ2t1AX4H+Vs1FaFkk5PuLdWKPkm22Pe1j1Vd5TtTXwfMx2NG+n64jmVychAQs5bMIWDKVxk14RUwvxb8mU4f5DHeKNQf/jWk1oP3mzVBcwe+pP/dBfdcsn0E42m0aROaJTCcH93ECDURAcAv/Fb/tbnB0uJhI5fGXkPLo2ZI0= pepe@charlie
Añadir el esquema openssh-lpk
Para añadir el esquema openssh-lpk
al directorio, creamos un fichero openssh-lpk.ldif
con el siguiente contenido:
nano /etc/ldap/schema/openssh-lpk.ldif
dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
DESC 'MANDATORY: OpenSSH Public key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
DESC 'MANDATORY: OpenSSH LPK objectclass'
MAY ( sshPublicKey $ uid )
)
Y lo añadimos al directorio:
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/openssh-lpk.ldif
Dará como resultado:
root@alfa:/home/nazare# ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/openssh-lpk.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=openssh-lpk,cn=schema,cn=config"
Vamos a crear un grupo de usuarios llamado alumnos
:
nano alumnos.ldif
dn: cn=alumnos,ou=Grupos,dc=nazareth,dc=gonzalonazareno,dc=org
objectClass: posixGroup
gidNumber: 10001
cn: alumnos
Y lo añadimos al directorio:
ldapadd -x -D cn=admin,dc=nazareth,dc=gonzalonazareno,dc=org -W -f alumnos.ldif
Podemos comprobarlo con:
ldapsearch -x -b "dc=nazareth,dc=gonzalonazareno,dc=org"
Script en python
Creamos e iniciamos el entorno virtual:
apt install python3-venv
python3 -m venv ldap-venv
source ldap-venv/bin/activate
Instalamos las librerías necesarias:
pip install python3-ldap
pip install ldap3==2.6
Creamos un script en python que nos permita añadir los usuarios al directorio LDAP. Para ello, creamos el fichero ldap.py
con el siguiente contenido:
nano ldap.py
#!/usr/bin/env python
import ldap3
from ldap3 import Connection, ALL
from getpass import getpass
from sys import exit
# Shell para los usuarios
shell = '/bin/bash'
# Ruta del directorio home de los usuarios
home_dir = '/home/ldap-users/'
# UID inicial para los usuarios
uid_number = 2001
# El GID del grupo de usuarios
gid = 10001
# Leemos el fichero .csv de los usuarios y guardamos cada linea en una lista.
with open('alumnos.csv', 'r') as usuarios:
usuarios = usuarios.readlines()
### Parametros para la conexion
ldap_ip = 'ldap://alfa.nazareth.gonzalonazareno.org:389'
dominio_base = 'dc=nazareth,dc=gonzalonazareno,dc=org'
user_admin = 'admin'
contrasena = getpass('Contrasena LDAP admin: ')
# Intenta realizar la conexion.
conn = Connection(ldap_ip, 'cn={},{}'.format(user_admin, dominio_base),contrasena)
# conn.bind() devuelve "True" si se ha establecido la conexion y "False" en caso contrario.
# Comprueba si se ha establecido la conexion.
if not conn.bind():
print('No se ha podido conectar con ldap')
if conn.result['description'] == 'invalidCredentials':
print('Credenciales no validas.')
# Termina el script.
exit(0)
# Recorre la lista de usuarios
for user in usuarios:
# Separa los valores del usuario usando como delimitador ",", y asigna cada valor a la variable correspondiente.
user = user.split(',')
cn = user[0]
sn = user[1]
mail = user[2]
uid = user[3]
ssh = user[4]
#Anade el usuario.
conn.add(
'uid={},ou=Personas,{}'.format(uid, dominio_base),
object_class =
[
'inetOrgPerson',
'posixAccount',
'ldapPublicKey'
],
attributes =
{
'cn': cn,
'sn': sn,
'mail': mail,
'uid': uid,
'uidNumber': str(uid_number),
'gidNumber': str(gid),
'homeDirectory': '{}{}'.format(home_dir,uid),
'loginShell': shell,
'sshPublicKey': str(ssh)
})
if conn.result['description'] == 'entryAlreadyExists':
print('El usuario {} ya existe.'.format(uid))
# Aumenta el contador para asignar un UID diferente a cada usuario (cada vez que ejecutemos el script debemos asegurarnos de ante mano que no existe dicho uid en el directorio ldap, o se solaparian los datos)
uid_number += 1
#Cierra la conexion.
conn.unbind()
Y lo ejecutamos:
python3 ldap.py
Para borrar las entradas:
ldapdelete -x -D cn=admin,dc=nazareth,dc=gonzalonazareno,dc=org -W "uid=nazare,ou=Personas,dc=nazareth,dc=gonzalonazareno,dc=org"
Si ejecutamos una búsqueda dará como salida:
# alumnos, Grupos, nazareth.gonzalonazareno.org
dn: cn=alumnos,ou=Grupos,dc=nazareth,dc=gonzalonazareno,dc=org
objectClass: posixGroup
gidNumber: 10001
cn: alumnos
# nazare, Personas, nazareth.gonzalonazareno.org
dn: uid=nazare,ou=Personas,dc=nazareth,dc=gonzalonazareno,dc=org
cn: Belen
sn: Duran
mail: belenn.457@gmail.com
uid: nazare
uidNumber: 2001
gidNumber: 10001
homeDirectory: /home/ldap-users/nazare
loginShell: /bin/bash
sshPublicKey:: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FEQXFhRlFGTGh
4bjNEd2VrL2h5UlhSSjZlaWxhbXJPSzlOUklFN1FyYmg4S0RISEFlOElNbXlaSno0YkROWlRTd29T
NlNwOWtMYUlFR2ZCMWU4VEpnWTNwL05PSjEvWHVObFpmcGpxQXRaWDNoL2FNUHNxM1FtZXkvMnRQQ
zgrc3NmOXNCREMzekpEVkNCeUJvQ2crRmFxcUtiSVRmUGE4OWV1K2cxaWZDci8xem5EZlJkamRUS0
5aTmtlUWNBbklUQks5SGF6bXpiN1F2eWZSOGVEMThvckFJbnNCWkRzM2JndGU4WlpBSjNPcGI4dVU
ySkxIaU8vT1lrcDFveVU4RXZsTkc4ZmtaWmNQeWMyUGJvajkvYlFacnNDOVo5eENyU0pzWUg5N0Nu
QUtiY3p5NFNCdmtiUWl6UHAxODVtV1FIbTZlU2ttczJNNDhnTGRJRTRDUHVGYzBiM2MxdytzYThrW
Xhsa2cxUnNjeE5RajdLTk85NnErd0dyNVRCN0lFREY1SU5QakFnV1VFd2t2b2F2VVJvSXlxdXYvVl
lLamhaZWg5RHkzTGRWZ0VaSDlUbjFOUUJta0tVUEU4Wlg0TlhUQkVpOVNxc3RoekRRb0grdTM3ZjZ
Nckx6V3pzQnlraTc3VXAxRkMxT2J1N0VpMkRkZU52YzN6MHlQenpzUlU9IG5hemFyZUBkZWx0YQo=
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: ldapPublicKey
# pepe, Personas, nazareth.gonzalonazareno.org
dn: uid=pepe,ou=Personas,dc=nazareth,dc=gonzalonazareno,dc=org
cn: Pepe
sn: Ejemplo
mail: pepeapruebame@gmail.com
uid: pepe
uidNumber: 2002
gidNumber: 10001
homeDirectory: /home/ldap-users/pepe
loginShell: /bin/bash
sshPublicKey:: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FEQ0pTWkM3bWI
vbTl6SmtLSjJPTUFCdEVQMWhXdlV6UngvZmpyUnlsekVNRTZEdmRNbVpZekd4bUErcjR4YnZCaHBC
MFlweDBLazdoSlhCY2laZHpIVnhIZGVTTE5wR3crWXlHbW4zZjd5Z0R2c1dHd1hpNHhiUEF0aFVRd
UVXc3dHdS9OKzl1QVlrQUtKVGlPa3V2Vm1kYnE4elZBcHZsRXNwdFZ5YVVHNFJOaHNnUDBiQ0FEM1
NUV2RrQmxXOU52WmJrQnZESVBrdG11SG5jS3NoUW1qZDRaamxTN0FwenFnZmpsTS9oRm54NmdheWJ
JRTJoRmFlTFMyeUxrZWVJU1VvV3BTbERlamg5K0hDN1dJYzJtY3hSeXBId04zcERPOXFPekdqd1kz
UkR2dHRHN1l4aFVzdSt0M2RCVS9ycjdIcXFHZmJ1ZjR1bGhiaCtkRzBwaEg4SFEydDFBWDRIK1ZzM
UZhRmtrNVB1TGRXS1BrbTIyUGUxajFWZDVUdFRYd2ZNeDJORytuNjRqbVZ5Y2hBUXM1Yk1JV0RLVn
hrMTRSVXd2eGI4bVU0ZjVESGVLTlFmL2pXazFvUDNtelZCY3dlK3BQL2RCZmRjc24wRTQybTBhUk9
hSlRDY0g5M0VDRFVSQWNBdi9GYi90Ym5CMHVKaEk1ZkdYa1BMbzJaSTA9IHBlcGVAY2hhcmxpZQo=
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: ldapPublicKey
# search result
search: 2
result: 0 Success
# numResponses: 9
# numEntries: 8
Validación de usuarios
Para esto, los clientes, deben de añadir en el fichero /etc/ldap/ldap.conf
la siguiente línea:
BASE dc=nazareth,dc=gonzalonazareno,dc=org
URI ldap://alfa.nazareth.gonzalonazareno.org
Configuración ssh
Para que los clientes se puedan conectar por ssh sin usar .ssh/known_hosts
y usando las claves públicas de LDAP, es decir, que solo se permita si el usuario está en LDAP, ejecutamos el siguiente comando en el servidor:
echo "session required pam_mkhomedir.so" >> /etc/pam.d/common-session
pam_mkhomedir.so
crea el directorio home del usuario si no existe.common-session
es el fichero de configuración de PAM que se encarga de crear el directorio home del usuario.
Creamos un script (sshkey-search.sh
) en /opt (ya que es tiene que ser un directorio que pueda ejecutar ssh desde el cliente) que busque las claves públicas de los usuarios en LDAP:
#!/bin/bash
# Script para buscar las claves públicas de los usuarios en LDAP
ldapsearch -x -u -LLL -o ldif-wrap=no '(&(objectClass=posixAccount)(uid='"$1"'))' -b "dc=nazareth,dc=gonzalonazareno,dc=org" 'sshPublicKey' | sed -n 's/^[ \t]*sshPublicKey::[ \t]*\(.*\)/\1/p' | base64 -d
ldapsearch -x -u -LLL -o ldif-wrap=no '(&(objectClass=posixAccount)(uid='"$1"'))' 'sshPublicKey'
busca en LDAP las claves públicas de los usuarios.-b "dc=nazareth,dc=gonzalonazareno,dc=org"
busca en el dominio LDAP.sed -n 's/^[ \t]*sshPublicKey::[ \t]*\(.*\)/\1/p'
elimina la cabecera de la clave pública.base64 -d
decodifica la clave pública.
Damos permisos:
chmod 755 /opt/sshkey-search.sh
Ejecutamos el script:
/opt/sshkey-search.sh pepe
(ldap-venv) root@alfa:~# ./sshkey-search.sh pepe
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCJSZC7mb/m9zJkKJ2OMABtEP1hWvUzRx/fjrRylzEME6DvdMmZYzGxmA+r4xbvBhpB0Ypx0Kk7hJXBciZdzHVxHdeSLNpGw+YyGmn3f7ygDvsWGwXi4xbPAthUQuEWswGu/N+9uAYkAKJTiOkuvVmdbq8zVApvlEsptVyaUG4RNhsgP0bCAD3STWdkBlW9NvZbkBvDIPktmuHncKshQmjd4ZjlS7ApzqgfjlM/hFnx6gaybIE2hFaeLS2yLkeeISUoWpSlDejh9+HC7WIc2mcxRypHwN3pDO9qOzGjwY3RDvttG7YxhUsu+t3dBU/rr7HqqGfbuf4ulhbh+dG0phH8HQ2t1AX4H+Vs1FaFkk5PuLdWKPkm22Pe1j1Vd5TtTXwfMx2NG+n64jmVychAQs5bMIWDKVxk14RUwvxb8mU4f5DHeKNQf/jWk1oP3mzVBcwe+pP/dBfdcsn0E42m0aROaJTCcH93ECDURAcAv/Fb/tbnB0uJhI5fGXkPLo2ZI0= pepe@charlie
Modificamos el fichero /etc/ssh/sshd_config
para que use el script:
AuthorizedKeysCommand /opt/sshkey-search.sh
AuthorizedKeysCommandUser nobody
Reiniciamos el servicio:
systemctl restart sshd
Comprobación
- Charlie:
ssh pepe@alfa
- Delta:
ssh benito@alfa
Desde el servidor:
*Nota: cada usuario tiene que tener en el /etc/host el nombre del servidor y su IP.