Práctica 3 — Usuarios, Red y SSH
Setup — ejecutar una sola vez
Sección titulada “Setup — ejecutar una sola vez"mkdir -p ~/practica3/{work,entrega}Todo lo que se pida “guardar” debe ir dentro de ~/practica3/entrega/.
Ejercicio 3.1 — Anatomía de usuarios: passwd, shadow y ciclo de vida
Sección titulada “Ejercicio 3.1 — Anatomía de usuarios: passwd, shadow y ciclo de vida"Contexto
Sección titulada “Contexto"Un sysadmin real no “crea usuarios a ciegas”: primero lee el estado actual del sistema, entiende qué hay, y deja evidencia de cada cambio. Aquí aprenderás a auditar /etc/passwd y /etc/shadow con precisión, y a distinguir cuentas de servicio de cuentas humanas.
- Audita el sistema antes de tocar nada. Guarda en
~/practica3/entrega/31_auditoria_previa.txt:
- Usuarios con UID ≥ 1000 (humanos): filtra
/etc/passwdconawkogrep - Si existe algún usuario con UID
0distinto deroot(demostrarlo con un comando; si no existe, el comando debe devolver vacío) - Permisos y propietario exactos de
/etc/shadowconstat /etc/shadow; explica en una línea de comentario#por qué esos permisos importan
- Crea estas tres cuentas con
useradd(noadduser):
svc_backup: sin HOME, shell/usr/sbin/nologin, UID fijo1500, GECOS “Servicio de backups”dev_ana: con HOME, shell/bin/bash, GECOS “Desarrolladora senior”ops_luis: con HOME, shell/bin/bash
-
Asigna contraseña a
dev_anayops_luis. Dejasvc_backupsin contraseña. Demuestra el estado de las 3 cuentas conpasswd -S <usuario>(los estados seránL,PoNP). -
Bloquea
ops_luisconusermod -L. Verifica que la línea en/etc/shadowcontiene el prefijo!en el hash. Luego desbloquéalo y vuelve a verificar.
Guarda todos los comandos y sus salidas en:
~/practica3/entrega/31_usuarios.txt
Ejercicio 3.2 — Grupos, delegación y sudo mínimo
Sección titulada “Ejercicio 3.2 — Grupos, delegación y sudo mínimo"Contexto
Sección titulada “Contexto"El error más habitual en producción con sudo es conceder demasiado poder. Aquí practicarás la delegación mínima: solo los comandos exactos que el rol necesita, con rutas absolutas, en un fichero drop-in validado antes de activarlo.
Setup — ejecutar una sola vez
Sección titulada “Setup — ejecutar una sola vez"sudo apt update && sudo apt install -y nginxsystemctl is-active nginx-
Crea el grupo
webops. Añadedev_anayops_luis(del ejercicio anterior) al grupo sin quitarles otros grupos (-aG). Verifica congetent group webops. -
Crea el fichero
/etc/sudoers.d/webopscon tu editor habitual:
sudo nano /etc/sudoers.d/webopsContenido (usa rutas absolutas — en LFCS es obligatorio):
%webops ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx, /usr/bin/systemctl reload nginxValida la sintaxis antes de confiar en el fichero:
sudo visudo -c -f /etc/sudoers.d/webopsSi hay error, corrígelo hasta que devuelva OK.
- Como
dev_ana, comprueba sus permisos declarados:
sudo -u dev_ana sudo -lGuarda la salida: debe listar los tres comandos de nginx y nada más.
- Demuestra los límites de la delegación ejecutando como
dev_ana:
sudo systemctl restart nginx→ debe funcionar sin contraseñasudo systemctl restart ssh→ debe fallarsudo ls /root→ debe fallar
- Verifica el trail de auditoría: busca en
/var/log/auth.loglas entradas dedev_anausandosudo:
grep "dev_ana" /var/log/auth.log | grep sudo | tail -10Guarda toda la evidencia (comandos + salidas) en:
~/practica3/entrega/32_sudo_webops.txt
Ejercicio 3.3 — Gestión de paquetes APT/dpkg con trazabilidad
Sección titulada “Ejercicio 3.3 — Gestión de paquetes APT/dpkg con trazabilidad"Contexto
Sección titulada “Contexto"En LFCS no basta con apt install: tienes que saber inspeccionar qué instala cada paquete antes de instalarlo, rastrear a qué paquete pertenece un fichero dado solo su ruta (sin conocer el nombre del paquete), controlar actualizaciones en producción y entender la diferencia real entre remove y purge. Un sysadmin que no sabe leer el estado de dpkg comete errores que no detecta hasta producción.
-
Auditoría previa. Comprueba si
curl,treeynet-toolsestán ya instalados consultandodpkg. El campo de estadoiisignifica instalado,rceliminado con configs residuales,unnunca instalado. Guarda la salida en~/practica3/entrega/33_estado_previo.txt. Instala los que falten y verifica que los tres tienen estadoii. -
Inspección antes de confiar. Un sysadmin responsable no instala paquetes a ciegas. Antes de que el profesor instale nada nuevo, usa
apt-cache show openssh-serverpara leer la descripción, versión y dependencias del paquete. Identifica en esa salida el campoInstalled-Sizey anota en un comentario#cuánto espacio ocupa en disco. -
Investigación inversa: del fichero al paquete. Sin buscar en Google, averigua a qué paquete pertenece el ejecutable
/usr/sbin/sshdusando solo herramientasdpkg. Guarda el comando exacto y su salida en~/practica3/entrega/33_inversion.txt. A continuación, con ese paquete identificado, lista todos los ficheros que instala y localiza el fichero de configuración principal del demonio (pista: está en/etc/ssh/). Anótalo con un comentario#. -
Control de versiones en producción. En entornos estables es habitual congelar paquetes críticos para que un
apt upgradedesatendido no los actualice. Congelacurly demuestra que el bloqueo está activo. Luego libéralo y demuestra que ha desaparecido. Guarda ambos estados en~/practica3/entrega/33_hold.txt. -
Remove vs purge: diferencias de estado. Elimina
treeen dos fases separadas. Tras cada fase, consulta su estado condpkg. Observa la diferencia en la columna de estado y explica con un comentario#qué letra aparece en cada fase, qué significa, y por qué en producciónpurgees la opción correcta cuando un servicio queda definitivamente fuera de uso.
Guarda todos los comandos y salidas en:
~/practica3/entrega/33_paquetes.txt
Ejercicio 3.4 — Diagnóstico de red con fallos sembrados
Sección titulada “Ejercicio 3.4 — Diagnóstico de red con fallos sembrados"Contexto
Sección titulada “Contexto"Tu servidor “parece caído”: la web no responde y la resolución de nombres está manipulada. Debes diagnosticar sistemáticamente sin adivinar — midiendo cada capa antes de tocar nada — e identificar exactamente qué está roto antes de repararlo.
Setup — el profesor ejecuta esto antes de entregarte el servidor
Sección titulada “Setup — el profesor ejecuta esto antes de entregarte el servidor"# (Para el profesor — no ejecutes esto tú si lo hace el profesor)sudo apt install -y nginx ufwsudo systemctl stop nginxsudo ufw --force enablesudo ufw default deny incomingsudo ufw allow sshecho "192.0.2.1 google.com" | sudo tee -a /etc/hosts- Diagnóstico inicial — sin tocar nada todavía. Guarda en
~/practica3/entrega/34_diagnostico_inicial.txtla salida de todos estos comandos en orden:
ip aip rsudo ss -tulpnsystemctl is-active nginxsystemctl status nginx --no-pagersudo ufw status verbosecurl -sv http://localhost 2>&1 | tail -8ping -c 2 8.8.8.8ping -c 2 google.com-
A partir del diagnóstico, identifica y documenta exactamente qué está mal (hay al menos 3 problemas independientes). Escribe cada problema en una línea de comentario
#dentro del fichero de entrega antes de corregirlo. -
Repara el sistema para que se cumpla todo simultáneamente:
curl -s -o /dev/null -w "HTTP: %{http_code}\n" http://localhost→HTTP: 200ping -c 1 8.8.8.8→ funcionaping -c 1 google.com→ resuelve a una IP distinta de192.0.2.xsudo ufw status→ muestra el puerto 80/tcp comoALLOW
- Guarda en
~/practica3/entrega/34_verificacion_final.txtlos mismos comandos del punto 1 ejecutados de nuevo, para comparar antes/después. Añade comentarios#indicando qué problema resuelve cada diferencia que observes.
Ejercicio 3.5 — SSH hardening: llaves, permisos y sshd_config
Sección titulada “Ejercicio 3.5 — SSH hardening: llaves, permisos y sshd_config"Contexto
Sección titulada “Contexto"El hardening SSH tiene trampas sutiles: permisos incorrectos en ~/.ssh hacen que las llaves sean ignoradas silenciosamente, y un error en sshd_config impide el reinicio del servicio. Hay que hacerlo en el orden correcto, probando en cada paso antes de cerrar la sesión actual.
Setup — ejecutar una sola vez
Sección titulada “Setup — ejecutar una sola vez"# Crea el usuario de prueba (si no existe del ejercicio anterior)id remoteops 2>/dev/null || sudo useradd -m -s /bin/bash remoteopsecho "remoteops:linux123" | sudo chpasswd- Genera un par de llaves ED25519 para tu usuario actual con un nombre específico:
ssh-keygen -t ed25519 -C "practica3-$(whoami)@$(hostname)" -f ~/.ssh/id_practica3Guarda en ~/practica3/entrega/35_claves_locales.txt:
ls -la ~/.ssh/(permisos del directorio y archivos)ssh-keygen -lf ~/.ssh/id_practica3.pub(huella de la clave)- El contenido de la clave pública (
cat ~/.ssh/id_practica3.pub) — nunca la privada
- Configura el acceso por llave para
remoteopsmanualmente (sinssh-copy-id, para entender qué hace internamente):
sudo mkdir -p /home/remoteops/.sshsudo chmod 700 /home/remoteops/.sshsudo cp ~/.ssh/id_practica3.pub /home/remoteops/.ssh/authorized_keyssudo chmod 600 /home/remoteops/.ssh/authorized_keyssudo chown -R remoteops:remoteops /home/remoteops/.sshGuarda en ~/practica3/entrega/35_authorized_keys.txt:
ls -la /home/remoteops/.ssh/stat /home/remoteops/.ssh/authorized_keys
- Endurece
sshd_config. Edita/etc/ssh/sshd_configy modifica o añade estas directivas:
PermitRootLogin noPasswordAuthentication noPubkeyAuthentication yesAuthorizedKeysFile .ssh/authorized_keysAntes de recargar el servicio, valida la sintaxis:
sudo sshd -tSi hay errores de sintaxis, corrígelos antes de continuar. Si todo está bien, recarga (no reinicies — evita cortar sesiones activas):
sudo systemctl reload ssh- Prueba el acceso con llave (abre una segunda terminal sin cerrar esta):
ssh -i ~/.ssh/id_practica3 -o PasswordAuthentication=no remoteops@localhost "whoami && hostname"Debe responder remoteops y el nombre del host. Guarda el comando y su salida en ~/practica3/entrega/35_prueba_llave.txt.
- Prueba que la contraseña queda rechazada:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no remoteops@localhostDebe fallar con Permission denied (publickey). Guarda la salida en el mismo fichero.
- Auditoría: Busca en
/var/log/auth.loglas entradas correspondientes al login exitoso y al rechazado:
grep "remoteops" /var/log/auth.log | tail -15Guarda las líneas relevantes en ~/practica3/entrega/35_auth_log.txt. Identifica con un comentario # qué línea corresponde a cada intento.
Ejercicio 3.6 ⚡ EXTRA — Autoridad Certificadora SSH interna
Sección titulada “Ejercicio 3.6 ⚡ EXTRA — Autoridad Certificadora SSH interna"Contexto
Sección titulada “Contexto"El modelo clásico de authorized_keys escala mal: si tienes 50 servidores y 10 administradores, gestionar qué clave va en qué fichero se convierte en una pesadilla. La solución de nivel empresarial es una Autoridad Certificadora SSH interna: firmas las claves de los usuarios con una CA privada y los servidores confían en la CA, no en cada clave individual. Añadir o revocar un acceso es cambiar un único fichero en el servidor, no editar authorized_keys en cada máquina.
En este ejercicio simularás todo el flujo en localhost: crearás la CA, firmarás una clave de usuario, configurarás el servidor para confiar en la CA y verificarás que el acceso funciona sin entradas en authorized_keys.
- Crea la Autoridad Certificadora. La clave de CA debe protegerse con passphrase y almacenarse fuera de
~/.ssh(en un entorno real iría en un HSM o máquina aislada):
mkdir -p ~/practica3/ssh-cassh-keygen -t ed25519 -C "lab-ca@$(hostname)" -f ~/practica3/ssh-ca/lab_cachmod 600 ~/practica3/ssh-ca/lab_cachmod 644 ~/practica3/ssh-ca/lab_ca.pubGuarda en ~/practica3/entrega/36_ca_info.txt:
ssh-keygen -lf ~/practica3/ssh-ca/lab_ca.pub(huella de la CA)- Explica con un comentario
#por qué la clave privada de la CA debe estar en una máquina dedicada y no en el servidor de destino.
- Firma la clave del usuario
remoteops. El certificado incluye un identificador (-I), el principal permitido (-n), y una validez temporal (-V):
ssh-keygen -s ~/practica3/ssh-ca/lab_ca \ -I "remoteops@lab-$(date +%Y%m%d)" \ -n remoteops \ -V +8h \ /home/remoteops/.ssh/authorized_keys/../id_practica3.pub 2>/dev/null || \ssh-keygen -s ~/practica3/ssh-ca/lab_ca \ -I "remoteops@lab-$(date +%Y%m%d)" \ -n remoteops \ -V +8h \ ~/.ssh/id_practica3.pubEsto genera ~/.ssh/id_practica3-cert.pub. Inspecciona el certificado generado:
ssh-keygen -Lf ~/.ssh/id_practica3-cert.pubGuarda la salida en ~/practica3/entrega/36_cert_inspeccion.txt. Identifica con comentarios # los campos: Key ID, Valid, Principals, y explica qué pasará cuando expire el certificado.
- Configura el servidor para confiar en la CA (no en certificados individuales):
sudo cp ~/practica3/ssh-ca/lab_ca.pub /etc/ssh/lab_ca.pubsudo chmod 644 /etc/ssh/lab_ca.pubAñade al final de /etc/ssh/sshd_config:
TrustedUserCAKeys /etc/ssh/lab_ca.pubValida y recarga:
sudo sshd -t && sudo systemctl reload ssh- Retira la clave pública directa de
remoteopspara demostrar que el acceso funciona solo por certificado:
sudo truncate -s 0 /home/remoteops/.ssh/authorized_keys- Prueba el acceso con certificado. SSH usará automáticamente el certificado si detecta
id_practica3-cert.pubjunto aid_practica3:
ssh -i ~/.ssh/id_practica3 -o PasswordAuthentication=no remoteops@localhost "whoami && echo 'Acceso por certificado CA OK'"Debe funcionar. Guarda el comando y su salida en ~/practica3/entrega/36_acceso_cert.txt.
- Simula una revocación. En producción, si una clave se compromete no puedes firmar un nuevo certificado con la misma CA sin más — necesitas una
KRL(Key Revocation List). Crea una KRL que revoque el certificado actual y verifica que el acceso queda bloqueado:
# Genera la KRL revocando el certificadossh-keygen -k -f ~/practica3/ssh-ca/lab_krl -s ~/practica3/ssh-ca/lab_ca.pub ~/.ssh/id_practica3-cert.pub
# Activa la KRL en el servidorsudo cp ~/practica3/ssh-ca/lab_krl /etc/ssh/lab_krlsudo chmod 644 /etc/ssh/lab_krlecho "RevokedKeys /etc/ssh/lab_krl" | sudo tee -a /etc/ssh/sshd_configsudo sshd -t && sudo systemctl reload ssh
# Prueba: debe fallar ahorassh -i ~/.ssh/id_practica3 -o PasswordAuthentication=no remoteops@localhost "whoami" 2>&1Guarda ambas pruebas (acceso OK y acceso denegado tras KRL) en ~/practica3/entrega/36_revocacion.txt. Explica con un comentario # la diferencia operativa entre una KRL y simplemente borrar la entrada de authorized_keys.
Ejercicio 3.7 ⚡ EXTRA — Política de contraseñas y bloqueo de cuentas con PAM
Sección titulada “Ejercicio 3.7 ⚡ EXTRA — Política de contraseñas y bloqueo de cuentas con PAM"Contexto
Sección titulada “Contexto"Cuando un servidor tiene cuentas con contraseña, dos políticas son imprescindibles en cualquier auditoría de seguridad: complejidad mínima de contraseñas (para evitar contraseñas débiles) y bloqueo automático tras intentos fallidos (para mitigar ataques de fuerza bruta). Ambas se gestionan a través de PAM, que actúa como capa intermedia entre las aplicaciones (login, SSH, sudo) y el sistema de autenticación del kernel.
sudo apt install -y libpam-pwquality- Audita la configuración PAM actual antes de tocar nada. Lee y guarda:
cat /etc/pam.d/common-passwordcat /etc/pam.d/common-authsudo faillock --user dev_ana 2>/dev/null || echo "faillock no tiene entradas aún"Guarda en ~/practica3/entrega/37_pam_previo.txt. Identifica con comentarios # qué módulo gestiona actualmente la autenticación de contraseñas y cuál gestiona la autenticación de sesión.
- Configura la política de complejidad de contraseñas con
pam_pwquality. Edita/etc/security/pwquality.confy establece:
minlen = 12dcredit = -1ucredit = -1lcredit = -1ocredit = -1maxrepeat = 3gecoscheck = 1Verifica que la política funciona intentando asignar contraseñas débiles:
# Debe fallar con mensaje de políticasudo passwd dev_ana <<< $'abc\nabc\n'
# Debe fallar por demasiado cortaecho "dev_ana:short1A!" | sudo chpasswd 2>&1
# Debe funcionarecho "dev_ana:C0mpl3ja!Pass#2024" | sudo chpasswd 2>&1Guarda los tres intentos y sus salidas en ~/practica3/entrega/37_pwquality.txt. Explica con un comentario # qué hace exactamente el parámetro gecoscheck.
- Configura el bloqueo de cuenta con
faillocktras 3 intentos fallidos durante 5 minutos de ventana, con bloqueo de 10 minutos. Edita/etc/security/faillock.conf:
deny = 3fail_interval = 300unlock_time = 600auditsilentVerifica que el módulo pam_faillock.so está activo en /etc/pam.d/common-auth. Debe aparecer en dos posiciones (preauth y authfail):
grep faillock /etc/pam.d/common-authSi no aparece, lo está ya manejando automáticamente en Debian/Ubuntu moderno a través de /etc/pam.d/login y /etc/pam.d/sshd. Verifica:
grep -r faillock /etc/pam.d/Guarda la salida en ~/practica3/entrega/37_faillock_config.txt.
- Provoca el bloqueo y observa el mecanismo. Desde una segunda terminal, intenta autenticarte como
dev_anacon contraseña incorrecta tres veces:
# Ejecuta esto 3 veces con contraseña incorrecta deliberadamentessh -o PreferredAuthentications=password dev_ana@localhostDespués, inspecciona el estado del bloqueo:
sudo faillock --user dev_anaLa salida mostrará los intentos fallidos con timestamp. Guarda en ~/practica3/entrega/37_bloqueo_activo.txt.
- Desbloquea manualmente la cuenta (como haría un sysadmin ante una llamada de soporte):
sudo faillock --user dev_ana --resetsudo faillock --user dev_anaVerifica que el estado queda limpio. Guarda en el mismo fichero. Explica con un comentario #:
- Por qué
faillock --resetes preferible a esperar elunlock_timeen producción - Qué diferencia hay entre
faillocky el antiguopam_tally2 - Por qué el parámetro
auditenfaillock.confes un arma de doble filo (pista: ¿qué aparece en los logs?)
- Integración con SSH. Comprueba que el bloqueo PAM afecta también a intentos de autenticación por SSH (no solo a login local). Busca en los logs evidencia de los intentos fallidos:
grep "dev_ana" /var/log/auth.log | grep -E "fail|FAIL|lock|pam" | tail -20Guarda en ~/practica3/entrega/37_auth_log_pam.txt. Identifica con comentarios # al menos dos líneas que demuestren que PAM interceptó los intentos fallidos de SSH.
📤 Bloque de entrega
Sección titulada “📤 Bloque de entrega"Sigue las instrucciones comunes: Cómo entregar las prácticas (usa N = 3).