Inicio Linux & Systems Cybersecurity Cloud & DevOps Networks & Infrastructure SIEM & Monitoring DFIR & Threat Intel Development & Other Todas las categorias Herramientas

Bastion Hardened Debian 13: Hardening extremo con KSPP, Seccomp-BPF y telemetria SIEM

Bastion Hardened Debian 13: Hardening extremo con KSPP, Seccomp-BPF y telemetria SIEM

Tabla de contenidos

Introduccion: Security by default

La mayoria de distribuciones Linux siguen un modelo de permiso por defecto: todo esta permitido hasta que el administrador lo restringe. Este post invierte ese modelo — construimos un sistema que nace restringido y solo se abre lo estrictamente necesario.

Este post es la evolucion de nuestra implementacion de Kernel Hardening con KSPP. Partimos de aquella base y la llevamos al siguiente nivel: un Debian 13 (Trixie) con seguridad por defecto a nivel de kernel, syscalls, red, filesystem y procesos. Un sistema operativo que es seguro antes de instalar nada.

PrincipioImplementacion
Deny-all syscallsSeccomp-BPF per-service via systemd (sshd excluido)
Deny-all rednftables con policy DROP en input, output y forward
Deny-all filesystemBlacklist modulos + particiones noexec
Deny-all modulosBlacklist agresivo + module.sig_enforce
Deny-all capacidadesCapabilityBoundingSet minimo por servicio
Kernel inmutablelockdown=confidentiality + auditd inmutable (-e 2)
Telemetria irrevocableEnvio en tiempo real a SIEM externo (Wazuh/Elastic/Splunk)

El resultado es un Debian con restricciones de nivel militar, manteniendo la flexibilidad del ecosistema Debian para entornos productivos. Toda la guia es aplicable tanto en bare-metal (portatil, servidor fisico) como en cualquier hipervisor empresarial (VMware, Proxmox, Hyper-V, Xen, KVM). Toda la configuracion ha sido validada en un entorno real (Debian 13, kernel 6.12.88).


Arquitectura de defensa en profundidad

CODE
┌─────────────────────────────────────────────────────┐
│              HARDWARE / HIPERVISOR                    │
│  Deshabilitar: clipboard, USB passthrough, audio     │
│  Aplicable a: VMware, Proxmox, Hyper-V, KVM, bare   │
├─────────────────────────────────────────────────────┤
│ CAPA 1: KERNEL (KSPP + lockdown)                    │
│  - lockdown=confidentiality                          │
│  - module.sig_enforce=1, audit=1                     │
│  - init_on_alloc/free, slab_nomerge, pti=on         │
│  - Spectre/MDS/TSX mitigations enforced              │
├─────────────────────────────────────────────────────┤
│ CAPA 2: SYSCTL (60+ parametros)                     │
│  - ptrace_scope=3, kptr_restrict=2                   │
│  - kexec_load_disabled=1, io_uring_disabled=2        │
│  - IPv6 deshabilitado, ICMP bloqueado               │
├─────────────────────────────────────────────────────┤
│ CAPA 3: SECCOMP-BPF (per-service)                   │
│  - SystemCallFilter por servicio via systemd         │
│  - sshd/auditd excluidos (privilege separation)      │
│  - fail2ban, cron, timesyncd: filtrado completo      │
├─────────────────────────────────────────────────────┤
│ CAPA 4: RED (nftables deny-all)                     │
│  - Input: solo SSH desde red gestion                 │
│  - Output: solo DNS + apt + NTP + SIEM              │
│  - Forward: DROP total                               │
│  - Rate-limiting en todas las reglas                 │
├─────────────────────────────────────────────────────┤
│ CAPA 5: FILESYSTEM                                  │
│  - /tmp noexec,nosuid,nodev                          │
│  - /proc hidepid=2                                   │
│  - Modulos kernel blacklisted (USB, BT, wireless)    │
├─────────────────────────────────────────────────────┤
│ CAPA 6: AUDITORIA (auditd inmutable)                │
│  - Reglas MITRE ATT&CK (-e 2 inmutable)             │
│  - AIDE/Wazuh syscheck cada 15 min                  │
│  - Fail2ban + logging centralizado                   │
├─────────────────────────────────────────────────────┤
│ CAPA 7: TELEMETRIA (SIEM + EDR + DFIR)             │
│  - Wazuh Agent (FIM + rootkit + log collection)      │
│  - Velociraptor Client (threat hunting en vivo)      │
│  - Envio a SIEM externo (irrevocable localmente)     │
└─────────────────────────────────────────────────────┘

Antes de hardening: Framework de Decision

Cada control tiene un coste: complejidad, rendimiento, operabilidad. Aplicar "maximo hardening" por defecto es tan peligroso como no aplicar ninguno. Antes de implementar cualquier restriccion, responde estas 4 preguntas:

1. Que estamos protegiendo (Asset Criticality)

NivelEjemploImplicacion
CriticoBastion de produccion, sistemas de pagoHardening maximo, incluso con impacto operacional
AltoServidores de aplicacion con datos sensiblesHardening adaptado, priorizando deteccion
MedioEntornos de staging, herramientas internasHardening base + monitoreo
BajoLaboratorios, VMs efimerasHardening minimo, foco en aislamiento de red

2. Quien podria atacarnos (Threat Modeling)

ActorMotivacionControles prioritarios
Script kiddie / BotnetOportunismo, cryptominingFirewall estricto, actualizaciones automaticas
Competidor / EspionajeRobo de IPTelemetria avanzada, FIM, auditoria inmutable
APT / Estado-nacionPersistencia en infraestructura criticaKernel hardening, remote attestation, Zero Trust
Insider maliciosoSabotaje, exfiltracionSegregacion de duties, logging de sesiones, MFA

3. Que podemos permitirnos perder (Risk Tolerance)

  • Es aceptable un downtime de 15 minutos para parchear el kernel?
  • Podemos bloquear una syscall y romper una aplicacion?
  • Tenemos capacidad de recuperacion si el hardening causa un incidente?

Regla de oro: Si no puedes responder estas preguntas, no estas listo para hardening avanzado. Empieza por los controles de deteccion.

4. Como sabremos si funciona (Success Metrics)

Define metricas antes de implementar:

  • Tiempo medio de deteccion de anomalias (MTTD) — objetivo: < 5 min
  • Falsos positivos por dia — objetivo: < 5 por bastion
  • Impacto en rendimiento — objetivo: < 10% degradacion
  • Tiempo de recuperacion ante incidente de configuracion (MTTR) — objetivo: < 15 min

Matriz de Decision: Que aplicar segun el servicio

No todos los servidores necesitan el mismo nivel de restriccion. Esta matriz guia la decision:

ControlBastionWeb AppBase de DatosContainer Host
lockdown=confidentialitySiIntegrityIntegrityIntegrity
io_uring_disabled=2SiSiNo (*)Si
user.max_user_namespaces=0SiSiSiNo (**)
Seccomp per-serviceAgresivoAdaptadoMinimoContainer-native
nftables output deny-allSiAllowlist FQDNAllowlist DB peersAllowlist registry
ptrace_scope=3SiScope=2Scope=2Scope=2
FIM cada 15 minSiCada 1hSolo configsSolo binarios
MFA obligatorioSiSiSiSolo admin

(*) PostgreSQL 14+ usa io_uring; deshabilitarlo = -30% throughput en escritura intensiva.

(**) Docker/Podman requieren user namespaces para aislamiento de contenedores.

Filosofia por tipo de servicio:

  • Bastion: "Deny-all por defecto" — superficie minima, acceso humano directo
  • Web/DB: "Detect-and-respond" — permitir funcionalidad, detectar anomalias
  • Containers: "Isolate-by-design" — confiar en aislamiento de runtime, harden el host

Todo lo que sigue en este post aplica el perfil Bastion (maximo nivel de restriccion). Adapta segun tu matriz de riesgo.

Parametros especificos por perfil

ControlBastionServidor WebBase de datosContainer host
user.max_user_namespaces0001024 (Docker necesita)
lockdownconfidentialityintegrityintegrityintegrity
slub_debug=FZPSiNo (impacto rendimiento ~5-15%)NoNo
init_on_free=1SiSiNo (impacto I/O)Si
io_uring_disabled220 (PostgreSQL usa io_uring)2
module.sig_enforce110 (DKMS/drivers)0 (overlay fs)
nftables outputSolo DNS/APT/NTP/SIEM+HTTP/HTTPS a backends+puertos DB+registry
Seccomp per-serviceSi (agresivo)Si (adaptado)No (rendimiento)Si (containerd)

Impacto real en rendimiento: slub_debug=FZP + init_on_free=1 + page_poison=1 pueden reducir throughput entre 5-15% en cargas intensivas de memoria. En un bastion es irrelevante. En un servidor de base de datos con miles de transacciones/segundo, es inaceptable. Medir siempre antes de desplegar.

Incompatibilidades conocidas con herramientas enterprise

HerramientaControl conflictivoSolucion
Docker/Podmanuser.max_user_namespaces=0Subir a 1024+ en container hosts
DKMS / drivers propietarios (NVIDIA, etc.)module.sig_enforce=1Deshabilitar o firmar modulos con MOK
Herramientas eBPF (Cilium, Falco, bpftrace)lockdown=confidentialityUsar lockdown=integrity o excluir
Agentes de backup (Veeam, NetBackup)Seccomp @system-serviceDrop-in especifico sin SystemCallFilter
Monitoring con perf/flamegraphkernel.yama.ptrace_scope=3Reducir a 2 en entornos de profiling
Antivirus enterprise (CrowdStrike, SentinelOne)lockdown=confidentiality + module.sigRequiere excepciones kernel o firma

Fase 1: Instalacion base minimalista

Requisitos de hardware/VM

  • CPU: 2 cores minimo
  • RAM: 2GB minimo, 4GB recomendado
  • Disco: 20GB minimo (con LUKS el cifrado consume overhead)
  • Red: Interfaz dedicada para gestion (separada del trafico de produccion)

Instalacion de Debian 13 (Trixie)

Independientemente del hipervisor o hardware fisico, la instalacion debe ser minimalista. Usar la ISO netinst y en el paso de seleccion de software (tasksel) marcar unicamente:

  • "Standard system utilities"

Desmarcar todo lo demas: escritorio, servidor web, servidor SSH (lo instalamos despues hardeneado), servidor de impresion, etc. El objetivo es partir de un sistema con la superficie de ataque mas reducida posible.

💡 Consideracion enterprise: En entornos con decenas de bastiones, esta instalacion se automatiza con Packer + Ansible. Ver seccion "Escalando el hardening".

Consideraciones por hipervisor

En entornos virtualizados, reducir la superficie de ataque del propio hipervisor:

HipervisorAcciones recomendadas
VMware ESXi/WorkstationDeshabilitar: copy-paste, drag-drop, shared folders, 3D acceleration. Activar Secure Boot en la VM
Proxmox/KVMUsar machine type q35, deshabilitar USB passthrough, tablet device y audio. Activar cpu host
Hyper-VDeshabilitar Integration Services innecesarios, activar Secure Boot, usar Generation 2
XenUsar modo HVM con IOMMU, deshabilitar PV drivers innecesarios
Bare-metalBIOS/UEFI: deshabilitar USB boot, activar Secure Boot, password en BIOS, deshabilitar Thunderbolt/FireWire si no se usan

Hardening post-instalacion inicial

Una vez con el sistema base instalado y acceso como root:

BASH
# Actualizar y limpiar
apt update && apt full-upgrade -y && apt autoremove --purge -y

# Instalar herramientas de hardening
apt install -y \
    apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra \
    libpam-apparmor \
    aide aide-common \
    auditd audispd-plugins \
    fail2ban \
    nftables \
    haveged rng-tools5 \
    lynis \
    unattended-upgrades \
    needrestart \
    debsums \
    systemd-coredump \
    cryptsetup-bin \
    libseccomp-dev libseccomp2 seccomp \
    openssh-server \
    jq curl gnupg2

# Eliminar paquetes innecesarios (minimalismo)
apt purge -y \
    telnet rsh-client xinetd nis tftp \
    avahi-daemon cups 2>/dev/null || true

apt autoremove --purge -y

Crear usuario de administracion

El acceso al bastion se hace exclusivamente por SSH con clave publica. Sin password login, sin acceso root directo:

BASH
# Crear grupo y usuario
groupadd bastion-ssh
useradd -m -s /bin/bash -G bastion-ssh,sudo bastion-admin

# Configurar SSH key
mkdir -p /home/bastion-admin/.ssh
chmod 700 /home/bastion-admin/.ssh

# Copiar tu clave publica (desde tu maquina local)
# echo "ssh-ed25519 AAAA..." > /home/bastion-admin/.ssh/authorized_keys
chmod 600 /home/bastion-admin/.ssh/authorized_keys
chown -R bastion-admin:bastion-admin /home/bastion-admin/.ssh

# Bloquear password de root
passwd -l root

Politica de contraseñas (PAM + pwquality)

Aunque el acceso SSH es exclusivamente por clave publica, las contraseñas locales siguen siendo relevantes para sudo, consola fisica y escalada de privilegios. Una politica robusta previene contraseñas debiles que un atacante podria explotar tras comprometer una sesion:

BASH
# /etc/login.defs - politica de ciclo de vida
PASS_MAX_DAYS   90      # Rotacion cada 90 dias
PASS_MIN_DAYS   7       # Minimo 7 dias entre cambios (evitar bypass de historial)
PASS_WARN_AGE   14      # Aviso 14 dias antes de expiracion
PASS_MIN_LEN    14      # Longitud minima 14 caracteres
BASH
# /etc/security/pwquality.conf - complejidad
minlen = 14             # Longitud minima
dcredit = -1            # Al menos 1 digito
ucredit = -1            # Al menos 1 mayuscula
ocredit = -1            # Al menos 1 caracter especial
lcredit = -1            # Al menos 1 minuscula
maxrepeat = 3           # Maximo 3 caracteres consecutivos iguales
maxclassrepeat = 4      # Maximo 4 consecutivos de misma clase
usercheck = 1           # No puede contener el username
dictcheck = 1           # Verificar contra diccionario
minclass = 4            # Minimo 4 clases de caracteres
difok = 8               # Minimo 8 caracteres diferentes al anterior
palindrome = 1          # Rechazar palindromos
BASH
# PAM: historial + bloqueo de cuentas
# /etc/pam.d/common-password (añadir remember=12)
password requisite pam_pwquality.so retry=3
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt remember=12

# Bloqueo tras intentos fallidos (pam_faillock)
# /etc/pam.d/common-auth
auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 fail_interval=900
auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 fail_interval=900
auth sufficient pam_faillock.so authsucc audit deny=5 unlock_time=900 fail_interval=900

Esto establece: contraseñas de minimo 14 caracteres con complejidad obligatoria, rotacion cada 90 dias, historial de 12 passwords (no reutilizables), y bloqueo de cuenta durante 15 minutos tras 5 intentos fallidos.

Sudo hardening

Sudo es el vector mas comun de escalada de privilegios. La configuracion por defecto es demasiado permisiva:

BASH
# /etc/sudoers.d/00-security
Defaults    requiretty              # Solo desde terminal real (no scripts remotos)
Defaults    use_pty                 # Forzar pseudo-terminal
Defaults    logfile="/var/log/sudo.log"
Defaults    log_input, log_output   # Grabar TODA la sesion sudo
Defaults    iolog_dir="/var/log/sudo-io"
Defaults    passwd_timeout=1        # 1 minuto para introducir password
Defaults    timestamp_timeout=5     # Cache de 5 min (no indefinido)
Defaults    passwd_tries=3          # Max 3 intentos
Defaults    env_reset               # Limpiar variables de entorno
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Defaults    !visiblepw              # No mostrar password en pantalla
Defaults    always_set_home         # Siempre setear HOME
Defaults    log_denied              # Logear intentos denegados
BASH
# /etc/sudoers.d/bastion - permisos granulares (principio de minimo privilegio)
bastion-admin ALL=(root) NOPASSWD: /usr/local/bin/security-test-suite.sh
bastion-admin ALL=(root) NOPASSWD: /usr/local/bin/bastion-emergency.sh audit-mode
bastion-admin ALL=(root) NOPASSWD: /usr/bin/journalctl
bastion-admin ALL=(root) NOPASSWD: /usr/bin/systemctl status *
bastion-admin ALL=(root) NOPASSWD: /usr/sbin/aide --check
bastion-admin ALL=(root) NOPASSWD: /usr/bin/lynis audit system *
bastion-admin ALL=(root) NOPASSWD: /usr/bin/aa-status

Nota sobre requiretty: Este parametro impide ejecutar sudo desde scripts no interactivos (como cron o pipelines CI/CD). En un bastion esto es deseable — todo acceso privilegiado debe ser interactivo y auditable. En otros tipos de servidor, puede requerir excepciones.

Autenticacion multifactor (2FA/MFA)

La clave publica SSH protege contra ataques de fuerza bruta, pero si un atacante roba la clave privada tiene acceso completo. El segundo factor (TOTP) mitiga este riesgo:

BASH
# Instalar
apt install -y libpam-google-authenticator

# Configurar para SSH: publickey + TOTP obligatorio
# /etc/ssh/sshd_config.d/hardening.conf (añadir)
AuthenticationMethods publickey,keyboard-interactive

# Configurar PAM para SSH
# /etc/pam.d/sshd (añadir al final)
auth required pam_google_authenticator.so nullok

# Habilitar challenge-response en SSH
# /etc/ssh/sshd_config.d/hardening.conf (añadir)
KbdInteractiveAuthentication yes

Cada usuario configura su TOTP:

BASH
# El usuario ejecuta (NO root):
google-authenticator -t -d -f -r 3 -R 30 -w 3 -Q UTF8

# Esto genera:
# - Un QR code para escanear con Google Authenticator/Authy/FreeOTP
# - Codigos de emergencia (guardar offline)
# - Fichero ~/.google_authenticator

# Opciones recomendadas:
# -t  = Time-based (TOTP)
# -d  = No permitir reutilizar token
# -f  = Forzar escritura del fichero
# -r 3 -R 30  = Rate limiting: 3 intentos cada 30 seg
# -w 3  = Window de 3 tokens (90 seg de margen)

nullok en PAM: Permite login sin 2FA si el usuario aun no ha configurado ~/.google_authenticator. Esto es util durante la transicion. Una vez todos los usuarios tienen 2FA, cambiar a auth required pam_google_authenticator.so (sin nullok) para hacerlo obligatorio.

Flujo de autenticacion resultante:

  1. Cliente presenta clave publica SSH → verificada contra authorized_keys
  2. Servidor solicita TOTP code via keyboard-interactive
  3. Usuario introduce codigo de 6 digitos de su app
  4. Acceso concedido solo si ambos factores son validos

Integracion con gestores de identidad empresarial

En entornos corporativos, las cuentas locales no escalan. La integracion con un IdP centralizado permite:

  • Gestion centralizada de accesos (altas/bajas automaticas)
  • Politicas de password unificadas
  • MFA gestionado corporativamente
  • Auditoria centralizada de accesos

FreeIPA / Red Hat IdM

BASH
# Instalar cliente FreeIPA
apt install -y freeipa-client

# Enrollar el bastion en el dominio
ipa-client-install \
  --server=ipa.example.com \
  --domain=example.com \
  --realm=EXAMPLE.COM \
  --mkhomedir \
  --ssh-trust-dns \
  --force-ntpd

# Configurar SSSD para controlar acceso por grupo
cat >> /etc/sssd/sssd.conf << 'EOF'
[domain/example.com]
access_provider = ipa
ipa_hbac_refresh = 60

# Solo permitir grupo especifico
simple_allow_groups = bastion-admins, security-team
EOF

# HBAC (Host-Based Access Control) en FreeIPA
# Crear regla que solo permita acceso al bastion desde el grupo autorizado
# ipa hbacrule-add bastion-access
# ipa hbacrule-add-host bastion-access --hosts=bastion.example.com
# ipa hbacrule-add-user bastion-access --groups=bastion-admins

# Sudo centralizado via FreeIPA
# ipa sudorule-add bastion-sudo
# ipa sudorule-add-host bastion-sudo --hosts=bastion.example.com
# ipa sudorule-add-user bastion-sudo --groups=bastion-admins
# ipa sudorule-add-runasuser bastion-sudo --users=root
# ipa sudorule-add-allow-command bastion-sudo --sudocmds="/usr/bin/journalctl"

Microsoft Entra ID (Azure AD) + SSSD

BASH
# Para entornos Microsoft, integrar via SSSD + Kerberos
apt install -y sssd sssd-tools sssd-krb5 krb5-user realmd adcli

# Unir al dominio
realm join --user=admin EXAMPLE.ONMICROSOFT.COM

# /etc/sssd/sssd.conf
[sssd]
domains = example.onmicrosoft.com
services = nss, pam, ssh, sudo

[domain/example.onmicrosoft.com]
id_provider = ad
auth_provider = ad
access_provider = ad
ad_access_filter = memberOf=CN=Bastion-Admins,OU=Security,DC=example,DC=com
sudo_provider = ad
ldap_sudo_search_base = OU=Sudoers,DC=example,DC=com

# MFA via Entra ID Conditional Access
# Se gestiona desde Azure Portal:
# - Conditional Access Policy → Require MFA for SSH access
# - Device compliance required
# - Location-based restrictions

LDAP generico (OpenLDAP, 389ds)

BASH
# /etc/sssd/sssd.conf para LDAP generico
[domain/ldap.example.com]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/ssl/certs/ca-ldap.pem
access_provider = simple
simple_allow_groups = bastion-ssh

SSH Certificate Authority (sin passwords, sin keys estaticas)

La solucion mas segura elimina authorized_keys por completo. Un CA firma certificados SSH de corta duracion:

BASH
# En sshd_config:
TrustedUserCAKeys /etc/ssh/ca-user.pub    # CA que firma certificados de usuario
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

# El usuario obtiene un certificado temporal (via Vault, Teleport, etc.)
# $ vault write ssh/sign/bastion public_key=@~/.ssh/id_ed25519.pub
# Genera un certificado valido por 8 horas con principals especificos

# Ventajas:
# - No hay authorized_keys que gestionar
# - Certificados expiran automaticamente
# - Revocacion inmediata via CRL
# - Auditoria de quien firmo que y cuando

Fase 2: KSPP completo - Parametros de kernel

El bootloader se configura con todos los parametros KSPP mas extensiones adicionales que van mas alla de las recomendaciones estandar:

💡 Consideracion enterprise: Los parametros KSPP varian segun perfil de servicio (bastion vs webserver vs database). Ver Matriz de Decision.

BASH
GRUB_CMDLINE_LINUX_DEFAULT="quiet \
  slab_nomerge \
  slub_debug=FZP \
  page_alloc.shuffle=1 \
  page_poison=1 \
  vsyscall=none \
  init_on_alloc=1 \
  init_on_free=1 \
  pti=on \
  randomize_kstack_offset=on \
  spectre_v2=on \
  spec_store_bypass_disable=seccomp \
  l1tf=full,force \
  mds=full,nosmt \
  tsx=off \
  tsx_async_abort=full,nosmt \
  mmio_stale_data=full,nosmt \
  retbleed=auto,nosmt \
  kvm.nx_huge_pages=force \
  lockdown=confidentiality \
  module.sig_enforce=1 \
  oops=panic \
  debugfs=off \
  lsm=landlock,lockdown,yama,integrity,apparmor,bpf \
  extra_latent_entropy \
  iommu=force \
  intel_iommu=on \
  efi=disable_early_pci_dma \
  audit=1 \
  audit_backlog_limit=8192"

Explicacion de parametros clave avanzados

ParametroPropositoNovedad
slub_debug=FZPFree/Zero/Poison checking en allocatorDetecta use-after-free
randomize_kstack_offset=onAleatoriza offset del stack del kernelAnti-ROP en kernel
oops=panicPanic inmediato en oopsEvita estado inconsistente
lsm=landlock,lockdown,yama,integrity,apparmor,bpfStack LSM completoMultiples capas MAC
iommu=forceForzar IOMMU para DMAAnti-DMA attacks
efi=disable_early_pci_dmaBloquear DMA pre-bootEvil-maid protection
mmio_stale_data=full,nosmtMitigacion MMIONuevas vulns CPU
retbleed=auto,nosmtMitigacion RetbleedAMD/Intel 2022+

Fase 3: Sysctl - 60+ parametros de hardening

El fichero /etc/sysctl.d/99-bastion-hardening.conf implementa restricciones que van significativamente mas alla de CIS Benchmark:

BASH
# Proteccion de informacion del kernel
kernel.kptr_restrict = 2        # Ocultar punteros kernel completamente
kernel.dmesg_restrict = 1       # Solo root puede leer dmesg
kernel.yama.ptrace_scope = 3    # No ptrace NUNCA (ni root)
kernel.kexec_load_disabled = 1  # Imposible cargar kernel alternativo
kernel.io_uring_disabled = 2    # Deshabilitar io_uring (superficie de ataque enorme)
kernel.sysrq = 0                # Sin magic sysrq

# ASLR maximo
vm.mmap_rnd_bits = 32           # 32 bits de entropia en mmap
vm.mmap_min_addr = 65536        # Prevenir null-pointer exploits

# Core dumps eliminados
fs.suid_dumpable = 0
kernel.core_pattern = |/bin/false

# Red: todo bloqueado por defecto
net.ipv4.icmp_echo_ignore_all = 1     # No responder pings
net.ipv4.tcp_timestamps = 0            # Sin timestamps (fingerprinting)
net.ipv4.tcp_sack = 0                  # Deshabilitar SACK (CVE-2019-11477)
net.ipv6.conf.all.disable_ipv6 = 1    # IPv6 innecesario = deshabilitado

# TTY hardening
dev.tty.ldisc_autoload = 0     # No autocargar line disciplines
user.max_user_namespaces = 0   # Sin user namespaces (escape de containers)

Diferencia con CIS Benchmark

CIS recomienda kernel.yama.ptrace_scope = 1. Nosotros usamos 3 (nadie puede usar ptrace, ni siquiera root). En un bastion no hay razon para debugging en produccion.

CIS no menciona kernel.io_uring_disabled. io_uring ha sido fuente de multiples CVEs criticos (CVE-2022-29582, CVE-2023-2598) y en un bastion no se necesita.


Fase 4: Seccomp-BPF System-Wide - El corazon del hardening

Concepto: Restriccion de syscalls por servicio

En Linux podemos restringir las syscalls que un proceso puede usar combinando:

  • Seccomp-BPF → filtrado de llamadas al sistema
  • Systemd sandboxing (ProtectHome, PrivateTmp) → aislamiento de filesystem
  • AppArmor → refuerzo adicional de paths y capabilities

💡 Consideracion enterprise: El generador de perfiles Seccomp permite crear perfiles adaptados por servicio, exportables a Ansible para despliegue masivo.

Generador de perfiles Seccomp

Creamos un generador que produce perfiles Seccomp organizados por niveles de privilegio:

BASH
#!/bin/bash
# seccomp-profile-generator.sh <servicio> <nivel>
# Niveles: minimal | standard | network | permissive

# STDIO: operaciones basicas de I/O y memoria
STDIO_SYSCALLS=(
    "brk" "mmap" "mprotect" "munmap" "mremap" "madvise"
    "clone" "exit" "exit_group" "getpid" "gettid"
    "rt_sigaction" "rt_sigprocmask" "rt_sigreturn"
    "futex" "nanosleep" "clock_gettime"
    "close" "read" "write" "lseek" "fstat"
    "arch_prctl" "prctl" "getrandom"
)

# RPATH: lectura de filesystem
RPATH_SYSCALLS=(
    "openat" "access" "faccessat2" "getcwd"
    "readlink" "stat" "getdents64"
)

# INET: operaciones de red
INET_SYSCALLS=(
    "socket" "bind" "connect" "listen" "accept4"
    "sendto" "recvfrom" "epoll_create1" "epoll_ctl" "epoll_wait"
)

# SIEMPRE DENEGADAS (KILL inmediato)
ALWAYS_DENY=(
    "kexec_load" "init_module" "finit_module" "delete_module"
    "bpf" "perf_event_open" "userfaultfd" "ptrace"
    "mount" "umount2" "pivot_root" "chroot"
    "io_uring_setup" "io_uring_enter" "io_uring_register"
)

El nivel minimal solo permite stdio + lectura de archivos. El nivel network anade sockets. Un servicio como auditd usa minimal, SSH usa network.

Aplicacion via systemd (por servicio)

Leccion aprendida en produccion: Un drop-in global en /etc/systemd/system/service.d/ parece atractivo pero rompe servicios criticos como sshd, que necesita setuid/setgid/sys_chroot para su privilege separation interna. El SystemCallFilter agresivo causa SIGSYS (signal 31) y mata el proceso inmediatamente. Lo mismo ocurre con ProtectSystem=strict que monta / como read-only e impide operaciones basicas.

La estrategia correcta es aplicar drop-ins individuales por servicio, excluyendo explicitamente sshd y auditd que requieren privilegios especiales:

BASH
# /etc/systemd/system/fail2ban.service.d/hardening.conf
# Aplicar tambien a: cron, systemd-timesyncd, y otros servicios no-privilegiados
[Service]
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@mount @swap @reboot @raw-io @debug @obsolete
SystemCallFilter=~io_uring_setup io_uring_enter io_uring_register
SystemCallFilter=~kexec_load kexec_file_load
SystemCallErrorNumber=EPERM
NoNewPrivileges=yes
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
MemoryDenyWriteExecute=true
RestrictNamespaces=true
LockPersonality=true
ProtectClock=true
ProtectHostname=true

Para sshd usamos un drop-in mas conservador — sandboxing de filesystem sin filtrado de syscalls:

BASH
# /etc/systemd/system/ssh.service.d/hardening.conf
[Service]
ProtectHome=read-only
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
RestrictNamespaces=true
RestrictAddressFamilies=AF_INET AF_UNIX
NoNewPrivileges=no
LockPersonality=true
ProtectClock=true
ProtectHostname=true
SystemCallArchitectures=native

Nota critica: No incluir SystemCallFilter, MemoryDenyWriteExecute=true ni ProtectSystem=strict en el drop-in de sshd. OpenSSH 9.8+ separa sshd (listener) de sshd-session (sesion), y la sesion necesita syscalls privilegiadas para PAM, PTY allocation y privilege separation. Forzar Seccomp aqui causa core-dumps inmediatos.

Por que per-service y no global

Un drop-in global en service.d/ pareceria mas seguro (deny-all), pero en la practica:

  1. Rompe servicios criticos: sshd, auditd, y systemd internals necesitan @privileged
  2. Irrecuperable remotamente: Si sshd muere por SIGSYS, solo puedes recuperar via consola fisica
  3. Falsa seguridad: El drop-in global se hereda pero no se puede "restar" — solo añadir excepciones que a menudo anulan todo el filtro

El enfoque correcto es:

  1. Drop-ins individuales para servicios que no necesitan privilegios (fail2ban, cron, timesyncd)
  2. Sandboxing conservador para sshd (filesystem + namespaces, sin syscall filter)
  3. AppArmor como capa de refuerzo para paths y capabilities (ver nota sobre sshd abajo)
  4. Auditd sin sandboxing de syscalls — necesita acceso total al netlink audit socket

Fase 5: Firewall nftables - Deny-all bidireccional

A diferencia de configuraciones tipicas que solo filtran INPUT, nuestro bastion controla tambien el trafico de salida:

💡 Consideracion enterprise: En flotas grandes, las reglas nftables se generan desde templates Jinja2 con variables por entorno (IPs de gestion, rangos SIEM).

NFT
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iif "lo" accept
        ct state established,related accept
        ct state invalid drop

        # Solo SSH desde red de gestion, rate-limited
        tcp dport 22 ip saddr 192.168.1.0/24 \
            ct state new limit rate 10/minute burst 20 packets accept

        # Log + drop todo lo demas
        limit rate 10/minute log prefix "NFT-DROP: " level warn
        counter drop
    }

    chain output {
        type filter hook output priority 0; policy drop;

        oif "lo" accept
        ct state established,related accept

        # Solo DNS, apt y NTP permitidos
        tcp dport 53 accept
        udp dport 53 accept
        tcp dport { 80, 443 } accept
        udp dport 123 accept

        # Todo lo demas logueado y dropeado
        limit rate 5/minute log prefix "NFT-OUT-DROP: " level warn
        counter drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
        counter drop
    }
}

Esto significa que si un atacante compromete un servicio, no puede establecer conexiones salientes (reverse shells, exfiltracion) excepto a puertos muy especificos.


Fase 6: Auditoria con mapeo MITRE ATT&CK

Requisito critico: El kernel Debian 13 tiene CONFIG_AUDIT=y pero audit se inicializa como disabled por defecto. Es obligatorio añadir audit=1 audit_backlog_limit=8192 a los parametros de GRUB. Sin esto, auditctl no puede abrir el socket netlink y todas las reglas fallan silenciosamente.

Las reglas de auditd usan tags que mapean directamente a tecnicas MITRE ATT&CK para correlacion inmediata con SIEMs:

BASH
# /etc/audit/rules.d/bastion.rules
-D
-b 8192
-f 2

## CREDENTIAL ACCESS (T1003)
-w /etc/shadow -p wa -k T1003_credential_access
-w /etc/gshadow -p wa -k T1003_credential_access
-w /etc/passwd -p wa -k T1003_credential_access

## PERSISTENCE (T1547/T1053/T1543)
-a always,exit -F arch=b64 -S init_module -S finit_module -S delete_module -k T1547_kernel_module
-w /etc/crontab -p wa -k T1053_cron_persistence
-w /etc/cron.d/ -p wa -k T1053_cron_persistence
-w /var/spool/cron/ -p wa -k T1053_cron_persistence
-w /etc/systemd/system/ -p wa -k T1543_systemd_persistence
-w /usr/lib/systemd/system/ -p wa -k T1543_systemd_persistence

## PRIVILEGE ESCALATION (T1548)
-a always,exit -F arch=b64 -S setuid -S setgid -S setreuid -S setregid -k T1548_privilege_change
-w /etc/sudoers -p wa -k T1548_sudo_modify
-w /etc/sudoers.d/ -p wa -k T1548_sudo_modify
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k T1548_priv_exec

## DEFENSE EVASION (T1562/T1070)
-w /etc/apparmor.d/ -p wa -k T1562_modify_apparmor
-w /usr/sbin/auditctl -p x -k T1562_tamper_audit
-w /etc/audit/ -p wa -k T1562_tamper_audit
-w /var/log/ -p wa -k T1070_log_tampering

## LATERAL MOVEMENT (T1021)
-w /etc/ssh/sshd_config -p wa -k T1021_ssh_config
-w /etc/ssh/sshd_config.d/ -p wa -k T1021_ssh_config

## EXECUTION (T1059)
-a always,exit -F arch=b64 -S execve -k T1059_execution

## REGLAS INMUTABLES tras boot (requiere reinicio para modificar)
-e 2

El flag -e 2 es critico: una vez cargadas las reglas, no se pueden modificar sin reiniciar. Un atacante que gane root no puede silenciar la auditoria.


Fase 7: AIDE con verificacion en tiempo real

AIDE se ejecuta cada 15 minutos via timer de systemd y alerta inmediatamente si detecta cambios:

BASH
# Timer: cada 15 min con jitter de 2 min
[Timer]
OnBootSec=10min
OnUnitActiveSec=15min
RandomizedDelaySec=2min
Persistent=true

Los atributos monitorizados incluyen SHA-256 + SHA-512 para archivos criticos de seguridad, haciendo extremadamente dificil crear colisiones:

CODE
SecurityFiles = p+u+g+s+b+m+c+sha256+sha512
/etc/apparmor.d SecurityFiles
/etc/seccomp SecurityFiles
/etc/ssh/sshd_config SecurityFiles
/boot Binlib

Fase 8: Telemetria centralizada - SIEM, EDR y Threat Hunting

Un bastion sin telemetria centralizada es un punto ciego. Las reglas de auditd y AIDE generan alertas locales, pero si el atacante tiene root puede manipular logs locales (a pesar de -e 2). La solucion es exfiltrar telemetria en tiempo real a un sistema externo.

💡 Consideracion enterprise: La eleccion de SIEM depende de tu stack existente. La seccion de telemetria incluye una matriz de comparacion para ayudar a decidir.

Arquitectura de telemetria

CODE
┌─────────────────────────┐
│       BASTION           │
│  auditd → audisp-remote ─────┐
│  Wazuh Agent ─────────────────┼──→ SIEM (Wazuh/Elastic/Splunk)
│  Velociraptor Client ─────────┼──→ Velociraptor Server (DFIR)
│  journald → remote ───────────┘
└─────────────────────────┘

Opcion 1: Wazuh Agent

Wazuh actua como EDR + SIEM agent en un solo componente. Monitoriza integridad de ficheros (reemplaza AIDE), recoge logs de auditd, detecta rootkits y envia todo al manager:

BASH
# Instalar Wazuh Agent
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --dearmor -o /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" > /etc/apt/sources.list.d/wazuh.list
apt update && apt install -y wazuh-agent

# Configurar manager
cat > /var/ossec/etc/ossec.conf << 'EOF'
<ossec_config>
  <client>
    <server>
      <address>WAZUH_MANAGER_IP</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <enrollment>
      <enabled>yes</enabled>
      <agent_name>bastion-prod</agent_name>
      <groups>linux,hardened,bastion</groups>
    </enrollment>
  </client>

  <!-- Monitorizar logs de auditd -->
  <localfile>
    <log_format>audit</log_format>
    <location>/var/log/audit/audit.log</location>
  </localfile>

  <!-- Integridad de ficheros criticos (syscheck) -->
  <syscheck>
    <frequency>900</frequency>
    <directories check_all="yes" realtime="yes">/etc/ssh,/etc/apparmor.d,/etc/audit,/etc/nftables.conf,/etc/sysctl.d</directories>
    <directories check_all="yes">/boot,/usr/sbin/sshd,/usr/sbin/auditd</directories>
  </syscheck>

  <!-- Deteccion de rootkits -->
  <rootcheck>
    <frequency>3600</frequency>
  </rootcheck>
</ossec_config>
EOF

systemctl enable wazuh-agent
systemctl start wazuh-agent

Opcion 2: Elastic Agent (Elastic SIEM)

BASH
# Descargar e instalar Elastic Agent
curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.x-linux-x86_64.tar.gz
tar xzf elastic-agent-*.tar.gz
cd elastic-agent-*

# Enrollar con Fleet Server
./elastic-agent install \
  --url=https://FLEET_SERVER:8220 \
  --enrollment-token=TOKEN \
  --insecure

Configurar en Kibana la integracion Auditd Manager + System para recoger automaticamente los eventos de auditd con los tags MITRE ATT&CK.

Opcion 3: Splunk Universal Forwarder

BASH
# Instalar forwarder
dpkg -i splunkforwarder-*.deb

# Configurar outputs
cat > /opt/splunkforwarder/etc/system/local/outputs.conf << 'EOF'
[tcpout]
defaultGroup = splunk_indexers
[tcpout:splunk_indexers]
server = SPLUNK_INDEXER:9997
sslCertPath = /opt/splunkforwarder/etc/certs/server.pem
sslPassword = ********
sslVerifyServerCert = true
EOF

# Monitorizar audit log
cat > /opt/splunkforwarder/etc/system/local/inputs.conf << 'EOF'
[monitor:///var/log/audit/audit.log]
sourcetype = linux:audit
index = security

[monitor:///var/log/auth.log]
sourcetype = linux_secure
index = security
EOF

Opcion 4: QRadar (envio via syslog/LEEF)

BASH
# audisp-remote para envio de audit logs via syslog
cat > /etc/audit/plugins.d/syslog.conf << 'EOF'
active = yes
direction = out
path = /sbin/audisp-syslog
type = always
args = LOG_LOCAL6
format = string
EOF

# rsyslog para enviar a QRadar
cat > /etc/rsyslog.d/qradar.conf << 'EOF'
local6.* @@QRADAR_IP:514;RSYSLOG_SyslogProtocol23Format
EOF

systemctl restart rsyslog
systemctl restart auditd

Velociraptor: Threat Hunting y DFIR en vivo

Velociraptor no es un SIEM — es una herramienta de threat hunting y respuesta a incidentes que permite ejecutar queries forenses en endpoints en tiempo real. Complementa al SIEM:

BASH
# Instalar Velociraptor client
curl -L -o /usr/local/bin/velociraptor \
  https://github.com/Velocidex/velociraptor/releases/latest/download/velociraptor-linux-amd64
chmod +x /usr/local/bin/velociraptor

# Generar config de cliente (desde el server)
# velociraptor config client > client.config.yaml

# Instalar como servicio
cat > /etc/systemd/system/velociraptor-client.service << 'EOF'
[Unit]
Description=Velociraptor Client
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/velociraptor client -config /etc/velociraptor/client.config.yaml
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

systemctl enable velociraptor-client
systemctl start velociraptor-client

Con Velociraptor puedes ejecutar VQL queries en vivo sobre el bastion:

SQL
-- Verificar que kernel params KSPP estan activos
SELECT * FROM proc_cmdline() WHERE Key =~ "lockdown|pti|slab"

-- Buscar procesos sin Seccomp
SELECT Pid, Name, Cmdline FROM pslist()
WHERE NOT SeccompMode

-- Verificar integridad de ficheros criticos
SELECT FullPath, hash(path=FullPath, hashselect="SHA256") as Hash
FROM glob(globs="/etc/ssh/**")

-- Detectar modulos kernel cargados (deberian ser minimos)
SELECT * FROM modules()

Consideraciones de nftables para telemetria

El firewall deny-all de salida requiere reglas adicionales para permitir el envio de telemetria:

NFT
# Añadir al chain output para permitir envio a SIEM
# Wazuh
tcp dport 1514 ip daddr WAZUH_MANAGER_IP accept
# Elastic
tcp dport 8220 ip daddr FLEET_SERVER_IP accept
# Splunk
tcp dport 9997 ip daddr SPLUNK_INDEXER_IP accept
# Velociraptor
tcp dport 8000 ip daddr VELOCIRAPTOR_SERVER_IP accept
# Syslog (QRadar)
tcp dport 514 ip daddr QRADAR_IP accept

Matriz de comparacion

CapacidadWazuhElastic SIEMSplunkQRadarVelociraptor
Log collectionSiSiSiSiNo (no es SIEM)
File integritySi (syscheck)Con moduloCon addonNo nativoSi (VQL)
Rootkit detectionSiNoNoNoSi (hunting)
MITRE mappingSiSiSi (ES)SiSi
Threat huntingLimitadoKQLSPLAQLVQL (avanzado)
DFIR liveNoNoNoNoSi
CosteOpen sourceLicenciaLicenciaLicenciaOpen source
RecomendacionEnterpriseEnterpriseEnterpriseEnterpriseComplemento

Recomendacion para bastiones

La combinacion ideal para un bastion es:

  1. Wazuh Agent como SIEM agent + FIM + rootkit detection (reemplaza AIDE)
  2. Velociraptor Client como herramienta de hunting/DFIR bajo demanda
  3. auditd con reglas MITRE como fuente de eventos del kernel (inmutable)

Con esta combinacion tienes: deteccion en tiempo real (Wazuh), auditoria inmutable del kernel (auditd -e 2), verificacion de integridad (Wazuh syscheck), y capacidad de investigacion forense en vivo (Velociraptor).


Fase 9: Blacklisting de modulos de kernel

Un bastion no necesita USB, Bluetooth, wireless ni protocolos de red exoticos. Cada modulo innecesario es superficie de ataque:

BASH
# /etc/modprobe.d/bastion-blacklist.conf

# Almacenamiento USB (vector de exfiltracion)
blacklist usb-storage
install usb-storage /bin/false

# Protocolos con historico de vulnerabilidades
blacklist dccp       # CVE-2017-6074
blacklist sctp       # Multiples CVEs
blacklist rds        # CVE-2010-3904
blacklist tipc       # CVE-2022-0435

# DMA attacks
blacklist firewire-core
blacklist thunderbolt
install thunderbolt /bin/false

# Wireless/Bluetooth (innecesario en servidor)
blacklist cfg80211
blacklist bluetooth

Fase 10: SSH - Algoritmos post-cuanticos

La configuracion SSH usa los algoritmos mas fuertes disponibles, incluyendo intercambio de claves resistente a computacion cuantica:

CODE
# /etc/ssh/sshd_config.d/hardening.conf

# Acceso restringido
Port 22
AddressFamily inet
ListenAddress 192.168.1.200
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30

# Key exchange: sntrup761 es hibrido post-cuantico + X25519
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256

# Solo cifrados AEAD
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com

# MACs Encrypt-then-MAC
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Host keys (mantener RSA para compatibilidad con clientes legacy)
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

# Zero forwarding
AllowTcpForwarding no
AllowAgentForwarding no
AllowStreamLocalForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
PermitUserEnvironment no

# Timeouts
ClientAliveInterval 300
ClientAliveCountMax 2

# Logging verbose para correlacion con auditd
LogLevel VERBOSE
Banner /etc/issue.net

Nota sobre AppArmor y sshd: En Debian 13, OpenSSH 9.8+ separa el proceso en sshd (listener) y sshd-session (sesion de usuario). Crear un perfil AppArmor personalizado para sshd es extremadamente fragil — la transicion de perfil entre ambos procesos requiere paths exactos que varian entre versiones y el modo enforce causa "Connection reset" sin generar logs DENIED utiles. La recomendacion es proteger sshd via: (1) config hardened, (2) nftables rate-limiting, (3) fail2ban, (4) auditd monitoring. El sandboxing real lo proporciona la privilege separation interna de OpenSSH.


Validacion: Security Test Suite

El bastion incluye un script de validacion automatizada que verifica las 12 areas de hardening:

BASH
$ sudo /usr/local/bin/security-test-suite.sh

============================================================
 BASTION SECURITY TEST SUITE
============================================================

[1/12] Parametros de kernel KSPP
  ✓ slab_nomerge
  ✓ init_on_alloc=1
  ✓ pti=on
  ✓ lockdown=confidentiality
  ...

[4/12] Seccomp-BPF en servicios
  ✓ ssh: SystemCallFilter activo
  ✓ auditd: drop-in de hardening presente
  ✓ Perfiles Seccomp generados: 3

[5/12] Firewall nftables
  ✓ nftables activo
  ✓ Politica deny-all (input + forward)

============================================================
 RESULTADOS
  Passed:   42 / 45
  Warnings: 3 / 45
  Failed:   0 / 45

  ESTADO: BASTION OPERATIVO
============================================================

Modo de emergencia

Para situaciones donde el hardening impide diagnosticar problemas, existe un modo auditoria que cambia bloqueos por logs:

BASH
# Modo auditoria: AppArmor complain + Seccomp log (sin bloquear)
sudo /usr/local/bin/bastion-emergency.sh audit-mode

# Monitorizar que se habria bloqueado
journalctl -f | grep -i 'apparmor\|seccomp\|denied'

# Restaurar hardening completo
sudo /usr/local/bin/bastion-emergency.sh restore-hardening

Validacion con Lynis

Resultado de lynis audit system en el servidor con todo el hardening aplicado:

CODE
Hardening index : 79 [###############     ]
Tests performed : 256
Warnings        : 1 (AIDE database no inicializada aun)
Suggestions     : 26

Score 79/100 es solido para un bastion con esta configuracion. Las sugerencias pendientes son menores:

  • GRUB password — no aplicable en VM con consola restringida via hipervisor
  • TCPKeepAlive=NO en SSH — preferimos mantenerlo para detectar conexiones muertas
  • umask 027 — ya aplicado via PAM pero login.defs usa 022 como fallback
  • process accounting — cubierto por auditd con reglas MITRE
  • malware scanner — Wazuh FIM + AIDE cubren esta funcion

Para subir el score a 85+ basta con: inicializar la base de datos AIDE (aideinit), instalar libpam-tmpdir, y configurar un GRUB password.


Metricas de exito operacional

Un score de Lynis no significa que estes seguro. Estas metricas si indican si el hardening esta funcionando:

Prevencion

MetricaComo medirObjetivo
Syscalls bloqueadas por Seccomp`journalctl -kgrep -c seccomp`0 en operacion normal (bloqueos = ataque o misconfiguracion)
Conexiones bloqueadas por nftablesnft list countersTendencia estable; picos = escaneo externo
Modulos kernel rechazadosregla auditd init_module0 intentos no autorizados

Deteccion

MetricaComo medirObjetivo
Tiempo medio de deteccion (MTTD)Timestamp auditd → timestamp alerta SIEM< 5 min para eventos criticos
Falsos positivos por diaAlertas SIEM marcadas como FP< 5/dia por bastion
Cobertura MITRE ATT&CKReglas auditd con tag T* / tecnicas aplicables> 80%

Resiliencia

MetricaComo medirObjetivo
MTTR de configuracionSimular drift → tiempo hasta restaurar baseline< 15 min con Ansible
Impacto en rendimientoBenchmark pre/post hardening< 10% degradacion
Exito de recuperacionEjecutar runbook de emergencia trimestralmente100%

Estas metricas deben alimentar un dashboard ejecutivo. La seguridad que no se mide, no se gestiona.


Conclusiones

Este bastion implementa 10 capas de seguridad que operan de forma independiente, validadas en un entorno real (Debian 13 Trixie, kernel 6.12.88). Un atacante necesitaria:

  1. Evadir el firewall nftables (deny-all bidireccional, rate-limited)
  2. Autenticarse via SSH con clave publica (sin password, algoritmos post-cuanticos)
  3. Escapar del sandbox Seccomp-BPF (syscalls criticas = KILL en servicios no-privilegiados)
  4. Evitar deteccion por auditd (reglas inmutables -e 2, 20 reglas MITRE ATT&CK)
  5. No disparar alertas del SIEM (telemetria exfiltrada en tiempo real, irrevocable localmente)
  6. No disparar AIDE/Wazuh syscheck (verificacion cada 15 min, hash SHA-512)
  7. Todo esto en un kernel con KSPP completo (lockdown=confidentiality, pti, ASLR 32-bit)

Lecciones aprendidas en produccion

Durante la implementacion real encontramos que:

  • ProtectSystem=strict en drop-ins globales rompe el sistema: Monta / como read-only para todos los servicios incluyendo los de systemd-core. Solo usar per-service.
  • SystemCallFilter es incompatible con sshd: OpenSSH necesita @privileged (setuid, setgid, chroot) para su privilege separation. Forzar Seccomp aqui causa SIGSYS (signal 31) y core-dump inmediato.
  • AppArmor enforce para sshd es fragil en Debian 13: La separacion sshd/sshd-session (OpenSSH 9.8+) hace que los perfiles requieran mantenimiento constante entre versiones.
  • audit=1 es obligatorio en GRUB: Sin este parametro, el subsistema audit se inicializa como disabled aunque CONFIG_AUDIT=y en el kernel.
  • Rate-limit agresivo en nftables causa lockouts: 3/minute con burst 5 es demasiado restrictivo durante testing. Usar 10/minute burst 20 y reducir en produccion.
  • tcp_wrappers (libwrap) sigue activo en Debian 13: sshd-session intenta leer /etc/hosts.allow y /etc/hosts.deny. Si AppArmor o sandboxing impide el acceso, la conexion se rechaza silenciosamente.

La filosofia es clara: no hay confianza implicita. Cada capa asume que las anteriores han sido comprometidas. Pero la implementacion debe ser pragmatica — un sistema que no puedes administrar remotamente no es seguro, es inutil.

Toda la configuracion es aplicable en cualquier entorno — desde un portatil con Debian hasta un cluster VMware empresarial. Los ficheros de configuracion son los mismos independientemente de donde se despliegue.


Mapeo a frameworks regulatorios

Las reglas de auditd mapean a MITRE ATT&CK, pero en entornos regulados se necesita trazabilidad a frameworks de cumplimiento:

Control tecnicoCIS BenchmarkPCI DSS 4.0NIST 800-53ISO 27001SOC 2
kptr_restrict=21.5.2Req 2.2.1SC-39A.12.6.1CC6.1
ptrace_scope=31.5.4Req 2.2.1AC-3A.9.4.1CC6.1
nftables deny-all3.5.xReq 1.2-1.5SC-7A.13.1.1CC6.6
SSH key-only5.3.xReq 8.3.1IA-2A.9.2.1CC6.1
auditd -e 24.1.xReq 10.2-10.3AU-9A.12.4.2CC7.2
Seccomp per-service-Req 2.2.1SC-39A.12.6.1CC6.1
AIDE/FIM1.3.xReq 11.5SI-7A.12.4.3CC7.1
lockdown=confidentiality-Req 2.2.1SC-34A.12.5.1CC6.1
Telemetria a SIEM4.1.xReq 10.6AU-6A.12.4.1CC7.2

Limitaciones y vectores de ataque residuales

Ningun hardening es absoluto. Esta seccion documenta honestamente lo que esta guia NO resuelve y los vectores que requieren capas adicionales:

Lo que lockdown + KSPP no cubre

VectorPor que persisteMitigacion adicional
Use-after-free en codigo kernel validolockdown bloquea acceso directo a memoria, no bugs logicosIntel TDX / AMD SEV-SNP con memoria cifrada por hardware
Side-channels (Spectre variantes nuevas)Mitigaciones cubren variantes conocidas, no futurasMicrocode updates + isolation por CPU pinning
Firmware/UEFI comprometidolockdown opera a nivel kernel, no firmwareCoreboot + Heads, measured boot con TPM 2.0
Intel ME / AMD PSPCoprocesadores con acceso total a memoriaHardware sin ME (System76, Purism) o me_cleaner
Modulos firmados con clave robadamodule.sig_enforce valida firma, no integridad de la CAClaves de firma en HSM (YubiHSM), rotacion periodica

Lo que Seccomp-BPF no puede bloquear

  • Vulnerabilidades dentro de syscalls permitidas: un buffer overflow en read() no requiere syscalls extra
  • Exfiltracion via syscalls legitimas: sendto() a un servidor permitido por nftables
  • Nuevas syscalls en kernels futuros: si no se actualiza la lista, quedan permitidas por defecto

Mitigacion: combinar Seccomp con Landlock (restriccion de filesystem paths), eBPF LSM para politicas dinamicas, o micro-VMs (Firecracker/gVisor) para aislamiento completo.

Red: vectores residuales con deny-all

  • Conexiones establecidas: ct state established accept permite tunelizar trafico sobre conexiones legitimas
  • DNS tunneling: si se permite UDP/53 saliente, herramientas como iodine/dnscat2 pueden exfiltrar datos
  • Compromiso de destinos permitidos: si el servidor apt o NTP es comprometido, el trafico es "legitimo"

Mitigacion: DNS proxy local con allowlist de dominios, mTLS para todas las conexiones salientes, egress filtering a nivel de FQDN (no solo IP).

Autenticacion: el eslabon humano

  • TOTP es phishable: un atacante puede obtener el codigo en tiempo real via sitio falso
  • Certificados SSH robados: validos hasta expiracion aunque se detecte compromiso
  • Administrador comprometido: si el endpoint del admin tiene keylogger, todo el hardening es irrelevante

Mitigacion: FIDO2/WebAuthn (resistente a phishing), certificados con TTL < 1 hora, Privileged Access Workstations (PAW) dedicadas.

Auditoria: lo que auditd no ve

  • Atacante con root puede saturar el buffer de audit antes de ejecutar acciones maliciosas
  • Si el SIEM receptor es comprometido, los logs pueden ser manipulados o ignorados
  • AIDE puede ser evadido si el atacante actualiza la base de datos antes del siguiente check

Mitigacion: WORM storage para logs (S3 Object Lock), multi-party logging a SIEMs independientes, fs-verity para integridad a nivel de bloque.

Supply chain: la confianza transitiva

Cada paquete instalado via apt depende de una cadena de confianza:

  1. Mirror de Debian no comprometido
  2. Claves GPG de firma no robadas
  3. Dependencias transitivas (libssl, libc6) sin backdoors

Mitigacion: reproducible builds, binary transparency logs, IMA/EVM para verificacion de binarios en runtime, version pinning estricto.

Conclusion sobre limitaciones

Esta guia cubre solidamente las capas software de hardening (kernel, userspace, red, autenticacion, auditoria). Para entornos de maxima sensibilidad (defensa, infraestructura critica) se requieren ademas:

  • Hardware trust: TPM 2.0, measured boot, remote attestation
  • Aislamiento fisico: air-gapped SIEMs, HSMs para claves
  • Procesos organizativos: Zero Trust para administradores, just-in-time access, PAWs

La seguridad es un proceso continuo, no un estado final. Este bastion es un punto de partida solido — no un destino.


Referencias

Comentarios