1. Introduction: The Kernel Attack Surface
Most hardening guides remain on the surface: configure firewalls, harden SSH, disable unnecessary services. They are necessary but insufficient measures. A sophisticated adversary does not attack your iptables rules; attacks the Kernel Space, where the absolute privileges of the system reside.
He Kernel Self-Protection Project (KSPP) represents a paradigm shift: instead of relying exclusively on external mechanisms (antivirus, IDS), we reconfigure the Linux kernel itself to be resilient by design against memory exploitation, privilege escalation, and arbitrary code execution.
This manual does not use third-party tools. We are going to modify how the kernel manages memory, system calls and process execution. The goal: create a system immune to entire classes of vulnerabilities, including zero-day exploits that have not yet been discovered.
2. KSPP Implementation: Bootloader Configuration
Action: Modify GRUB parameters
/etc/default/grub and modify the line GRUB_CMDLINE_LINUX_DEFAULT:GRUB_CMDLINE_LINUX_DEFAULT="quiet slab_nomerge page_alloc.shuffle=1 vsyscall=none init_on_alloc=1 init_on_free=1 pti=on spectre_v2=on spec_store_bypass_disable=seccomp l1tf=full mds=full,nosmt tsx=off kvm.nx_huge_pages=force"Analysis of critical parameters
| Parameter | Protection Mechanism | Mitigated Attack Vector |
|---|---|---|
slab_nomerge | Prevents the kernel from merging object caches of different sizes | Heap overflow attacks that depend on manipulating adjacent caches |
page_alloc.shuffle=1 | Randomize memory page allocation | Memory address prediction attacks (ASLR bypass) |
vsyscall=none | Disable vsyscall legacy emulation | ROP (Return-Oriented Programming) gadgets in fixed memory |
init_on_alloc=1 | Initializes all allocated memory to zero | Using previous kernel residual data (information leaks) |
init_on_free=1 | Overwrite freed memory before reallocating | Use-after-free exploits |
pti=on | Isolates kernel and user page tables | Meltdown and kernel memory read attacks from userland |
spectre_v2=on | Enable branch target injection mitigations | Specter v2 (Branch Target Injection) |
spec_store_bypass_disable=seccomp | Protection against Speculative Store Bypass | Variant 4 of speculative attacks |
l1tf=full | Complete mitigation against L1 Terminal Fault | L1TF (Foreshadow) |
mds=full,nosmt | Protection against Microarchitectural Data Sampling | RIDL, Fallout, ZombieLoad |
tsx=off | Disable Intel TSX (Transactional Synchronization Extensions) | TAA (TSX Asynchronous Abort) |
kvm.nx_huge_pages=force | Force No-Execute on hypervisor huge pages | Memory corruption attacks in virtualized environments |
Apply changes:
sysctl --systemApply grub changes
sudo update-grub
sudo rebootNote for virtualized environments: These parameters work on both bare-metal hardware and VMs (KVM, VMware, VirtualBox). The hypervisor emulates hardware protections, allowing you to test extreme security configurations without risk to the host system.
3. Network Stack Hardening: Advanced Sysctl
Settings /etc/sysctl.d/99-security.conf
# === Protección contra spoofing y redirección ===
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# === Prevención de inundaciones y DoS ===
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
net.netfilter.nf_conntrack_max = 2000000
net.netfilter.nf_conntrack_tcp_loose = 0
# === Ocultamiento de información del kernel ===
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.yama.ptrace_scope = 2
kernel.unprivileged_bpf_disabled = 1
net.core.bpf_jit_harden = 2
# === Protección de memoria ===
vm.mmap_rnd_bits = 32
vm.mmap_rnd_compat_bits = 16
vm.unprivileged_userfaultfd = 0Technical explanation of key parameters
kernel.kptr_restrict = 2: Hides kernel symbol memory addresses even from the root user. Privilege escalation exploits often require knowing specific kernel addresses to build ROP gadgets. With this configuration, /proc/kallsyms sample 0000000000000000 for all kernel symbols.kernel.yama.ptrace_scope = 2: Limits the use of ptrace (debugging tool) only to processes with CAP_SYS_PTRACE capability. This blocks common code injection and credential dumping techniques from compromised processes.vm.unprivileged_userfaultfd = 0: Disable userfaultfd for non-privileged users. This syscall has been exploited in multiple vulnerabilities (such as the DirtyPipe exploit chain) to manipulate kernel memory management.Settings application
sudo sysctl --system4. Execution Control: Seccomp and Capabilities
Concept: Reduction of attack surface by syscall
execve, the process cannot launch new programs. If we eliminate openat, you cannot open arbitrary files.Practice: Seccomp Profile for Nginx
/etc/nginx/seccomp-bpf.json{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86"],
"syscalls": [
{
"names": [
"accept", "accept4", "bind", "clone", "close", "connect",
"epoll_create", "epoll_create1", "epoll_ctl", "epoll_pwait",
"epoll_wait", "eventfd2", "exit", "exit_group", "fchdir",
"fchmod", "fchown", "fcntl", "fstat", "fstatfs", "fsync",
"ftruncate", "getcwd", "getdents", "getdents64", "getegid",
"geteuid", "getgid", "getpgrp", "getpid", "getppid", "getrlimit",
"getuid", "ioctl", "io_setup", "listen", "lseek", "mkdir",
"mmap", "mprotect", "munmap", "nanosleep", "openat", "pipe",
"pipe2", "poll", "pread64", "pselect6", "pwrite64", "read",
"recvfrom", "recvmsg", "rename", "rmdir", "rt_sigaction",
"rt_sigprocmask", "rt_sigreturn", "select", "sendfile",
"sendmsg", "sendto", "setitimer", "setrlimit", "setsockopt",
"shutdown", "sigaltstack", "socket", "socketpair", "stat",
"statfs", "sysinfo", "umask", "uname", "unlink", "utimensat",
"write", "writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["execve", "execveat", "fork", "vfork"],
"action": "SCMP_ACT_KILL"
}
]
}Integration with Systemd
/etc/systemd/system/nginx.service.d/override.conf):[Service]
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUIDProtection analysis
-
SystemCallFilter=@system-service: Uses the predefined set of safe syscalls for system services. -
NoNewPrivileges=true: Prevents the process from gaining additional privileges (blocks setuid binaries). -
ProtectSystem=strict: Mounts the root file system as read-only and inaccessible to the service. -
MemoryDenyWriteExecute=true: Enables W^X (Write XOR Execute), preventing memory from being simultaneously writable and executable (anti-shellcode mitigation).
sudo systemctl daemon-reload
sudo systemctl restart nginxExample of Seccomp-BPF templates for Common Services
Create directory and file
sudo mkdir -p /etc/seccomp
sudo vi /etc/seccomp/profiles.json/etc/seccomp/profiles.json (format supported by Docker, containerd, systemd and runc){
"profiles": {
"nginx-web-server": {
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_AARCH64"
],
"syscalls": [
{
"names": [
"accept", "accept4", "access", "alarm", "bind", "brk",
"capget", "capset", "chdir", "chmod", "chown", "clock_gettime",
"clone", "clone3", "close", "close_range", "connect", "copy_file_range",
"creat", "dup", "dup2", "dup3", "epoll_create", "epoll_create1",
"epoll_ctl", "epoll_pwait", "epoll_pwait2", "epoll_wait", "eventfd2",
"execve", "execveat", "exit", "exit_group", "faccessat", "faccessat2",
"fadvise64", "fallocate", "fanotify_mark", "fchdir", "fchmod",
"fchmodat", "fchown", "fchownat", "fcntl", "fdatasync", "fgetxattr",
"flistxattr", "flock", "fork", "fremovexattr", "fsetxattr", "fstat",
"fstatfs", "fsync", "ftruncate", "futex", "getcwd", "getdents",
"getdents64", "getegid", "geteuid", "getgid", "getgroups",
"getitimer", "getpeername", "getpgid", "getpgrp", "getpid",
"getppid", "getpriority", "getrandom", "getresgid", "getresuid",
"getrlimit", "getrusage", "getsid", "getsockname", "getsockopt",
"gettid", "gettimeofday", "getuid", "getxattr", "inotify_add_watch",
"inotify_init1", "inotify_rm_watch", "io_cancel", "io_destroy",
"io_getevents", "io_setup", "io_submit", "ioctl", "ioprio_get",
"ioprio_set", "kcmp", "kill", "lchown", "lgetxattr", "link",
"linkat", "listen", "listxattr", "llistxattr", "lremovexattr",
"lseek", "lsetxattr", "lstat", "madvise", "memfd_create",
"mincore", "mkdir", "mkdirat", "mknod", "mknodat", "mlock",
"mlock2", "mlockall", "mmap", "mprotect", "mq_getsetattr",
"mq_notify", "mq_open", "mq_timedreceive", "mq_timedsend",
"mq_unlink", "mremap", "msgctl", "msgget", "msgrcv", "msgsnd",
"msync", "munlock", "munlockall", "munmap", "nanosleep",
"newfstatat", "open", "openat", "openat2", "pause", "pipe",
"pipe2", "poll", "ppoll", "prctl", "pread64", "preadv",
"preadv2", "prlimit64", "pselect6", "pwrite64", "pwritev",
"pwritev2", "read", "readahead", "readdir", "readlink",
"readlinkat", "readv", "recv", "recvfrom", "recvmmsg",
"recvmsg", "remap_file_pages", "removexattr", "rename",
"renameat", "renameat2", "restart_syscall", "rmdir", "rseq",
"rt_sigaction", "rt_sigpending", "rt_sigprocmask", "rt_sigqueueinfo",
"rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", "rt_tgsigqueueinfo",
"sched_getaffinity", "sched_getattr", "sched_getparam",
"sched_get_priority_max", "sched_get_priority_min", "sched_getscheduler",
"sched_rr_get_interval", "sched_setaffinity", "sched_setattr",
"sched_setparam", "sched_setscheduler", "sched_yield", "seccomp",
"select", "semctl", "semget", "semop", "semtimedop", "send",
"sendfile", "sendmmsg", "sendmsg", "sendto", "setfsgid", "setfsuid",
"setgid", "setgroups", "setitimer", "setpgid", "setpriority",
"setregid", "setresgid", "setresuid", "setreuid", "setrlimit",
"setsid", "setsockopt", "setuid", "setxattr", "shmat", "shmctl",
"shmdt", "shmget", "shutdown", "sigaltstack", "signalfd4",
"socket", "socketpair", "splice", "stat", "statfs", "statx",
"symlink", "symlinkat", "sync", "sync_file_range", "syncfs",
"sysinfo", "tee", "tgkill", "time", "timer_create", "timer_delete",
"timer_getoverrun", "timer_gettime", "timer_settime", "timerfd_create",
"timerfd_gettime", "timerfd_settime", "times", "tkill", "truncate",
"umask", "uname", "unlink", "unlinkat", "utime", "utimensat",
"utimes", "vfork", "wait4", "waitid", "write", "writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": [
"bpf",
"clock_adjtime",
"clock_settime",
"create_module",
"delete_module",
"finit_module",
"get_kernel_syms",
"get_mempolicy",
"init_module",
"ioperm",
"iopl",
"kexec_file_load",
"kexec_load",
"keyctl",
"lookup_dcookie",
"mbind",
"nfsservctl",
"open_by_handle_at",
"perf_event_open",
"personality",
"pivot_root",
"process_vm_readv",
"process_vm_writev",
"ptrace",
"query_module",
"quotactl",
"reboot",
"request_key",
"set_mempolicy",
"setns",
"settimeofday",
"stime",
"swapoff",
"swapon",
"_sysctl",
"syslog",
"tuxcall",
"umount2",
"uselib",
"userfaultfd",
"ustat",
"vm86",
"vm86old"
],
"action": "SCMP_ACT_KILL"
}
]
},
"database-server": {
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": [
"accept", "accept4", "access", "bind", "brk", "capget", "capset",
"chdir", "chmod", "chown", "clock_gettime", "clone", "clone3",
"close", "connect", "creat", "dup", "dup2", "dup3", "epoll_create",
"epoll_create1", "epoll_ctl", "epoll_pwait", "epoll_wait", "eventfd2",
"execve", "exit", "exit_group", "faccessat", "fadvise64", "fallocate",
"fchdir", "fchmod", "fchmodat", "fchown", "fchownat", "fcntl",
"fdatasync", "flock", "fork", "fstat", "fstatfs", "fsync", "ftruncate",
"futex", "getcwd", "getdents", "getdents64", "getegid", "geteuid",
"getgid", "getgroups", "getitimer", "getpeername", "getpgrp", "getpid",
"getppid", "getpriority", "getrandom", "getresgid", "getresuid",
"getrlimit", "getrusage", "getsockname", "getsockopt", "gettid",
"gettimeofday", "getuid", "ioctl", "kill", "link", "linkat", "listen",
"lseek", "lstat", "madvise", "mkdir", "mkdirat", "mknod", "mknodat",
"mlock", "mlockall", "mmap", "mprotect", "mq_getsetattr", "mq_notify",
"mq_open", "mq_timedreceive", "mq_timedsend", "mq_unlink", "mremap",
"msync", "munlock", "munlockall", "munmap", "nanosleep", "newfstatat",
"open", "openat", "pause", "pipe", "pipe2", "poll", "ppoll", "prctl",
"pread64", "preadv", "prlimit64", "pselect6", "pwrite64", "pwritev",
"read", "readahead", "readlink", "readlinkat", "readv", "recv",
"recvfrom", "recvmmsg", "recvmsg", "rename", "renameat", "renameat2",
"rmdir", "rt_sigaction", "rt_sigpending", "rt_sigprocmask",
"rt_sigqueueinfo", "rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait",
"sched_getaffinity", "sched_getattr", "sched_getparam",
"sched_get_priority_max", "sched_get_priority_min", "sched_getscheduler",
"sched_rr_get_interval", "sched_setaffinity", "sched_setattr",
"sched_setparam", "sched_setscheduler", "sched_yield", "select",
"semctl", "semget", "semop", "semtimedop", "send", "sendfile",
"sendmmsg", "sendmsg", "sendto", "setfsgid", "setfsuid", "setgid",
"setgroups", "setitimer", "setpgid", "setpriority", "setregid",
"setresgid", "setresuid", "setreuid", "setrlimit", "setsid",
"setsockopt", "setuid", "shmat", "shmctl", "shmdt", "shmget",
"shutdown", "sigaltstack", "signalfd4", "socket", "socketpair",
"splice", "stat", "statfs", "sync", "sync_file_range", "syncfs",
"sysinfo", "tee", "tgkill", "time", "timer_create", "timer_delete",
"timer_getoverrun", "timer_gettime", "timer_settime", "timerfd_create",
"timerfd_gettime", "timerfd_settime", "times", "tkill", "truncate",
"umask", "uname", "unlink", "unlinkat", "utime", "utimensat",
"utimes", "vfork", "wait4", "waitid", "write", "writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["execveat", "ptrace", "process_vm_readv", "process_vm_writev",
"userfaultfd", "perf_event_open", "bpf", "mount", "umount2"],
"action": "SCMP_ACT_KILL"
}
]
},
"minimal-microservice": {
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": [
"accept", "accept4", "bind", "brk", "capget", "capset", "chdir",
"chmod", "clock_gettime", "clone", "clone3", "close", "connect",
"creat", "dup", "dup2", "dup3", "epoll_create", "epoll_create1",
"epoll_ctl", "epoll_pwait", "epoll_wait", "eventfd2", "exit",
"exit_group", "faccessat", "fadvise64", "fallocate", "fchdir",
"fchmod", "fchmodat", "fchown", "fchownat", "fcntl", "fdatasync",
"flock", "fstat", "fstatfs", "fsync", "ftruncate", "futex", "getcwd",
"getdents", "getdents64", "getegid", "geteuid", "getgid", "getgroups",
"getitimer", "getpeername", "getpgrp", "getpid", "getppid",
"getpriority", "getrandom", "getresgid", "getresuid", "getrlimit",
"getrusage", "getsockname", "getsockopt", "gettid", "gettimeofday",
"getuid", "ioctl", "listen", "lseek", "lstat", "madvise", "mkdir",
"mkdirat", "mmap", "mprotect", "mremap", "munmap", "nanosleep",
"newfstatat", "open", "openat", "pipe", "pipe2", "poll", "ppoll",
"pread64", "preadv", "prlimit64", "pselect6", "pwrite64", "pwritev",
"read", "readahead", "readlink", "readlinkat", "readv", "recv",
"recvfrom", "recvmmsg", "recvmsg", "rename", "renameat", "renameat2",
"rmdir", "rt_sigaction", "rt_sigpending", "rt_sigprocmask",
"rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", "sched_getaffinity",
"sched_getattr", "sched_getparam", "sched_get_priority_max",
"sched_get_priority_min", "sched_getscheduler", "sched_rr_get_interval",
"sched_setaffinity", "sched_setattr", "sched_setparam",
"sched_setscheduler", "sched_yield", "select", "send", "sendfile",
"sendmmsg", "sendmsg", "sendto", "setfsgid", "setfsuid", "setgid",
"setgroups", "setitimer", "setpgid", "setpriority", "setregid",
"setresgid", "setresuid", "setreuid", "setrlimit", "setsid",
"setsockopt", "setuid", "shutdown", "sigaltstack", "signalfd4",
"socket", "socketpair", "splice", "stat", "statfs", "sync",
"sync_file_range", "syncfs", "sysinfo", "tee", "tgkill", "time",
"timer_create", "timer_delete", "timer_getoverrun", "timer_gettime",
"timer_settime", "timerfd_create", "timerfd_gettime", "timerfd_settime",
"times", "tkill", "truncate", "umask", "uname", "unlink", "unlinkat",
"utime", "utimensat", "utimes", "wait4", "waitid", "write", "writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["execve", "execveat", "fork", "vfork", "ptrace", "mount",
"umount2", "chroot", "pivot_root", "setns", "unshare"],
"action": "SCMP_ACT_KILL"
}
]
}
}
}Archive: /usr/local/bin/apply-seccomp.sh
#!/bin/bash
# Aplica perfiles seccomp a servicios systemd
PROFILE=$1
SERVICE=$2
if [[ -z "$PROFILE" || -z "$SERVICE" ]]; then
echo "Uso: $0 <nginx-web-server|database-server|minimal-microservice> <nombre-servicio>"
exit 1
fi
# Crear directorios necesarios si no existen
mkdir -p /var/log/nginx /var/lib/nginx /var/cache/nginx /run/nginx
mkdir -p /etc/systemd/system/${SERVICE}.service.d/
cat > /etc/systemd/system/${SERVICE}.service.d/seccomp.conf << EOF
[Service]
# 1. Primero: definir rutas de lectura/escritura
ReadWritePaths=/var/log/nginx /var/lib/nginx /var/cache/nginx /run/nginx
# 2. Luego: protecciones de sistema
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
# 3. Restricciones de seguridad
NoNewPrivileges=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
# 4. Filtro de syscalls
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
EOF
systemctl daemon-reload
systemctl restart $SERVICE
echo "Perfil seccomp aplicado a $SERVICE"
We give permissions and execute the script
chmod +x /usr/local/bin/apply-seccomp.sh
/usr/local/bin/apply-seccomp.sh nginx-web-server nginx
Perfil seccomp aplicado a nginx
We verify that the service
root@srvlhm01:~# systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Drop-In: /etc/systemd/system/nginx.service.d
└─seccomp.conf
Active: active (running) since Fri 2026-02-13 17:49:06 UTC; 45s ago
Docs: man:nginx(8)
Process: 753 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 754 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 755 (nginx)
Tasks: 3 (limit: 4700)
Memory: 2.4M
CPU: 129ms
CGroup: /system.slice/nginx.service
├─755 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
├─756 "nginx: worker process"
└─757 "nginx: worker process"
How to modify Script for New Service
Add case in apply-seccomp.sh:
#!/bin/bash
PROFILE=$1
SERVICE=$2
# Crear directorios específicos del servicio
case "$SERVICE" in
nginx)
mkdir -p /var/log/nginx /var/lib/nginx /var/cache/nginx /run/nginx
READWRITE="/var/log/nginx /var/lib/nginx /var/cache/nginx /run/nginx"
;;
mysql|mariadb)
mkdir -p /var/log/mysql /var/lib/mysql /var/run/mysqld /tmp
READWRITE="/var/log/mysql /var/lib/mysql /var/run/mysqld /tmp"
;;
postgresql)
mkdir -p /var/log/postgresql /var/lib/postgresql /var/run/postgresql /tmp
READWRITE="/var/log/postgresql /var/lib/postgresql /var/run/postgresql /tmp"
;;
redis)
mkdir -p /var/log/redis /var/lib/redis /var/run/redis /tmp
READWRITE="/var/log/redis /var/lib/redis /var/run/redis /tmp"
;;
*)
echo "Servicio no configurado. Usando rutas genéricas."
READWRITE="/var/log/$SERVICE /var/lib/$SERVICE /var/run/$SERVICE /tmp"
mkdir -p $READWRITE
;;
esac
mkdir -p /etc/systemd/system/${SERVICE}.service.d/
cat > /etc/systemd/system/${SERVICE}.service.d/seccomp.conf << EOF
[Service]
ReadWritePaths=$READWRITE
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
EOF
systemctl daemon-reload
systemctl restart $SERVICE
echo "Perfil seccomp aplicado a $SERVICE"Exception Management
When a Service Fails
Step 1: Check specific error
journalctl -xeu SERVICIO --no-pager -n 50Step 2: Identify type of error
| Mistake | Cause | Solution |
|---|---|---|
Read-only file system | Missing directory in ReadWritePaths | Add route |
No such file or directory + NAMESPACE | Directory does not exist | mkdir -p before |
Operation not permitted | Syscall blocked | Review SystemCallFilter |
Permission denied | Insufficient capabilities | Adjust CapabilityBoundingSet |
/etc/systemd/system/SERVICIO.service.d/seccomp.conf:[Service]
# Añadir excepción de ruta
ReadWritePaths=/var/log/nginx /var/lib/nginx /var/cache/nginx /run/nginx /NUEVA/RUTA
# O añadir capability específica
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_NUEVA
# O relajar SystemCallFilter para syscalls específicas
SystemCallFilter=@system-service personality # añadir syscall al allowlistStep 4: Recharge and restart
systemctl daemon-reload
systemctl restart SERVICIOPractical Examples
MySQL/MariaDB
/usr/local/bin/apply-seccomp.sh database-server mysqlTypical exceptions:
/var/lib/mysql- Data/var/log/mysql– Logs/var/run/mysqld–Socket/tmp– Temporary
Additional Capability:
CapabilityBoundingSet=CAP_IPC_LOCK # Para bloquear memoria con mlockPostgreSQL
/usr/local/bin/apply-seccomp.sh database-server postgresql
/var/lib/postgresql/var/log/postgresql/var/run/postgresql
Redis
/usr/local/bin/apply-seccomp.sh minimal-microservice redisExceptions:
/var/lib/redis/var/log/redis/var/run/redis
Function Verification
# Verificar que el servicio está activo
systemctl status SERVICIO
# Verificar que las restricciones están aplicadas
systemctl show SERVICIO --property=ProtectSystem,ProtectHome,NoNewPrivileges,MemoryDenyWriteExecute
# Verificar syscalls bloqueadas (auditar denegaciones)
dmesg | grep -i "audit.*seccomp\|syscall"grep -E "CONFIG_IMA|CONFIG_IMA_APPRAISE|CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY" /boot/config-$(uname -r)5. Integrity Audit: AIDE (Advanced Intrusion Detection Environment)
Root of Trust Requirements
-
Secure storage: AIDE database should be saved on read-only media or immutable remote file system
-
Kernel hardening: KSPP protections prevent AIDE modification at runtime
-
Monitoring: Integration with SIEM for real-time alerts of critical modifications
Support Verification and Installation
# Instalación en Debian/Ubuntu
sudo apt update && sudo apt install -y aide aide-common
# Verificar instalación
aide --versionAIDE Configuration
/etc/aide/aide.conf# === CONFIGURACIÓN BASE ===
database_in=file:/var/lib/aide/aide.db
database_out=file:/var/lib/aide/aide.db.new
database_new=file:/var/lib/aide/aide.db.new
gzip_dbout=yes
verbose=5
# === REGLAS DE DEFINICIÓN ===
# Atributos a monitorear:
# p: permisos
# i: inode
# n: número de enlaces
# u: usuario
# g: grupo
# s: tamaño
# b: bloques
# m: mtime
# a: atime
# c: ctime
# S: checksum SHA256
# sha256: hash SHA256 completo
# Binarios críticos - monitoreo estricto
Binlib = p+u+g+s+b+m+c+sha256
# Archivos de configuración - detectar modificaciones
ConfFiles = p+u+g+s+b+m+c+sha256
# Logs - solo permisos y ownership (cambian frecuentemente)
Logs = p+u+g
# Directorios - estructura básica
Dirs = p+u+g
# === RUTAS A MONITOREAR ===
# Directorios de sistema críticos
/boot Binlib
/bin Binlib
/sbin Binlib
/lib Binlib
/lib64 Binlib
/usr/bin Binlib
/usr/sbin Binlib
/usr/lib Binlib
/usr/lib64 Binlib
# Archivos de configuración críticos
/etc ConfFiles
!/etc/mtab
!/etc/resolv.conf
!/etc/hdparam
!/etc/udev/devices
!/etc/network/run
!/etc/network/interfaces
!/etc/network/if-*
!/etc/network/if-*/.*
!/etc/puppet/ssl
!/etc/rc*.d
!/etc/init.d
!/etc/cron.d
!/etc/cron.daily
!/etc/cron.hourly
!/etc/cron.weekly
!/etc/cron.monthly
!/etc/logrotate.d
!/etc/logrotate.status
!/etc/prelink.cache
# Kernel y módulos
/lib/modules Binlib
/usr/src Binlib
# Archivos de servicios críticos
/usr/local/bin Binlib
/opt Binlib
# Exclusiones para reducir ruido
!/var/log/.*
!/var/cache/.*
!/var/tmp/.*
!/var/spool/.*
!/var/run/.*
!/var/lock/.*
!/proc
!/sys
!/dev
!/run
!/tmp
!/home
!/root/.bash_history
!/root/.lesshst
!/root/.viminfoDatabase Initialization
# Crear directorio para la base de datos
sudo mkdir -p /var/lib/aide
sudo chmod 700 /var/lib/aide
# Inicializar base de datos (primera vez - toma tiempo)
sudo aideinit
# El comando anterior crea /var/lib/aide/aide.db.new
# Mover a ubicación definitiva
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
sudo chmod 600 /var/lib/aide/aide.db
# Verificar que se creó correctamente
ls -la /var/lib/aide/Verification and Manual Checks
# Verificar integridad del sistema contra la base de datos
sudo aide --check
# Actualizar base de datos después de cambios legítimos
sudo aide --update
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.dbAutomation with Cron
Archive: /etc/cron.d/aide-check
# Verificación diaria de integridad a las 3:00 AM
0 3 * * * root /usr/bin/aide --check | mail -s "AIDE Check $(hostname)" root@localhost
# Actualización semanal de la base de datos (domingos a las 2:00 AM)
0 2 * * 0 root /usr/bin/aide --update && mv /var/lib/aide/aide.db.new /var/lib/aide/aide.dbIntegration with Systemd for Real-Time Alerts
Archive: /etc/systemd/system/aide-check.service
[Unit]
Description=AIDE Integrity Check
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/aide --check
StandardOutput=journal
StandardError=journal/etc/systemd/system/aide-check.timer[Unit]
Description=Run AIDE check every hour
Requires=aide-check.service
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.targetActivate timer:
sudo systemctl daemon-reload
sudo systemctl enable aide-check.timer
sudo systemctl start aide-check.timerGRUB Configuration with AIDE Support
Since AIDE operates in user space, it does not require special kernel parameters. However, we maintain the KSPP hardening:
GRUB_CMDLINE_LINUX_DEFAULT="quiet slab_nomerge page_alloc.shuffle=1 vsyscall=none pti=on spectre_v2=on spec_store_bypass_disable=seccomp l1tf=full mds=full,nosmt tsx=off net.ifnames=0 biosdevname=0"sudo update-grub
sudo rebootFunction Verification
# Verificar que AIDE está instalado
which aide
aide --version
# Verificar base de datos
ls -la /var/lib/aide/aide.db
# Ejecutar chequeo manual
sudo aide --check
# Ver logs de systemd si usas timer
journalctl -u aide-check.service --no-pager -n 20Automated Configuration Script for Web Servers
Archive: aide-setup-webserver.sh
#!/bin/bash
# AIDE Setup for Web Servers
# Configura AIDE para servidores web con monitoreo específico
set -euo pipefail
AIDE_DIR="/var/lib/aide"
CONF_DIR="/etc/aide"
LOG_DIR="/var/log/aide"
KEYS_DIR="/etc/keys/aide"
log() {
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $1"
}
error() {
echo "[ERROR] $1" >&2
exit 1
}
check_aide_support() {
if ! command -v aide &> /dev/null; then
log "Instalando AIDE..."
apt-get update && apt-get install -y aide aide-common || \
yum install -y aide || \
pacman -S aide
fi
if ! command -v aide &> /dev/null; then
error "No se pudo instalar AIDE"
fi
log "AIDE instalado: $(aide --version | head -1)"
}
setup_directories() {
mkdir -p "$AIDE_DIR" "$CONF_DIR" "$LOG_DIR" "$KEYS_DIR"
chmod 700 "$AIDE_DIR" "$KEYS_DIR"
chmod 755 "$CONF_DIR" "$LOG_DIR"
log "Directorios creados"
}
create_custom_config() {
log "Creando configuración personalizada para servidor web..."
cat > "${CONF_DIR}/aide-web.conf" << 'EOF'
# AIDE Configuration for Web Servers
database_in=file:/var/lib/aide/aide.db
database_out=file:/var/lib/aide/aide.db.new
database_new=file:/var/lib/aide/aide.db.new
gzip_dbout=yes
verbose=5
# Definiciones de reglas
Binlib = p+u+g+s+b+m+c+sha256
ConfFiles = p+u+g+s+b+m+c+sha256
WebFiles = p+u+g+s+b+m+c+sha256
Logs = p+u+g
# Sistema base
/boot Binlib
/bin Binlib
/sbin Binlib
/lib Binlib
/lib64 Binlib
/usr/bin Binlib
/usr/sbin Binlib
/usr/lib Binlib
/usr/lib64 Binlib
# Configuración del sistema
/etc ConfFiles
# Servidores web específicos
/etc/nginx ConfFiles
/etc/apache2 ConfFiles
/etc/httpd ConfFiles
/etc/php ConfFiles
/usr/share/nginx Binlib
/usr/share/apache2 Binlib
/var/www WebFiles
/var/www/html WebFiles
/var/www/cgi-bin WebFiles
# Aplicaciones web comunes
/opt/lampp Binlib
/usr/local/lampp Binlib
# Exclusiones
!/var/log/.*
!/var/cache/.*
!/var/tmp/.*
!/var/spool/.*
!/var/run/.*
!/var/lock/.*
!/proc
!/sys
!/dev
!/run
!/tmp
!/home
!/root/.bash_history
!/root/.lesshst
!/root/.viminfo
!/var/lib/aide
!/var/log/aide
EOF
chmod 644 "${CONF_DIR}/aide-web.conf"
log "Configuración creada en ${CONF_DIR}/aide-web.conf"
}
initialize_database() {
log "Inicializando base de datos AIDE..."
if [[ -f "${AIDE_DIR}/aide.db" ]]; then
log "Base de datos existente encontrada. ¿Regenerar? (s/N)"
read -r response
if [[ ! "$response" =~ ^[Ss]$ ]]; then
return
fi
backup_file="${AIDE_DIR}/aide.db.backup.$(date +%Y%m%d%H%M%S)"
cp "${AIDE_DIR}/aide.db" "$backup_file"
log "Backup creado: $backup_file"
fi
aide --config="${CONF_DIR}/aide-web.conf" --init
if [[ -f "${AIDE_DIR}/aide.db.new" ]]; then
mv "${AIDE_DIR}/aide.db.new" "${AIDE_DIR}/aide.db"
chmod 600 "${AIDE_DIR}/aide.db"
log "Base de datos inicializada correctamente"
else
error "No se pudo crear la base de datos"
fi
}
setup_cron_jobs() {
log "Configurando cron jobs..."
# Script de chequeo con notificación
cat > /usr/local/bin/aide-check-wrapper.sh << 'EOF'
#!/bin/bash
# Wrapper para AIDE con logging y alertas
LOG_FILE="/var/log/aide/aide-check-$(date +%Y%m%d-%H%M%S).log"
REPORT_FILE="/var/log/aide/aide-report-$(date +%Y%m%d-%H%M%S).txt"
echo "=== AIDE Check Started: $(date) ===" > "$LOG_FILE"
# Ejecutar chequeo
aide --config=/etc/aide/aide-web.conf --check > "$REPORT_FILE" 2>&1
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo "AIDE check completed: No changes detected" >> "$LOG_FILE"
logger -t aide "Integrity check passed - no changes"
else
echo "AIDE check completed: CHANGES DETECTED" >> "$LOG_FILE"
echo "See report: $REPORT_FILE" >> "$LOG_FILE"
logger -t aide "ALERT: Integrity check failed - changes detected"
# Enviar alerta (personalizar según infraestructura)
if command -v mail &> /dev/null; then
mail -s "AIDE ALERT: Changes detected on $(hostname)" root@localhost < "$REPORT_FILE"
fi
# Opcional: Integración con Slack/Discord/webhook
# curl -X POST -H 'Content-type: application/json' \
# --data '{"text":"AIDE Alert: Changes detected on '$(hostname)'"}' \
# YOUR_WEBHOOK_URL
fi
# Mantener solo los últimos 30 días de logs
find /var/log/aide -name "aide-check-*.log" -mtime +30 -delete
find /var/log/aide -name "aide-report-*.txt" -mtime +30 -delete
exit $RESULT
EOF
chmod +x /usr/local/bin/aide-check-wrapper.sh
# Cron job para chequeo diario
echo "0 3 * * * root /usr/local/bin/aide-check-wrapper.sh" > /etc/cron.d/aide-web-check
log "Cron jobs configurados"
}
create_status_script() {
cat > /usr/local/bin/aide-status << 'EOF'
#!/bin/bash
# AIDE Status Checker
echo "=== AIDE Status Report ==="
echo "Fecha: $(date)"
echo ""
if command -v aide &> /dev/null; then
echo "AIDE está instalado"
aide --version | head -1
echo ""
if [[ -f /var/lib/aide/aide.db ]]; then
echo "Base de datos: $(ls -lh /var/lib/aide/aide.db | awk '{print $5, $6, $7, $8}')"
echo "Última modificación: $(stat -c %y /var/lib/aide/aide.db)"
else
echo "ADVERTENCIA: No se encontró base de datos"
fi
echo ""
echo "Últimos chequeos:"
ls -lt /var/log/aide/aide-check-*.log 2>/dev/null | head -5 | awk '{print $6, $7, $8, $9}'
echo ""
echo "Para ejecutar chequeo manual: sudo aide --config=/etc/aide/aide-web.conf --check"
echo "Para actualizar base de datos: sudo aide --config=/etc/aide/aide-web.conf --update"
else
echo "AIDE NO está instalado"
fi
EOF
chmod +x /usr/local/bin/aide-status
log "Script de verificación creado: /usr/local/bin/aide-status"
}
create_emergency_script() {
cat > /usr/local/bin/aide-emergency << 'EOF'
#!/bin/bash
# AIDE Emergency Procedures
echo "=== AIDE Emergency Tools ==="
echo ""
case "$1" in
update)
echo "Actualizando base de datos AIDE..."
aide --config=/etc/aide/aide-web.conf --update
if [[ -f /var/lib/aide/aide.db.new ]]; then
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
echo "Base de datos actualizada"
fi
;;
compare)
echo "Comparando con backup..."
if [[ -f /var/lib/aide/aide.db.backup.* ]]; then
latest=$(ls -t /var/lib/aide/aide.db.backup.* | head -1)
echo "Comparando con: $latest"
# Nota: AIDE no soporta comparación directa de DBs, se requiere exportar
echo "Use: aide --config=/etc/aide/aide-web.conf --check"
else
echo "No se encontraron backups"
fi
;;
reset)
echo "REINICIALIZANDO BASE DE DATOS AIDE..."
echo "¿Estás seguro? Esto eliminará la base actual. (yes/no)"
read -r confirm
if [[ "$confirm" == "yes" ]]; then
rm -f /var/lib/aide/aide.db
aide --config=/etc/aide/aide-web.conf --init
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
echo "Base de datos reinicializada"
else
echo "Operación cancelada"
fi
;;
*)
echo "Uso: $0 {update|compare|reset}"
echo ""
echo " update - Actualizar base de datos después de cambios legítimos"
echo " compare - Intentar comparar con backup (manual)"
echo " reset - Reinicializar base de datos (¡PELIGROSO!)"
;;
esac
EOF
chmod +x /usr/local/bin/aide-emergency
log "Script de emergencia creado: /usr/local/bin/aide-emergency"
}
main() {
echo "=========================================="
echo " AIDE Setup for Web Servers"
echo "=========================================="
echo ""
echo "ADVERTENCIA: Este script configura AIDE para"
echo "monitoreo de integridad de archivos."
echo "Asegúrate de tener snapshots antes de continuar."
echo ""
echo "¿Continuar? (s/N)"
read -r confirm
if [[ ! "$confirm" =~ ^[Ss]$ ]]; then
log "Operación cancelada"
exit 0
fi
check_aide_support
setup_directories
create_custom_config
initialize_database
setup_cron_jobs
create_status_script
create_emergency_script
echo ""
echo "=========================================="
log "Configuración AIDE completada"
echo ""
echo "PRÓXIMOS PASOS:"
echo "1. Verificar estado con: /usr/local/bin/aide-status"
echo "2. Ejecutar chequeo manual: sudo aide --config=/etc/aide/aide-web.conf --check"
echo "3. Revisar logs en: /var/log/aide/"
echo ""
echo "IMPORTANTE:"
echo "- La base de datos debe almacenarse en medio de solo lectura"
echo "- Configurar notificaciones en /usr/local/bin/aide-check-wrapper.sh"
echo "- Programar actualizaciones de DB después de actualizaciones del sistema"
echo "=========================================="
}
main "$@"Supplementary file: aide-emergency-fix.sh
#!/bin/bash
# AIDE Emergency Procedures - Usar si hay demasiados falsos positivos
echo "=== AIDE Emergency Tools ==="
case "$1" in
log-only)
echo "Cambiando a modo solo-log (sin alertas)..."
sed -i 's/verbose=5/verbose=1/' /etc/aide/aide-web.conf
systemctl stop aide-check.timer 2>/dev/null || true
echo "AIDE configurado para logging mínimo"
;;
restore)
echo "Restaurando configuración normal..."
sed -i 's/verbose=1/verbose=5/' /etc/aide/aide-web.conf
systemctl start aide-check.timer 2>/dev/null || true
echo "AIDE restaurado"
;;
disable)
echo "Deshabilitando chequeos automáticos..."
systemctl disable aide-check.timer 2>/dev/null || true
systemctl stop aide-check.timer 2>/dev/null || true
rm -f /etc/cron.d/aide-web-check
echo "Chequeos automáticos deshabilitados"
;;
*)
echo "Uso: $0 {log-only|restore|disable}"
echo ""
echo " log-only - Reducir verbosidad temporalmente"
echo " restore - Restaurar configuración normal"
echo " disable - Detener todos los chequeos automáticos"
;;
esacQuick Use Instructions
# 1. Preparar entorno (VM recomendada)
sudo apt update && sudo apt install -y aide aide-common
# 2. Ejecutar hardening de GRUB
chmod +x kspp-grub-hardening.sh
sudo ./kspp-grub-hardening.sh
# 3. Configurar AIDE (antes de reiniciar)
chmod +x aide-setup-webserver.sh
sudo ./aide-setup-webserver.sh
# 4. Aplicar seccomp a servicios
chmod +x apply-seccomp.sh
sudo ./apply-seccomp.sh nginx-web-server nginx
# 5. Reiniciar y verificar
sudo reboot
# Después del reinicio:
sudo /usr/local/bin/aide-status
sudo aide --config=/etc/aide/aide-web.conf --check6. Entropy Analysis and Management
A cryptographically secure system depends on the quality of its random number generator (RNG). In virtualized environments, entropy is frequently insufficient because VMs lack sources of hardware noise (disk interrupts, thermal variations, etc.).
Entropy diagnosis
Check the current status:
cat /proc/sys/kernel/random/entropy_avail
# Valor ideal: > 3000
# En VMs recién iniciadas: frecuentemente < 1000Advanced settings
Archive: /etc/default/haveged
DAEMON_ARGS="-w 1024 -v 1"
# -w 1024: Mantener reserva de 1024 bits de entropía
# -v 1: Nivel de verbosidad para loggingPost-implementation verification
# Monitoreo continuo de entropía
watch -n 1 cat /proc/sys/kernel/random/entropy_avail
# Prueba de calidad del RNG
rngtest -c 1000 < /dev/randomAlternative: VirtIO-RNG (virtualized environments)
If your hypervisor supports it (KVM/QEMU), use the host entropy generator:
# Verificar dispositivo virtio-rng
ls /sys/devices/virtual/misc/hw_random/rng_available
# Debe mostrar "virtio_rng.0"
# Configurar como fuente primaria
sudo rngd -r /dev/hwrngImpact on hardening
Low entropy compromises:
- SSH/TLS key generation (predictable keys)
- Effective ASLR (predictable memory addresses)
- Crypto nonces (replay attacks)
With haveged or virtio-rng, we guarantee that the system has enough randomness to maintain the effectiveness of all previous mitigations.
Defense in Depth Architecture
This manual has implemented structural hardening that operates on multiple layers:Table
| Layer | Mechanism | Protection |
|---|---|---|
| Boot | KSPP kernel parameters | Security invariants before user space |
| Memory | Randomization, isolation | Prevention of corruption and exploitation |
| Grid | Sysctl hardening | Strict filtering and information hiding |
| Process | Seccomp + Capabilities | Reduction of syscalls and attack surface |
| Integrity | AIDE | Detection of unauthorized modifications |
| Cryptographic | Guaranteed entropy | Secure RNG Operations |
The result is a system where:
- Nginx buffer overflow exploit fails to execute shellcode (W^X, seccomp)
- An attacker with root access leaves detectable traces (AIDE monitoring critical binaries)
- A compromised process cannot escalate privileges (kptr_restrict, yama)
- Arbitrary code execution is prevented by design, not just patches
- Unauthorized modifications are detected and alerted (AIDE + centralized logging)
Key Differences: IMA vs AIDE
| Feature | IMA (Integrity Measurement Architecture) | AIDE (Advanced Intrusion Detection Environment) |
|---|---|---|
| Operation level | Kernel space | User space |
| Verification | In real time, blocks execution | Periodic, detects post-modification |
| Kernel requirements | CONFIG_IMA_APPRAISE, signatures in binaries | None special |
| Blockage risk | Kernel panic if signature is missing | Alerts only, no blocking |
| Complexity | Registration (MOK, signatures, initramfs) | Average (rules settings) |
| Resource usage | Low (built into kernel) | Moderate (periodic scans) |
| Rootkit detection | Limited (signed binaries only) | Complete (all files monitored) |
| Ideal for | High security critical systems | Web servers, intrusion detection |
-
Flexibility needed in update management
-
You cannot risk system availability
-
Change detection is required in configurations, not just binaries
-
Security team needs post-incident forensic audit
:wq!
Comments