Ir al contenido

Práctica 3 — Usuarios, Red y SSH


ventana terminal
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"

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.

  1. Audita el sistema antes de tocar nada. Guarda en ~/practica3/entrega/31_auditoria_previa.txt:
  • Usuarios con UID ≥ 1000 (humanos): filtra /etc/passwd con awk o grep
  • Si existe algún usuario con UID 0 distinto de root (demostrarlo con un comando; si no existe, el comando debe devolver vacío)
  • Permisos y propietario exactos de /etc/shadow con stat /etc/shadow; explica en una línea de comentario # por qué esos permisos importan
  1. Crea estas tres cuentas con useradd (no adduser):
  • svc_backup: sin HOME, shell /usr/sbin/nologin, UID fijo 1500, GECOS “Servicio de backups”
  • dev_ana: con HOME, shell /bin/bash, GECOS “Desarrolladora senior”
  • ops_luis: con HOME, shell /bin/bash
  1. Asigna contraseña a dev_ana y ops_luis. Deja svc_backup sin contraseña. Demuestra el estado de las 3 cuentas con passwd -S <usuario> (los estados serán L, P o NP).

  2. Bloquea ops_luis con usermod -L. Verifica que la línea en /etc/shadow contiene 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"

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.

ventana terminal
sudo apt update && sudo apt install -y nginx
systemctl is-active nginx
  1. Crea el grupo webops. Añade dev_ana y ops_luis (del ejercicio anterior) al grupo sin quitarles otros grupos (-aG). Verifica con getent group webops.

  2. Crea el fichero /etc/sudoers.d/webops con tu editor habitual:

ventana terminal
sudo nano /etc/sudoers.d/webops

Contenido (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 nginx

Valida la sintaxis antes de confiar en el fichero:

ventana terminal
sudo visudo -c -f /etc/sudoers.d/webops

Si hay error, corrígelo hasta que devuelva OK.

  1. Como dev_ana, comprueba sus permisos declarados:
ventana terminal
sudo -u dev_ana sudo -l

Guarda la salida: debe listar los tres comandos de nginx y nada más.

  1. Demuestra los límites de la delegación ejecutando como dev_ana:
  • sudo systemctl restart nginx → debe funcionar sin contraseña
  • sudo systemctl restart ssh → debe fallar
  • sudo ls /root → debe fallar
  1. Verifica el trail de auditoría: busca en /var/log/auth.log las entradas de dev_ana usando sudo:
ventana terminal
grep "dev_ana" /var/log/auth.log | grep sudo | tail -10

Guarda 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"

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.

  1. Auditoría previa. Comprueba si curl, tree y net-tools están ya instalados consultando dpkg. El campo de estado ii significa instalado, rc eliminado con configs residuales, un nunca instalado. Guarda la salida en ~/practica3/entrega/33_estado_previo.txt. Instala los que falten y verifica que los tres tienen estado ii.

  2. 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-server para leer la descripción, versión y dependencias del paquete. Identifica en esa salida el campo Installed-Size y anota en un comentario # cuánto espacio ocupa en disco.

  3. Investigación inversa: del fichero al paquete. Sin buscar en Google, averigua a qué paquete pertenece el ejecutable /usr/sbin/sshd usando solo herramientas dpkg. 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 #.

  4. Control de versiones en producción. En entornos estables es habitual congelar paquetes críticos para que un apt upgrade desatendido no los actualice. Congela curl y demuestra que el bloqueo está activo. Luego libéralo y demuestra que ha desaparecido. Guarda ambos estados en ~/practica3/entrega/33_hold.txt.

  5. Remove vs purge: diferencias de estado. Elimina tree en dos fases separadas. Tras cada fase, consulta su estado con dpkg. 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ón purge es 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"

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"
ventana terminal
# (Para el profesor — no ejecutes esto tú si lo hace el profesor)
sudo apt install -y nginx ufw
sudo systemctl stop nginx
sudo ufw --force enable
sudo ufw default deny incoming
sudo ufw allow ssh
echo "192.0.2.1 google.com" | sudo tee -a /etc/hosts
  1. Diagnóstico inicial — sin tocar nada todavía. Guarda en ~/practica3/entrega/34_diagnostico_inicial.txt la salida de todos estos comandos en orden:
ventana terminal
ip a
ip r
sudo ss -tulpn
systemctl is-active nginx
systemctl status nginx --no-pager
sudo ufw status verbose
curl -sv http://localhost 2>&1 | tail -8
ping -c 2 8.8.8.8
ping -c 2 google.com
  1. 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.

  2. Repara el sistema para que se cumpla todo simultáneamente:

  • curl -s -o /dev/null -w "HTTP: %{http_code}\n" http://localhostHTTP: 200
  • ping -c 1 8.8.8.8 → funciona
  • ping -c 1 google.com → resuelve a una IP distinta de 192.0.2.x
  • sudo ufw status → muestra el puerto 80/tcp como ALLOW
  1. Guarda en ~/practica3/entrega/34_verificacion_final.txt los 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"

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.

ventana terminal
# Crea el usuario de prueba (si no existe del ejercicio anterior)
id remoteops 2>/dev/null || sudo useradd -m -s /bin/bash remoteops
echo "remoteops:linux123" | sudo chpasswd
  1. Genera un par de llaves ED25519 para tu usuario actual con un nombre específico:
ventana terminal
ssh-keygen -t ed25519 -C "practica3-$(whoami)@$(hostname)" -f ~/.ssh/id_practica3

Guarda 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
  1. Configura el acceso por llave para remoteops manualmente (sin ssh-copy-id, para entender qué hace internamente):
ventana terminal
sudo mkdir -p /home/remoteops/.ssh
sudo chmod 700 /home/remoteops/.ssh
sudo cp ~/.ssh/id_practica3.pub /home/remoteops/.ssh/authorized_keys
sudo chmod 600 /home/remoteops/.ssh/authorized_keys
sudo chown -R remoteops:remoteops /home/remoteops/.ssh

Guarda en ~/practica3/entrega/35_authorized_keys.txt:

  • ls -la /home/remoteops/.ssh/
  • stat /home/remoteops/.ssh/authorized_keys
  1. Endurece sshd_config. Edita /etc/ssh/sshd_config y modifica o añade estas directivas:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

Antes de recargar el servicio, valida la sintaxis:

ventana terminal
sudo sshd -t

Si hay errores de sintaxis, corrígelos antes de continuar. Si todo está bien, recarga (no reinicies — evita cortar sesiones activas):

ventana terminal
sudo systemctl reload ssh
  1. Prueba el acceso con llave (abre una segunda terminal sin cerrar esta):
ventana terminal
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.

  1. Prueba que la contraseña queda rechazada:
ventana terminal
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no remoteops@localhost

Debe fallar con Permission denied (publickey). Guarda la salida en el mismo fichero.

  1. Auditoría: Busca en /var/log/auth.log las entradas correspondientes al login exitoso y al rechazado:
ventana terminal
grep "remoteops" /var/log/auth.log | tail -15

Guarda 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"

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.

  1. 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):
ventana terminal
mkdir -p ~/practica3/ssh-ca
ssh-keygen -t ed25519 -C "lab-ca@$(hostname)" -f ~/practica3/ssh-ca/lab_ca
chmod 600 ~/practica3/ssh-ca/lab_ca
chmod 644 ~/practica3/ssh-ca/lab_ca.pub

Guarda 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.
  1. Firma la clave del usuario remoteops. El certificado incluye un identificador (-I), el principal permitido (-n), y una validez temporal (-V):
ventana terminal
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.pub

Esto genera ~/.ssh/id_practica3-cert.pub. Inspecciona el certificado generado:

ventana terminal
ssh-keygen -Lf ~/.ssh/id_practica3-cert.pub

Guarda 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.

  1. Configura el servidor para confiar en la CA (no en certificados individuales):
ventana terminal
sudo cp ~/practica3/ssh-ca/lab_ca.pub /etc/ssh/lab_ca.pub
sudo chmod 644 /etc/ssh/lab_ca.pub

Añade al final de /etc/ssh/sshd_config:

TrustedUserCAKeys /etc/ssh/lab_ca.pub

Valida y recarga:

ventana terminal
sudo sshd -t && sudo systemctl reload ssh
  1. Retira la clave pública directa de remoteops para demostrar que el acceso funciona solo por certificado:
ventana terminal
sudo truncate -s 0 /home/remoteops/.ssh/authorized_keys
  1. Prueba el acceso con certificado. SSH usará automáticamente el certificado si detecta id_practica3-cert.pub junto a id_practica3:
ventana terminal
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.

  1. 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:
ventana terminal
# Genera la KRL revocando el certificado
ssh-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 servidor
sudo cp ~/practica3/ssh-ca/lab_krl /etc/ssh/lab_krl
sudo chmod 644 /etc/ssh/lab_krl
echo "RevokedKeys /etc/ssh/lab_krl" | sudo tee -a /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl reload ssh
# Prueba: debe fallar ahora
ssh -i ~/.ssh/id_practica3 -o PasswordAuthentication=no remoteops@localhost "whoami" 2>&1

Guarda 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"

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.

ventana terminal
sudo apt install -y libpam-pwquality
  1. Audita la configuración PAM actual antes de tocar nada. Lee y guarda:
ventana terminal
cat /etc/pam.d/common-password
cat /etc/pam.d/common-auth
sudo 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.

  1. Configura la política de complejidad de contraseñas con pam_pwquality. Edita /etc/security/pwquality.conf y establece:
minlen = 12
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
maxrepeat = 3
gecoscheck = 1

Verifica que la política funciona intentando asignar contraseñas débiles:

ventana terminal
# Debe fallar con mensaje de política
sudo passwd dev_ana <<< $'abc\nabc\n'
# Debe fallar por demasiado corta
echo "dev_ana:short1A!" | sudo chpasswd 2>&1
# Debe funcionar
echo "dev_ana:C0mpl3ja!Pass#2024" | sudo chpasswd 2>&1

Guarda los tres intentos y sus salidas en ~/practica3/entrega/37_pwquality.txt. Explica con un comentario # qué hace exactamente el parámetro gecoscheck.

  1. Configura el bloqueo de cuenta con faillock tras 3 intentos fallidos durante 5 minutos de ventana, con bloqueo de 10 minutos. Edita /etc/security/faillock.conf:
deny = 3
fail_interval = 300
unlock_time = 600
audit
silent

Verifica que el módulo pam_faillock.so está activo en /etc/pam.d/common-auth. Debe aparecer en dos posiciones (preauth y authfail):

ventana terminal
grep faillock /etc/pam.d/common-auth

Si 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:

ventana terminal
grep -r faillock /etc/pam.d/

Guarda la salida en ~/practica3/entrega/37_faillock_config.txt.

  1. Provoca el bloqueo y observa el mecanismo. Desde una segunda terminal, intenta autenticarte como dev_ana con contraseña incorrecta tres veces:
ventana terminal
# Ejecuta esto 3 veces con contraseña incorrecta deliberadamente
ssh -o PreferredAuthentications=password dev_ana@localhost

Después, inspecciona el estado del bloqueo:

ventana terminal
sudo faillock --user dev_ana

La salida mostrará los intentos fallidos con timestamp. Guarda en ~/practica3/entrega/37_bloqueo_activo.txt.

  1. Desbloquea manualmente la cuenta (como haría un sysadmin ante una llamada de soporte):
ventana terminal
sudo faillock --user dev_ana --reset
sudo faillock --user dev_ana

Verifica que el estado queda limpio. Guarda en el mismo fichero. Explica con un comentario #:

  • Por qué faillock --reset es preferible a esperar el unlock_time en producción
  • Qué diferencia hay entre faillock y el antiguo pam_tally2
  • Por qué el parámetro audit en faillock.conf es un arma de doble filo (pista: ¿qué aparece en los logs?)
  1. 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:
ventana terminal
grep "dev_ana" /var/log/auth.log | grep -E "fail|FAIL|lock|pam" | tail -20

Guarda 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.


Sigue las instrucciones comunes: Cómo entregar las prácticas (usa N = 3).