Implementación del Framework de Ciberseguridad NIST en Linux

El Framework de Ciberseguridad del NIST (NIST CSF) proporciona un conjunto de estándares, directrices y mejores prácticas para gestionar el riesgo de ciberseguridad en infraestructura crítica. Organizado en cinco funciones principales—Identificar, Proteger, Detectar, Responder y Recuperar—el CSF es flexible y se adapta tanto a pequeñas organizaciones como a grandes empresas. Esta guía cubre la implementación práctica del NIST CSF en servidores Linux, con controles técnicos concretos para cada función.

Requisitos Previos

  • Servidores Linux: Ubuntu 20.04+, CentOS 8+, Rocky Linux 8+
  • Acceso root o privilegios sudo
  • Herramientas: auditd, fail2ban, AIDE, nmap, openssl
  • Documentación de arquitectura de red e inventario de activos inicial

IDENTIFICAR: Gestión de Activos y Riesgos

La función Identificar (ID) establece el contexto organizacional y la comprensión de los activos que necesitan protección.

Inventario de Activos (ID.AM)

# Script de inventario automático de activos del servidor
cat > /usr/local/bin/nist-inventory.sh << 'EOF'
#!/bin/bash
# NIST CSF ID.AM-1: Inventario de activos físicos y de software
INVENTARIO="/opt/nist-csf/inventario"
mkdir -p "$INVENTARIO"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Información del sistema
{
    echo "=== INVENTARIO DE ACTIVOS NIST CSF ==="
    echo "Fecha: $(date)"
    echo "Hostname: $(hostname -f)"
    echo "IP: $(hostname -I)"
    echo ""

    echo "--- Sistema Operativo ---"
    cat /etc/os-release

    echo ""
    echo "--- Hardware ---"
    lscpu | grep -E "Architecture|CPU\(s\)|Model name"
    free -h | head -2
    df -h --total | tail -1

    echo ""
    echo "--- Servicios Activos ---"
    systemctl list-units --type=service --state=running --no-pager

    echo ""
    echo "--- Puertos en Escucha ---"
    ss -tlnp

    echo ""
    echo "--- Paquetes Instalados ---"
    dpkg -l 2>/dev/null || rpm -qa 2>/dev/null

} > "$INVENTARIO/asset-inventory-$TIMESTAMP.txt"

echo "Inventario guardado: $INVENTARIO/asset-inventory-$TIMESTAMP.txt"
EOF
chmod +x /usr/local/bin/nist-inventory.sh
/usr/local/bin/nist-inventory.sh

Evaluación de Riesgos (ID.RA)

# Script de evaluación de riesgos técnicos
cat > /usr/local/bin/nist-risk-assess.sh << 'EOF'
#!/bin/bash
# NIST CSF ID.RA: Evaluación de riesgos
REPORTE="/opt/nist-csf/risk-assessment-$(date +%Y%m%d).txt"
mkdir -p /opt/nist-csf

echo "=== EVALUACIÓN DE RIESGOS NIST CSF ===" > "$REPORTE"
echo "Fecha: $(date)" >> "$REPORTE"

# Verificar actualizaciones de seguridad pendientes
echo -e "\n[RIESGO: Actualizaciones pendientes]" >> "$REPORTE"
apt list --upgradable 2>/dev/null | grep -i security | wc -l >> "$REPORTE"

# Verificar puertos abiertos
echo -e "\n[RIESGO: Puertos expuestos]" >> "$REPORTE"
ss -tlnp >> "$REPORTE"

# Verificar cuentas privilegiadas
echo -e "\n[RIESGO: Cuentas privilegiadas]" >> "$REPORTE"
getent group sudo wheel | head -5 >> "$REPORTE"

# Verificar contraseñas débiles o vacías
echo -e "\n[RIESGO: Cuentas sin contraseña]" >> "$REPORTE"
awk -F: '($2 == "" || $2 == "!") {print $1 " - SIN CONTRASEÑA"}' /etc/shadow >> "$REPORTE"

echo "Evaluación guardada: $REPORTE"
EOF
chmod +x /usr/local/bin/nist-risk-assess.sh

PROTEGER: Implementación de Salvaguardas

Gestión de Identidad y Acceso (PR.AC)

# Configurar control de acceso según NIST CSF PR.AC-1
# Implementar política de contraseñas fuerte
cat > /etc/security/pwquality.conf << 'EOF'
# NIST CSF PR.AC-1: Política de contraseñas
minlen = 12
minclass = 3
maxrepeat = 2
maxsequence = 3
ucredit = -1
lcredit = -1
dcredit = -1
ocredit = -1
EOF

# Configurar PAM para usar pwquality
# En /etc/pam.d/common-password (Ubuntu)
grep -q "pam_pwquality" /etc/pam.d/common-password || \
    sed -i '/pam_unix.so/i password requisite pam_pwquality.so retry=3' \
    /etc/pam.d/common-password

# Configurar SSH con autenticación fuerte
cat > /etc/ssh/sshd_config.d/nist-csf.conf << 'EOF'
# NIST CSF PR.AC-3: Acceso remoto seguro
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
Protocol 2
MaxAuthTries 3
LoginGraceTime 60
AllowAgentForwarding no
EOF

systemctl reload sshd

Seguridad de Red (PR.AC-5, PR.PT-4)

# Configurar firewall con UFW (Ubuntu) o firewalld (CentOS)
# Ubuntu/Debian
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 443/tcp
ufw enable

# Verificar reglas
ufw status verbose

# Para CentOS/Rocky Linux
# firewall-cmd --permanent --add-service=ssh
# firewall-cmd --permanent --add-service=https
# firewall-cmd --reload

# Configurar fail2ban para protección contra fuerza bruta (PR.AC-4)
apt install fail2ban -y

cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd

[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 86400
EOF

systemctl enable --now fail2ban

Capacitación y Concienciación (PR.AT)

# Banner de inicio de sesión con aviso legal (PR.AT-1)
cat > /etc/motd << 'EOF'

=========================================
  SISTEMA PRIVADO - ACCESO RESTRINGIDO
=========================================
Este sistema es propiedad de [Empresa].
El acceso no autorizado está prohibido y
puede ser objeto de acciones legales.
Todas las actividades son monitoreadas
y registradas.
=========================================
EOF

cat > /etc/issue.net << 'EOF'
AVISO: Sistema de acceso restringido.
Solo usuarios autorizados pueden continuar.
EOF

# Habilitar banner en SSH
echo "Banner /etc/issue.net" >> /etc/ssh/sshd_config.d/nist-csf.conf
systemctl reload sshd

Protección de Datos (PR.DS)

# NIST CSF PR.DS-1: Protección de datos en reposo
# Verificar y configurar permisos de archivos sensibles
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 600 /etc/ssh/ssh_host_*_key  # Claves privadas SSH
chmod 644 /etc/ssh/ssh_host_*_key.pub  # Claves públicas SSH

# NIST CSF PR.DS-2: Protección de datos en tránsito
# Verificar configuración TLS
openssl s_client -connect localhost:443 2>/dev/null | grep -E "Protocol|Cipher"

# Configurar eliminación segura de datos
apt install secure-delete  # Ubuntu
# Uso: srm -v archivo-sensible.txt

DETECTAR: Monitoreo de Seguridad

Detección de Anomalías (DE.AE)

# Configurar auditd para detección de eventos de seguridad
cat > /etc/audit/rules.d/nist-csf.rules << 'EOF'
# NIST CSF DE.AE-1: Establecer línea base de comportamiento

# Detectar escalada de privilegios
-a always,exit -F arch=b64 -S setuid -F a0=0 -F exe=/usr/bin/sudo -k privilege-escalation
-w /usr/bin/sudo -p x -k sudo-usage

# Detectar modificaciones del sistema
-w /etc/cron.d/ -p wa -k cron-changes
-w /etc/crontab -p wa -k cron-changes
-w /var/spool/cron/ -p wa -k cron-changes

# Detectar inicio de nuevos servicios
-w /etc/systemd/system/ -p wa -k service-changes
-w /lib/systemd/system/ -p wa -k service-changes

# Detectar cambios en binarios del sistema
-w /usr/bin/ -p wa -k bin-changes
-w /usr/sbin/ -p wa -k bin-changes
-w /bin/ -p wa -k bin-changes

# Detectar acceso a claves SSH
-w /root/.ssh/ -p rwa -k ssh-access
-a always,exit -F arch=b64 -S open,openat -F dir=/home -F name=authorized_keys -k ssh-access
EOF

augenrules --load

Monitoreo Continuo (DE.CM)

# Script de monitoreo continuo NIST CSF
cat > /usr/local/bin/nist-monitor.sh << 'EOF'
#!/bin/bash
# NIST CSF DE.CM-1: Monitoreo continuo de la red y el sistema
LOG="/var/log/nist-monitoring.log"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Verificar nuevas conexiones de red sospechosas
CONNS_EXTERNAS=$(ss -tn state established | tail -n +2 | \
    awk '{print $5}' | grep -v "127\." | grep -v "::1" | wc -l)

# Verificar uso de CPU anormal (posible minería de criptomonedas)
CPU_CARGA=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')

# Verificar procesos con privilegios elevados inusuales
PROC_ROOT=$(ps aux | awk '$1=="root" && $11!~/^\[/ {print $11}' | sort | uniq | wc -l)

echo "$TIMESTAMP CONEXIONES_EXT=$CONNS_EXTERNAS CPU_CARGA=$CPU_CARGA PROC_ROOT=$PROC_ROOT" \
    >> "$LOG"

# Alertar si hay valores anormales
if [ "${CPU_CARGA%.*}" -gt 10 ] 2>/dev/null; then
    logger -t nist-csf "ALERTA DE.CM: CPU carga alta: $CPU_CARGA"
fi
EOF
chmod +x /usr/local/bin/nist-monitor.sh
echo "*/5 * * * * root /usr/local/bin/nist-monitor.sh" > /etc/cron.d/nist-monitor

RESPONDER: Gestión de Incidentes

Plan de Respuesta (RS.RP)

# Script de respuesta a incidentes NIST CSF
cat > /usr/local/bin/nist-incident-response.sh << 'EOF'
#!/bin/bash
# NIST CSF RS.RP-1: Plan de respuesta a incidentes
INCIDENTE_ID="INC-$(date +%Y%m%d-%H%M%S)"
DIR_INCIDENTE="/opt/nist-csf/incidents/$INCIDENTE_ID"
mkdir -p "$DIR_INCIDENTE"

echo "=== RESPUESTA A INCIDENTE: $INCIDENTE_ID ===" | tee "$DIR_INCIDENTE/timeline.txt"
echo "Inicio: $(date)" | tee -a "$DIR_INCIDENTE/timeline.txt"

# Capturar estado del sistema en el momento del incidente
echo "--- Conexiones activas ---" >> "$DIR_INCIDENTE/system-state.txt"
ss -tnp >> "$DIR_INCIDENTE/system-state.txt"

echo "--- Procesos en ejecución ---" >> "$DIR_INCIDENTE/system-state.txt"
ps aux >> "$DIR_INCIDENTE/system-state.txt"

echo "--- Usuarios conectados ---" >> "$DIR_INCIDENTE/system-state.txt"
who >> "$DIR_INCIDENTE/system-state.txt"
last -n 20 >> "$DIR_INCIDENTE/system-state.txt"

echo "--- Últimas entradas de log ---" >> "$DIR_INCIDENTE/system-state.txt"
journalctl -n 200 --no-pager >> "$DIR_INCIDENTE/system-state.txt"

echo "--- Logs de autenticación ---" >> "$DIR_INCIDENTE/system-state.txt"
tail -100 /var/log/auth.log >> "$DIR_INCIDENTE/system-state.txt" 2>/dev/null

echo "Evidencia de incidente guardada en: $DIR_INCIDENTE"
echo "ID de incidente: $INCIDENTE_ID"
EOF
chmod +x /usr/local/bin/nist-incident-response.sh

Comunicaciones de Incidente (RS.CO)

# Configurar notificación automática de incidentes
cat > /usr/local/bin/nist-alert.sh << 'EOF'
#!/bin/bash
# NIST CSF RS.CO-2: Alertas de incidentes de seguridad
TIPO=$1
DETALLE=$2
DESTINATARIO="[email protected]"

if [ -z "$TIPO" ]; then
    echo "Uso: $0 <tipo-incidente> <descripcion>"
    exit 1
fi

MENSAJE="Sistema: $(hostname)
Fecha: $(date)
Tipo de incidente: $TIPO
Descripción: $DETALLE

Conexiones activas:
$(ss -tn state established | head -10)

Usuarios conectados:
$(who)"

echo "$MENSAJE" | mail -s "ALERTA NIST CSF: $TIPO en $(hostname)" "$DESTINATARIO"
logger -t nist-csf "INCIDENTE: $TIPO - $DETALLE"
EOF
chmod +x /usr/local/bin/nist-alert.sh

RECUPERAR: Planes de Recuperación

Plan de Recuperación (RC.RP)

# Script de verificación de recuperación post-incidente
cat > /usr/local/bin/nist-recovery-verify.sh << 'EOF'
#!/bin/bash
# NIST CSF RC.RP-1: Verificación post-recuperación
REPORTE="/opt/nist-csf/recovery-verify-$(date +%Y%m%d_%H%M%S).txt"

echo "=== VERIFICACIÓN DE RECUPERACIÓN NIST CSF ===" > "$REPORTE"
echo "Fecha: $(date)" >> "$REPORTE"

# Verificar servicios críticos
echo -e "\n[SERVICIOS]" >> "$REPORTE"
for SERVICIO in ssh nginx apache2 mysql postgresql; do
    STATUS=$(systemctl is-active "$SERVICIO" 2>/dev/null || echo "no instalado")
    echo "$SERVICIO: $STATUS" >> "$REPORTE"
done

# Verificar integridad del sistema de archivos
echo -e "\n[SISTEMA DE ARCHIVOS]" >> "$REPORTE"
df -h >> "$REPORTE"

# Verificar conectividad de red
echo -e "\n[CONECTIVIDAD]" >> "$REPORTE"
ping -c 3 8.8.8.8 >> "$REPORTE" 2>&1

# Verificar logs recientes de errores
echo -e "\n[ERRORES RECIENTES]" >> "$REPORTE"
journalctl -p err -n 20 --no-pager >> "$REPORTE"

echo "Verificación guardada: $REPORTE"
cat "$REPORTE"
EOF
chmod +x /usr/local/bin/nist-recovery-verify.sh

Evaluación de Madurez

Medir el Nivel de Madurez del CSF

# Script de evaluación del nivel de implementación del NIST CSF
cat > /usr/local/bin/nist-maturity.sh << 'EOF'
#!/bin/bash
# Evaluación de madurez NIST CSF (Tiers 1-4)
PUNTOS=0
TOTAL=10

echo "=== EVALUACIÓN DE MADUREZ NIST CSF ==="

# ID: Inventario de activos
[ -f /opt/nist-csf/inventario/asset-inventory-*.txt ] && \
    { echo "[PASS] ID.AM: Inventario de activos"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] ID.AM: Sin inventario de activos"

# PR: SSH sin contraseña
grep -q "PasswordAuthentication no" /etc/ssh/sshd_config* 2>/dev/null && \
    { echo "[PASS] PR.AC: SSH solo con claves"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] PR.AC: SSH permite contraseñas"

# PR: Firewall activo
ufw status 2>/dev/null | grep -q "active" && \
    { echo "[PASS] PR.PT: Firewall activo"; PUNTOS=$((PUNTOS+1)); } || \
    firewall-cmd --state 2>/dev/null | grep -q "running" && \
    { echo "[PASS] PR.PT: Firewall activo"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] PR.PT: Sin firewall activo"

# PR: fail2ban activo
systemctl is-active fail2ban &>/dev/null && \
    { echo "[PASS] PR.AC: fail2ban activo"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] PR.AC: fail2ban no activo"

# DE: auditd activo
systemctl is-active auditd &>/dev/null && \
    { echo "[PASS] DE.CM: auditd activo"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] DE.CM: auditd no activo"

# DE: Reglas NIST auditd
auditctl -l 2>/dev/null | grep -q "nist" && \
    { echo "[PASS] DE.AE: Reglas de auditoría NIST"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] DE.AE: Sin reglas de auditoría NIST"

# RS: Script de respuesta disponible
[ -x /usr/local/bin/nist-incident-response.sh ] && \
    { echo "[PASS] RS.RP: Plan de respuesta disponible"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] RS.RP: Sin plan de respuesta"

# RC: Backups configurados
crontab -l 2>/dev/null | grep -qi backup || \
    ls /etc/cron.d/ | grep -qi backup && \
    { echo "[PASS] RC.RP: Backups configurados"; PUNTOS=$((PUNTOS+1)); } || \
    echo "[FAIL] RC.RP: Sin backups configurados"

echo ""
echo "Puntuación: $PUNTOS/$TOTAL"
TIER=$((PUNTOS * 4 / TOTAL))
[ $TIER -lt 1 ] && TIER=1
echo "Nivel de Madurez (Tier): $TIER/4"
EOF
chmod +x /usr/local/bin/nist-maturity.sh
/usr/local/bin/nist-maturity.sh

Solución de Problemas

El script de inventario no captura servicios:

# Verificar que systemctl está disponible
systemctl list-units --type=service --state=running
# Si falla, usar service como alternativa
service --status-all 2>&1 | grep "+"

Reglas de auditoría no se cargan tras reinicio:

systemctl status auditd
ls -la /etc/audit/rules.d/
augenrules --load
systemctl restart auditd

fail2ban bloqueando IPs legítimas:

# Ver IPs actualmente bloqueadas
fail2ban-client status sshd
# Desbloquear una IP específica
fail2ban-client set sshd unbanip 192.168.1.100
# Añadir IP a la lista blanca en jail.local
echo "ignoreip = 127.0.0.1/8 192.168.1.0/24" >> /etc/fail2ban/jail.local
systemctl restart fail2ban

Evaluación de madurez reporta niveles bajos:

# Ejecutar cada sección de implementación faltante
/usr/local/bin/nist-inventory.sh   # ID
ufw enable                          # PR
systemctl enable --now auditd       # DE

Conclusión

El Framework de Ciberseguridad del NIST proporciona una estructura completa y adaptable para gestionar el riesgo de seguridad en infraestructura Linux. Implementar los controles de las cinco funciones—Identificar, Proteger, Detectar, Responder y Recuperar—crea una postura de seguridad en capas que reduce el riesgo de brechas y mejora la capacidad de respuesta ante incidentes. Realiza evaluaciones de madurez periódicas para medir el progreso y priorizar las mejoras más urgentes.