I built a Linux Security Module that lives in-tree and boots from upstream
Stackable LSM, four feature-flagged layers, CI compiling it in-tree, and a kernel that boots it in QEMU.
Why?
Most "security" hobby projects live in user space. I wanted something that lives where the actual protection happens — inside the kernel's LSM framework — and that a reviewer could compile from a clean torvalds/linux checkout with two commands.
That's AEGIS (Advanced Guardian for Integrated System Security).
~1,700 lines of C, six source files, built against upstream Linux 7.3-rc1.
What it does
AEGIS hooks the kernel's LSM framework and adds four independently-compilable layers:
| Layer | Hooks | What it does |
|---|---|---|
| Process protection |
task_alloc / task_free, ptrace_*
|
Tracks protected processes; restricts agent attach and PTRACE_TRACEME to harden against debugger-aided exploits |
| File integrity |
file_open, file_permission, inode_permission
|
SHA-256 digest tracking + write-protection for protected system files |
| Syscall audit | bprm_check_security |
Blocks or logs dangerous syscalls per process policy |
| Module control |
kernel_load_data, kernel_read_file
|
Restricts runtime loading of kernel modules |
It stacks in the security= chain (LSM_HOOK_INIT in one registered hook table), so it coexists with capability, Yama and AppArmor instead of replacing them.
Each layer is a Kconfig symbol:
CONFIG_SECURITY_AEGIS=y # main LSM
CONFIG_SECURITY_AEGIS_PROCESS_PROTECT=y # anti-ptrace / anti-debugging
CONFIG_SECURITY_AEGIS_FILE_INTEGRITY=y # SHA-256 integrity + write protect
CONFIG_SECURITY_AEGIS_SYSCALL_AUDIT=y # syscall block/log
CONFIG_SECURITY_AEGIS_MODULE_CONTROL=y # module loading control
Runtime control is exposed two ways — sysctl and securityfs:
$ sysctl kernel/aegis
kernel.aegis.ptrace_restrict_all = 1
$ cat /sys/kernel/security/aegis/status
$ cat /sys/kernel/security/aegis/protected_procs
$ cat /sys/kernel/security/aegis/protected_files
$ cat /sys/kernel/security/aegis/blocked_syscalls
The hard part: building it like upstream code
The whole module is written as if it were being merged — that was the rule.
-
cp -r aegis security/aegisinto the tree — nothing special, no out-of-tree hacks - Four
.patchfiles integrate it: LSM hook table, UAPI, Kconfig, Makefile - CI clones
torvalds/linuxat the exact base commit (4d7d9486c04d…), applies the patches, runsmake prepare, then compilessecurity/aegis/in-tree - The actual kernel builds and boots it in QEMU through a minimal static initramfs
$ make -s kernelversion
7.3.0-aegis
/ # uname -r
7.3.0-1-aegis
/ # aegisctl status
AEGIS LSM status: enabled
Feature flags: process-protect file-integrity syscall-audit module-control
Protected procs: 12
Protected files: 5
Blocked syscalls: 3
Audit events: 214
apply.sh reproduces the whole thing — clone, integrate, patch, configure, build:
$ ./apply.sh
==> Cloning upstream kernel...
==> Installing AEGIS module source
==> Applying integration patches
==> Copying build configuration
==> Building kernel (this takes a while)...
==> Done. Kernel: /tmp/aegis-build/linux/arch/x86/boot/bzImage
And the devkit turns it into a bootable mini-OS: static PID 1, an aegisctl control tool, an initramfs, and a QEMU launcher (nographic, gdb, smp, mem).
What I learned
-
LSM_HOOK_INITmakes stacking trivial — the modern hook table is the cleanest kernel extension point I've touched. Registering one table per module is genuine composition, not forks. -
bpf_probe_read-less logic calls are the easy part — the hard part is matching the kernel's expectations about ordering and return semantics of each hook. -
In-tree beats out-of-tree — when your module lives in the tree,
make prepare+ Kconfig handle 90% of the integration headaches. - CI for kernel code pays off immediately — having the module compiled in-tree on every push caught real breaks (a POSIX-shell portability fix in the devkit Makefile, for example).
Try it
$ git clone --single-branch --branch master https://github.com/torvalds/linux.git
$ cd linux
$ git checkout 4d7d9486c04d917265f64c55bd23b2cc4fe7749c
$ cp -r ../aegis security/aegis
$ git apply ../patches/*.patch
$ cp ../build/aegis.config .config && make olddefconfig
$ make -j$(nproc)
Or boot it: cd devkit && make initramfs && make qemu.
Repo: github.com/BartoszOsiej/linux-aegis
Built by Bartosz Osiej — 19, Poland, open to first paid role. Everything here is deployed infrastructure, not a tutorial.
Top comments (0)