DEV Community

Bartosz Osiej
Bartosz Osiej

Posted on

I built a Linux Security Module that lives in-tree and boots from upstream

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/aegis into the tree — nothing special, no out-of-tree hacks
  • Four .patch files integrate it: LSM hook table, UAPI, Kconfig, Makefile
  • CI clones torvalds/linux at the exact base commit (4d7d9486c04d…), applies the patches, runs make prepare, then compiles security/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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

  1. LSM_HOOK_INIT makes 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.
  2. 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.
  3. In-tree beats out-of-tree — when your module lives in the tree, make prepare + Kconfig handle 90% of the integration headaches.
  4. 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)
Enter fullscreen mode Exit fullscreen mode

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)