Scripts con Privilegios Sudo

Desarrollo seguro de scripts administrativos con manejo responsable de privilegios elevados

Módulo 10 ⏱️ 35-40 min 🛡️ Seguridad 👑 Sudo 📊 Avanzado

Introducción a Sudo y Seguridad

Sudo (Super User Do) es una herramienta fundamental en sistemas Unix/Linux que permite a usuarios ejecutar comandos con privilegios de otros usuarios, típicamente root. El desarrollo de scripts que utilizan sudo requiere un entendimiento profundo de las implicaciones de seguridad y las mejores prácticas para minimizar riesgos.

La administración responsable de privilegios elevados es crucial para mantener la seguridad del sistema. Un script mal diseñado con privilegios sudo puede comprometer completamente la seguridad del servidor, permitir escalación de privilegios no autorizada o causar daños irreparables al sistema.

Principios de Seguridad Fundamentales
  • Principio de menor privilegio: Solo otorgar los permisos mínimos necesarios
  • Validación exhaustiva: Validar todas las entradas y parámetros
  • Auditoría completa: Registrar todas las acciones privilegiadas
  • Manejo seguro de errores: Fallar de manera segura sin exponer información
¿Cuándo usar sudo en scripts?

Scripts con sudo son apropiados para:

  • Tareas de mantenimiento del sistema automatizadas
  • Scripts de instalación y configuración
  • Operaciones de backup y restauración
  • Gestión de servicios y usuarios
  • Monitoreo y alertas del sistema

Configuración Segura de Sudo

Configuración del archivo sudoers

La configuración correcta del archivo /etc/sudoers es fundamental para la seguridad.

Configuración sudoers segura Copiar
# SIEMPRE editar con visudo, nunca directamente
sudo visudo

# Configuración básica segura en /etc/sudoers

# Configuraciones por defecto
Defaults    env_reset
Defaults    mail_badpass
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Defaults    use_pty
Defaults    log_input, log_output
Defaults    iolog_dir=/var/log/sudo-io
Defaults    logfile=/var/log/sudo.log

# Usuario específico para scripts administrativos
script_admin ALL=(ALL) NOPASSWD: /usr/local/bin/system_maintenance.sh

# Grupo para operaciones específicas
%backup_operators ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart backup-service, \
                                     /usr/bin/systemctl status backup-service, \
                                     /bin/cp /var/backups/* /mnt/backup/

# Usuario con comandos limitados y contraseña
deploy_user ALL=(ALL) PASSWD: /bin/systemctl restart apache2, \
                              /bin/systemctl reload apache2, \
                              /usr/bin/git -C /var/www/html pull

# Ejemplos de configuraciones restrictivas
monitoring_user ALL=(ALL) NOPASSWD: /bin/cat /var/log/*.log, \
                                    /usr/bin/tail -f /var/log/*.log, \
                                    /bin/df, /usr/bin/free, /usr/bin/top

# Configuración con timeout
Defaults:script_admin timestamp_timeout=15

# Prohibir comandos peligrosos globalmente
Cmnd_Alias DANGEROUS = /bin/rm -rf *, /usr/bin/dd, /sbin/mkfs*, /sbin/fdisk
ALL ALL = ALL, !DANGEROUS
Mejores Prácticas para sudoers
  • SIEMPRE usar visudo: Valida la sintaxis antes de guardar
  • NOPASSWD con cautela: Solo para scripts automatizados específicos
  • Rutas absolutas: Especificar rutas completas a comandos
  • Logging habilitado: Registrar todas las actividades sudo
  • secure_path: Controlar el PATH para evitar ataques

Patrones Seguros para Scripts con Sudo

Template Base para Scripts Seguros

Plantilla robusta para scripts administrativos con privilegios elevados.

secure_admin_template.sh Copiar
#!/bin/bash

# Template Seguro para Scripts Administrativos
# Desarrollado siguiendo mejores prácticas de seguridad

# ========================================
# CONFIGURACIÓN INICIAL Y SEGURIDAD
# ========================================

# Configuración estricta de bash
set -euo pipefail  # Salir en error, variables no definidas, fallos en pipes
IFS=$'\n\t'       # Internal Field Separator seguro

# Variables de configuración
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_PID="$$"
readonly TIMESTAMP="$(date '+%Y-%m-%d_%H:%M:%S')"

# Archivos de configuración y log
readonly CONFIG_FILE="/etc/${SCRIPT_NAME%.*}.conf"
readonly LOG_FILE="/var/log/${SCRIPT_NAME%.*}.log"
readonly LOCK_FILE="/var/run/${SCRIPT_NAME%.*}.lock"

# Configuración de seguridad
readonly REQUIRED_USER="script_admin"  # Usuario requerido para ejecutar
readonly MIN_UID=1000                   # UID mínimo (evitar usuarios del sistema)
readonly MAX_PARAMS=10                  # Máximo número de parámetros

# ========================================
# FUNCIONES DE UTILIDAD Y SEGURIDAD
# ========================================

# Función de logging segura
log_message() {
    local level="$1"
    local message="$2"
    local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
    local user="$(whoami)"
    local real_user="${SUDO_USER:-$user}"
    
    # Formato: TIMESTAMP [LEVEL] USER(REAL_USER) PID: MESSAGE
    local log_entry="$timestamp [$level] $user($real_user) PID:$SCRIPT_PID: $message"
    
    # Escribir al log de forma atómica
    echo "$log_entry" | tee -a "$LOG_FILE" >/dev/null
    
    # También enviar a syslog
    logger -t "$SCRIPT_NAME" -p "user.$level" "$message"
    
    # Output coloreado en terminal
    case "$level" in
        ERROR)   echo -e "\e[31m[ERROR]\e[0m $message" >&2 ;;
        WARNING) echo -e "\e[33m[WARNING]\e[0m $message" >&2 ;;
        INFO)    echo -e "\e[34m[INFO]\e[0m $message" ;;
        SUCCESS) echo -e "\e[32m[SUCCESS]\e[0m $message" ;;
    esac
}

# Función de cleanup y salida segura
cleanup() {
    local exit_code=${1:-0}
    
    log_message "INFO" "Iniciando cleanup, código de salida: $exit_code"
    
    # Remover archivos temporales
    [[ -n "${TEMP_FILES:-}" ]] && rm -f $TEMP_FILES 2>/dev/null
    
    # Remover lock file
    [[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
    
    # Restaurar permisos si fueron cambiados
    [[ -n "${ORIGINAL_PERMS:-}" ]] && restore_permissions
    
    log_message "INFO" "Script finalizado con código: $exit_code"
    exit "$exit_code"
}

# Trap para cleanup en señales
trap 'cleanup 130' INT  # Ctrl+C
trap 'cleanup 143' TERM # Termination
trap 'cleanup 1' ERR    # Error

# Verificaciones de seguridad iniciales
security_checks() {
    # Verificar si se está ejecutando como el usuario correcto
    if [[ "$(whoami)" != "$REQUIRED_USER" ]]; then
        log_message "ERROR" "Script debe ejecutarse como usuario '$REQUIRED_USER'"
        cleanup 1
    fi
    
    # Verificar UID mínimo
    local current_uid="$(id -u)"
    if [[ "$current_uid" -lt "$MIN_UID" ]]; then
        log_message "ERROR" "UID demasiado bajo ($current_uid). Posible ataque."
        cleanup 1
    fi
    
    # Verificar que no se esté ejecutando desde directorio escribible por otros
    local script_perms="$(stat -c %a "$SCRIPT_DIR")"
    if [[ "${script_perms: -1}" != "0" && "${script_perms: -1}" != "5" ]]; then
        log_message "ERROR" "Directorio del script escribible por otros: $SCRIPT_DIR"
        cleanup 1
    fi
    
    # Verificar permisos del script
    local file_perms="$(stat -c %a "$0")"
    if [[ "${file_perms: -2}" != "00" ]]; then
        log_message "ERROR" "Script escribible por grupo/otros"
        cleanup 1
    fi
    
    # Verificar variables de entorno peligrosas
    unset LD_PRELOAD LD_LIBRARY_PATH
    export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    
    log_message "SUCCESS" "Verificaciones de seguridad completadas"
}

# Verificar y crear lock file
check_lock() {
    if [[ -f "$LOCK_FILE" ]]; then
        local lock_pid="$(cat "$LOCK_FILE" 2>/dev/null || echo "")"
        
        if [[ -n "$lock_pid" ]] && kill -0 "$lock_pid" 2>/dev/null; then
            log_message "ERROR" "Script ya ejecutándose (PID: $lock_pid)"
            cleanup 1
        else
            log_message "WARNING" "Lock file obsoleto encontrado, removiendo"
            rm -f "$LOCK_FILE"
        fi
    fi
    
    echo "$SCRIPT_PID" > "$LOCK_FILE"
    log_message "INFO" "Lock file creado: $LOCK_FILE"
}

# Validar parámetros de entrada
validate_input() {
    local param_count="$#"
    
    # Verificar número máximo de parámetros
    if [[ "$param_count" -gt "$MAX_PARAMS" ]]; then
        log_message "ERROR" "Demasiados parámetros ($param_count > $MAX_PARAMS)"
        cleanup 1
    fi
    
    # Verificar caracteres peligrosos en parámetros
    for param in "$@"; do
        # Rechazar parámetros que contengan caracteres peligrosos
        if [[ "$param" =~ [;\|\&\$\`] ]]; then
            log_message "ERROR" "Parámetro contiene caracteres peligrosos: $param"
            cleanup 1
        fi
        
        # Verificar longitud máxima
        if [[ "${#param}" -gt 256 ]]; then
            log_message "ERROR" "Parámetro demasiado largo: ${#param} caracteres"
            cleanup 1
        fi
    done
    
    log_message "INFO" "Validación de parámetros completada"
}

# Ejecutar comando con sudo de forma segura
safe_sudo() {
    local command="$1"
    shift
    local args=("$@")
    
    log_message "INFO" "Ejecutando comando privilegiado: $command ${args[*]}"
    
    # Verificar que el comando esté en la lista de permitidos
    case "$command" in
        /bin/systemctl|/usr/bin/systemctl)
            # Comandos systemctl seguros
            if [[ ! "${args[0]}" =~ ^(start|stop|restart|reload|status|enable|disable)$ ]]; then
                log_message "ERROR" "Acción systemctl no permitida: ${args[0]}"
                return 1
            fi
            ;;
        /bin/cp|/usr/bin/cp)
            # Verificar que no sea copia masiva peligrosa
            if [[ "${args[*]}" =~ \* ]]; then
                log_message "ERROR" "Copia con wildcard no permitida"
                return 1
            fi
            ;;
        *)
            log_message "ERROR" "Comando no permitido: $command"
            return 1
            ;;
    esac
    
    # Ejecutar con timeout y capturar salida
    if timeout 60 sudo "$command" "${args[@]}" 2>&1 | tee -a "$LOG_FILE"; then
        log_message "SUCCESS" "Comando ejecutado exitosamente"
        return 0
    else
        local exit_code=$?
        log_message "ERROR" "Comando falló con código: $exit_code"
        return $exit_code
    fi
}

# ========================================
# FUNCIONES ESPECÍFICAS DEL SCRIPT
# ========================================

# Función principal de ejemplo
example_function() {
    local service_name="$1"
    
    log_message "INFO" "Reiniciando servicio: $service_name"
    
    # Validar nombre del servicio
    if [[ ! "$service_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
        log_message "ERROR" "Nombre de servicio inválido: $service_name"
        return 1
    fi
    
    # Verificar que el servicio existe
    if ! systemctl list-unit-files | grep -q "^${service_name}.service"; then
        log_message "ERROR" "Servicio no encontrado: $service_name"
        return 1
    fi
    
    # Ejecutar operaciones de forma segura
    safe_sudo "/usr/bin/systemctl" "stop" "$service_name"
    sleep 2
    safe_sudo "/usr/bin/systemctl" "start" "$service_name"
    
    # Verificar estado
    if safe_sudo "/usr/bin/systemctl" "is-active" "$service_name" | grep -q "active"; then
        log_message "SUCCESS" "Servicio $service_name reiniciado correctamente"
        return 0
    else
        log_message "ERROR" "Servicio $service_name no se inició correctamente"
        return 1
    fi
}

# Mostrar ayuda
show_help() {
    cat << EOF
Uso: $0 [OPCIONES] COMANDO

COMANDOS:
    restart-service NOMBRE    - Reiniciar servicio específico
    
OPCIONES:
    -h, --help               - Mostrar esta ayuda
    -v, --verbose            - Modo verbose
    
EJEMPLOS:
    $0 restart-service apache2
    $0 restart-service mysql

SEGURIDAD:
    - Script debe ejecutarse como usuario: $REQUIRED_USER
    - Todas las acciones son registradas en: $LOG_FILE
    - Se requieren privilegios sudo específicos en /etc/sudoers
EOF
}

# ========================================
# FUNCIÓN PRINCIPAL
# ========================================

main() {
    log_message "INFO" "=== Iniciando $SCRIPT_NAME ==="
    log_message "INFO" "Usuario: $(whoami), Usuario real: ${SUDO_USER:-N/A}"
    log_message "INFO" "Parámetros: $*"
    
    # Verificaciones iniciales
    security_checks
    check_lock
    validate_input "$@"
    
    # Procesar argumentos
    while [[ $# -gt 0 ]]; do
        case $1 in
            restart-service)
                [[ -z "${2:-}" ]] && { log_message "ERROR" "Nombre de servicio requerido"; cleanup 1; }
                example_function "$2"
                shift 2
                ;;
            -v|--verbose)
                set -x  # Modo debug
                shift
                ;;
            -h|--help)
                show_help
                cleanup 0
                ;;
            *)
                log_message "ERROR" "Opción desconocida: $1"
                show_help
                cleanup 1
                ;;
        esac
    done
    
    [[ $# -eq 0 ]] && { show_help; cleanup 1; }
    
    log_message "SUCCESS" "Script completado exitosamente"
    cleanup 0
}

# ========================================
# PUNTO DE ENTRADA
# ========================================

# Verificar que se está ejecutando directamente
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi

Técnicas de Validación Avanzadas

Validación de Entradas

Implementación de validaciones robustas para prevenir inyecciones y ataques.

Funciones de validación Copiar
#!/bin/bash

# Biblioteca de funciones de validación seguras

# Validar dirección IP
validate_ip() {
    local ip="$1"
    local ip_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
    
    if [[ ! "$ip" =~ $ip_regex ]]; then
        return 1
    fi
    
    # Verificar que cada octeto esté en rango válido
    IFS='.' read -ra octets <<< "$ip"
    for octet in "${octets[@]}"; do
        if [[ "$octet" -lt 0 || "$octet" -gt 255 ]]; then
            return 1
        fi
    done
    
    return 0
}

# Validar nombre de archivo/directorio seguro
validate_filename() {
    local filename="$1"
    
    # Rechazar nombres vacíos
    [[ -z "$filename" ]] && return 1
    
    # Rechazar caracteres peligrosos
    [[ "$filename" =~ [/\\\$\`\;] ]] && return 1
    
    # Rechazar nombres que empiecen con punto o guión
    [[ "$filename" =~ ^[.-] ]] && return 1
    
    # Rechazar nombres demasiado largos
    [[ "${#filename}" -gt 255 ]] && return 1
    
    # Solo permitir caracteres alfanuméricos, guión y underscore
    [[ "$filename" =~ ^[a-zA-Z0-9_-]+$ ]] || return 1
    
    return 0
}

# Validar puerto de red
validate_port() {
    local port="$1"
    
    # Verificar que sea numérico
    [[ ! "$port" =~ ^[0-9]+$ ]] && return 1
    
    # Verificar rango válido
    [[ "$port" -lt 1 || "$port" -gt 65535 ]] && return 1
    
    return 0
}

# Validar nombre de usuario
validate_username() {
    local username="$1"
    
    # Verificar longitud
    [[ "${#username}" -lt 1 || "${#username}" -gt 32 ]] && return 1
    
    # Solo permitir caracteres válidos para username
    [[ "$username" =~ ^[a-z_][a-z0-9_-]*$ ]] || return 1
    
    # Verificar que el usuario existe en el sistema
    id "$username" &>/dev/null || return 1
    
    return 0
}

# Validar path absoluto y seguro
validate_path() {
    local path="$1"
    local allow_create="${2:-false}"
    
    # Debe ser ruta absoluta
    [[ "$path" != /* ]] && return 1
    
    # No debe contener .. o caracteres peligrosos
    [[ "$path" =~ \.\.|[\$\`\;] ]] && return 1
    
    # Si allow_create es false, el path debe existir
    if [[ "$allow_create" == "false" && ! -e "$path" ]]; then
        return 1
    fi
    
    # Verificar permisos del directorio padre
    local parent_dir="$(dirname "$path")"
    if [[ -d "$parent_dir" ]]; then
        local perms="$(stat -c %a "$parent_dir")"
        # Rechazar si otros tienen permisos de escritura
        [[ "${perms: -1}" =~ [2367] ]] && return 1
    fi
    
    return 0
}

# Validar comando y argumentos
validate_command() {
    local command="$1"
    shift
    local args=("$@")
    
    # Lista de comandos permitidos (whitelist)
    local allowed_commands=(
        "/bin/systemctl"
        "/usr/bin/systemctl" 
        "/bin/cp"
        "/usr/bin/cp"
        "/bin/mv"
        "/usr/bin/mv"
        "/bin/chown"
        "/usr/bin/chown"
        "/bin/chmod"
        "/usr/bin/chmod"
    )
    
    # Verificar que el comando esté en la lista permitida
    local command_allowed=false
    for allowed in "${allowed_commands[@]}"; do
        if [[ "$command" == "$allowed" ]]; then
            command_allowed=true
            break
        fi
    done
    
    [[ "$command_allowed" == "false" ]] && return 1
    
    # Validaciones específicas por comando
    case "$command" in
        */systemctl)
            validate_systemctl_args "${args[@]}"
            ;;
        */cp|*/mv)
            validate_file_operation_args "${args[@]}"
            ;;
        */chown)
            validate_chown_args "${args[@]}"
            ;;
        */chmod)
            validate_chmod_args "${args[@]}"
            ;;
    esac
}

# Validar argumentos de systemctl
validate_systemctl_args() {
    local action="$1"
    local service="${2:-}"
    
    # Acciones permitidas
    case "$action" in
        start|stop|restart|reload|status|enable|disable|is-active|is-enabled)
            ;;
        *)
            return 1
            ;;
    esac
    
    # Si se especifica servicio, validarlo
    if [[ -n "$service" ]]; then
        validate_filename "$service" || return 1
        
        # Verificar que el servicio existe
        systemctl list-unit-files | grep -q "^${service}.service" || return 1
    fi
    
    return 0
}

# Validar operaciones de archivo
validate_file_operation_args() {
    local source="$1"
    local destination="$2"
    
    # Validar rutas
    validate_path "$source" false || return 1
    validate_path "$destination" true || return 1
    
    # Verificar que no se está intentando sobrescribir archivos críticos
    local critical_files=(
        "/etc/passwd"
        "/etc/shadow"
        "/etc/sudoers"
        "/etc/ssh/sshd_config"
    )
    
    for critical in "${critical_files[@]}"; do
        [[ "$destination" == "$critical" ]] && return 1
    done
    
    return 0
}

# Ejemplo de uso en script principal
secure_file_copy() {
    local source="$1"
    local destination="$2"
    
    log_message "INFO" "Validando operación de copia: $source -> $destination"
    
    # Validar argumentos
    if ! validate_file_operation_args "$source" "$destination"; then
        log_message "ERROR" "Validación de argumentos falló"
        return 1
    fi
    
    # Ejecutar con logging completo
    if safe_sudo "/bin/cp" "$source" "$destination"; then
        log_message "SUCCESS" "Archivo copiado: $source -> $destination"
        return 0
    else
        log_message "ERROR" "Falló la copia de archivo"
        return 1
    fi
}

Auditoría y Monitoreo

Sistema de Logging Avanzado

Implementación de logging completo para auditoría de seguridad.

audit_logger.sh Copiar
#!/bin/bash

# Sistema de Auditoría y Logging Avanzado para Scripts Sudo

# ========================================
# CONFIGURACIÓN DE AUDITORÍA
# ========================================

readonly AUDIT_LOG_DIR="/var/log/script-audit"
readonly AUDIT_LOG_FILE="$AUDIT_LOG_DIR/audit-$(date +%Y-%m).log"
readonly SECURITY_LOG_FILE="$AUDIT_LOG_DIR/security-$(date +%Y-%m).log"
readonly ACCESS_LOG_FILE="$AUDIT_LOG_DIR/access-$(date +%Y-%m).log"

# Configuración de alertas
readonly ALERT_EMAIL="[email protected]"
readonly ALERT_THRESHOLD_FAILURES=3
readonly ALERT_TIME_WINDOW=300  # 5 minutos

# ========================================
# FUNCIONES DE AUDITORÍA
# ========================================

# Inicializar sistema de auditoría
init_audit_system() {
    # Crear directorios de log con permisos restrictivos
    sudo mkdir -p "$AUDIT_LOG_DIR"
    sudo chmod 750 "$AUDIT_LOG_DIR"
    sudo chown root:adm "$AUDIT_LOG_DIR"
    
    # Crear archivos de log si no existen
    for log_file in "$AUDIT_LOG_FILE" "$SECURITY_LOG_FILE" "$ACCESS_LOG_FILE"; do
        sudo touch "$log_file"
        sudo chmod 640 "$log_file"
        sudo chown root:adm "$log_file"
    done
}

# Log de eventos de auditoría
audit_log() {
    local event_type="$1"
    local severity="$2"
    local message="$3"
    local user="$(whoami)"
    local real_user="${SUDO_USER:-$user}"
    local pid="$$"
    local ppid="$PPID"
    local tty="$(tty 2>/dev/null || echo 'unknown')"
    local ssh_client="${SSH_CLIENT:-local}"
    local timestamp="$(date '+%Y-%m-%d %H:%M:%S %Z')"
    
    # Formato de log estructurado
    local log_entry=$(cat << EOF
{
  "timestamp": "$timestamp",
  "event_type": "$event_type",
  "severity": "$severity",
  "user": "$user",
  "real_user": "$real_user",
  "pid": $pid,
  "ppid": $ppid,
  "tty": "$tty",
  "ssh_client": "$ssh_client",
  "script": "$SCRIPT_NAME",
  "message": "$message"
}
EOF
    )
    
    # Escribir a archivo de auditoría
    echo "$log_entry" | sudo tee -a "$AUDIT_LOG_FILE" >/dev/null
    
    # Si es evento de seguridad, también a log de seguridad
    if [[ "$event_type" =~ ^(security|auth|privilege) ]]; then
        echo "$log_entry" | sudo tee -a "$SECURITY_LOG_FILE" >/dev/null
    fi
    
    # Enviar a syslog también
    logger -p "authpriv.$severity" -t "SCRIPT_AUDIT[$pid]" "$event_type: $message"
}

# Log de acceso
access_log() {
    local action="$1"
    local resource="$2"
    local result="$3"
    local details="${4:-}"
    
    local user="$(whoami)"
    local real_user="${SUDO_USER:-$user}"
    local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
    
    local access_entry="$timestamp|$user|$real_user|$action|$resource|$result|$details"
    echo "$access_entry" | sudo tee -a "$ACCESS_LOG_FILE" >/dev/null
    
    audit_log "access" "info" "Action: $action, Resource: $resource, Result: $result"
}

# Detectar intentos de ataque
security_monitor() {
    local event_type="$1"
    local details="$2"
    
    case "$event_type" in
        "failed_validation")
            audit_log "security" "warning" "Validation failed: $details"
            check_attack_patterns "validation_failure"
            ;;
        "privilege_escalation")
            audit_log "security" "critical" "Privilege escalation attempt: $details"
            send_security_alert "CRITICAL: Privilege escalation attempt detected"
            ;;
        "unauthorized_access")
            audit_log "security" "error" "Unauthorized access: $details"
            check_attack_patterns "unauthorized_access"
            ;;
        "suspicious_command")
            audit_log "security" "warning" "Suspicious command: $details"
            ;;
    esac
}

# Verificar patrones de ataque
check_attack_patterns() {
    local attack_type="$1"
    local user="${SUDO_USER:-$(whoami)}"
    local current_time="$(date +%s)"
    local window_start=$((current_time - ALERT_TIME_WINDOW))
    
    # Contar eventos similares en la ventana de tiempo
    local event_count=$(awk -v start="$window_start" -v user="$user" -v type="$attack_type" '
        $1 >= start && /real_user.*'"$user"'/ && /'"$attack_type"'/ { count++ }
        END { print count+0 }
    ' "$SECURITY_LOG_FILE" 2>/dev/null || echo 0)
    
    if [[ "$event_count" -ge "$ALERT_THRESHOLD_FAILURES" ]]; then
        send_security_alert "ALERT: Multiple $attack_type attempts from user $user"
        
        # Log evento crítico
        audit_log "security" "critical" "Attack pattern detected: $attack_type, count: $event_count, user: $user"
    fi
}

# Enviar alerta de seguridad
send_security_alert() {
    local alert_message="$1"
    local hostname="$(hostname)"
    local timestamp="$(date)"
    
    # Log de alerta
    audit_log "alert" "critical" "$alert_message"
    
    # Enviar email si está configurado
    if command -v mail >/dev/null 2>&1 && [[ -n "$ALERT_EMAIL" ]]; then
        {
            echo "ALERTA DE SEGURIDAD - $hostname"
            echo "Timestamp: $timestamp"
            echo "Mensaje: $alert_message"
            echo ""
            echo "Detalles del evento:"
            tail -5 "$SECURITY_LOG_FILE"
        } | mail -s "SECURITY ALERT: $hostname" "$ALERT_EMAIL"
    fi
    
    # Notificación local
    if command -v notify-send >/dev/null 2>&1; then
        notify-send -u critical "Security Alert" "$alert_message"
    fi
}

# Generar reporte de auditoría
generate_audit_report() {
    local report_period="${1:-daily}"  # daily, weekly, monthly
    local output_file="/tmp/audit_report_$(date +%Y%m%d).txt"
    
    {
        echo "REPORTE DE AUDITORÍA - $(date)"
        echo "Periodo: $report_period"
        echo "Sistema: $(hostname)"
        echo "=================================================="
        echo
        
        # Resumen de eventos por tipo
        echo "RESUMEN DE EVENTOS:"
        awk -F'"' '/event_type/ { events[$4]++ } END { 
            for (event in events) print event ": " events[event] 
        }' "$AUDIT_LOG_FILE" | sort
        echo
        
        # Top usuarios
        echo "TOP USUARIOS:"
        awk -F'"' '/real_user/ { users[$4]++ } END { 
            for (user in users) print user ": " users[user] 
        }' "$AUDIT_LOG_FILE" | sort -k2 -rn | head -10
        echo
        
        # Eventos de seguridad
        echo "EVENTOS DE SEGURIDAD RECIENTES:"
        grep -E '(security|auth|privilege)' "$AUDIT_LOG_FILE" | tail -20
        echo
        
        # Comandos más ejecutados
        echo "COMANDOS MÁS EJECUTADOS:"
        awk -F'"' '/message.*command/ { 
            match($0, /command: ([^"]*)"/, cmd); 
            if (cmd[1]) commands[cmd[1]]++ 
        } END { 
            for (command in commands) print command ": " commands[command] 
        }' "$AUDIT_LOG_FILE" | sort -k2 -rn | head -10
        
    } | tee "$output_file"
    
    echo "Reporte generado: $output_file"
}

# Rotar logs de auditoría
rotate_audit_logs() {
    local retention_days="${1:-90}"
    
    # Comprimir logs antiguos
    find "$AUDIT_LOG_DIR" -name "*.log" -mtime +1 -exec gzip {} \;
    
    # Eliminar logs muy antiguos
    find "$AUDIT_LOG_DIR" -name "*.gz" -mtime +$retention_days -delete
    
    audit_log "maintenance" "info" "Log rotation completed, retention: $retention_days days"
}

# Wrapper para comandos auditados
audited_command() {
    local command="$1"
    shift
    local args=("$@")
    
    local start_time="$(date +%s.%N)"
    
    # Log inicio de comando
    access_log "command_start" "$command ${args[*]}" "started"
    audit_log "command" "info" "Starting command: $command ${args[*]}"
    
    # Ejecutar comando
    local exit_code=0
    if "$command" "${args[@]}"; then
        access_log "command_end" "$command ${args[*]}" "success"
        audit_log "command" "info" "Command completed successfully: $command"
    else
        exit_code=$?
        access_log "command_end" "$command ${args[*]}" "failed"
        audit_log "command" "error" "Command failed (exit: $exit_code): $command"
    fi
    
    local end_time="$(date +%s.%N)"
    local duration="$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "unknown")"
    
    audit_log "performance" "info" "Command duration: ${duration}s for $command"
    
    return $exit_code
}

# ========================================
# FUNCIÓN PRINCIPAL DE INICIALIZACIÓN
# ========================================

# Inicializar auditoría al cargar el script
if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -n "${SUDO_USER:-}" ]]; then
    init_audit_system
    audit_log "system" "info" "Audit system initialized for script execution"
fi

Ejercicios Prácticos

Ejercicio 1: Script de Backup Seguro

Desarrolla un script con privilegios sudo que:

  1. Realice backup de directorios específicos
  2. Valide todas las entradas de usuario
  3. Implemente logging completo de auditoría
  4. Maneje errores de forma segura
  5. Use principio de menor privilegio
Proyecto: Sistema de Gestión de Usuarios

Implementa un sistema completo que permita:

  • Creación y eliminación de usuarios con validaciones
  • Configuración de grupos y permisos
  • Auditoría completa de todas las operaciones
  • Alertas de seguridad automáticas
  • Interface segura para administradores
Mejores Prácticas Resumidas
  • Validación exhaustiva: Nunca confíes en datos externos
  • Logging completo: Registra toda actividad privilegiada
  • Principio de menor privilegio: Solo los permisos mínimos necesarios
  • Manejo de errores: Falla de forma segura
  • Configuración restrictiva: Usa sudoers de forma específica
  • Monitoreo activo: Detecta patrones de ataque