Hacker News new | ask | show | jobs
by rollcat 1466 days ago
OpenBSD has removed loadable kernel modules back in 2014; macOS is aggressively moving in the same direction. Meanwhile - is running a Linux system without module support even viable these days?

$ du -sh /lib/modules/$(uname -r)

294M /lib/modules/5.10.0-15-amd64

7 comments

It's not that hard to run Linux without modules, I've been doing it on my laptop for a decade.

Just build the kernel and set the right options, this is for a Dell XPS13: https://github.com/jcalvinowens/misc/blob/main/kbuild/config...

It takes a few hours to whittle it down for a particular piece of hardware, but I've never broken anything on Debian by running kernels built with CONFIG_MODULE=n.

* Edited for clarity

What is that and how is it used?
Sorry that was really unclear, I edited. It's the kernel build configuration for my laptop, with module loading disabled. All the drivers are statically linked.
You can disable module loading at any time by writing to a /proc file: echo 1 > /proc/sys/kernel/modules_disabled

(you must reboot to re-enable module loading)

Useful on servers where specifying all modules to load is practical (netfilter modules are usually the only new modules unless hardware changes). But, on a workstation, doing so will be very frustrating unless you never plug in any new usb devices etc.

> But, on a workstation, doing so will be very frustrating unless you never plug in any new usb devices etc.

If you know what the devices you are likely to plug in, you could just modprobe them all before disabling it.

My impression is that Darwin did it by moving more drivers directly into user space. But yes, you can absolutely run Linux with everything statically compiled into the kernel as long as you're not using some handful of things that resist it (below comment mentions nvidia, ZFS). You can even run without an initial ramdisk if you're not doing RAID or ZFS or encrypted disks or something like that.

Edit: I should mention, this will either result in a massive kernel that consumes a lot of memory, or in very little driver support and your machine will not tend to just work when you plug new devices in. Linux has a lot of drivers; there's a reason why it uses modules.

I wonder if you can force the code to compile ZFS in, since the license problem is one of distribution not of user/runtime.

Ubuntu might not be able to distribute said "no module" kernel, but it might run.

I believe ZFS at least used to have an option to insert itself directly into a Linux source tree, in which case it would look just like a normal driver. I don't know if that still exists and I never tried it, but it was a thing. Note that you probably still need an initial ramdisk to get the userspace tools to actually bring a pool online if you're using it for root.

Edit: I'm having trouble finding it in the official documentation, but here's a page that describes how to do it on an old version: https://slackwiki.com/ZFS_root_(builtin) and here's what looks like a script to do that on the current tip of master: https://github.com/openzfs/zfs/blob/master/copy-builtin

How is the vector of persistence of any significance here? At some point the attackers got root access on your system, game over.
I believe the concern is that the attackers gain root access on system A but hide their presence/activity - even in the presence of logs to remote, more trusted server B.

https://github.com/c-blake/kslog has maybe a little more color on this topic, though I'm sure there are whole volumes written about it elsewhere. :)

EDIT: But maybe your "game over" point is just that it is kind of a pipe dream to hope to block all concealment tactics? That may be fair, but I think a lot of security folks cling to that dream. :)

> I believe the concern is that the attackers gain root access on system A but hide their presence/activity - even in the presence of logs to remote, more trusted server B.

That's generally called pivoting and has nothing to do with method of persistence of the malicious code.

OP makes a point that certain systems move or have moved away from giving root user the ability to extend/modify kernel code at runtime via kernel modules, my argument is that none of that matters since root user can still extend/modify kernel code at runtime via binary patching.

> my argument is that none of that matters since root user can still extend/modify kernel code at runtime via binary patching.

OpenBSD restricts that ability as well[1]. Neither /dev/mem nor /dev/kmem can be opened (read or write) during normal multi-user operation; you have to enter single-user mode (which requires serial console or physical access to achieve anything useful). Raw disk devices of mounted partitions can't be altered, immutable/append-only files can't be altered, etc.

You can also choose to completely prohibit access to raw disk devices, although that gets annoying when you e.g. need to format an external drive. There is of course still a lot of potential to do harm as root, but it's not as easy to create a persistent threat or resist in-system analysis by an administrator.

[1]: https://man.openbsd.org/securelevel

From your link:

> securelevel may no longer be lowered except by init

> The list of securelevel's effects may not be comprehensive.

So yes, it's a nice sandbox that can help prevent accidents, but doesn't sound like something you should rely on for actual defense.

You sound like you're dismissing it, but even if it wasn't all that useful on its own, it's a part of defense in depth strategy - it's just one layer in a carefully thought out system. Pledge/unveil is another, so is privsep+imsg, W^X, (K)ASLR, syscall origin verification, boot-time libc/kernel relinking, and a couple dozen other features I can't even recall now.

Most importantly, all of these features and mitigations are enabled by default, and are pretty much invisible to the end user or administrator; and actually easy to use for a developer. Contrast this with e.g. seccomp or SELinux. Google is even hinting "selinux permissive" and "selinux disable" in top 3 suggestions...

Ah. I misunderstood your "persistence" to mean "persistence of logs" not "of code/illicit powers". Sorry - I read too quickly.

I do think the defense mentality, as evidenced by many comments in this thread, remains a bit too much about "how challenging to make things" rather than the "in theory possibility". Besides binary patching a static kernel as you say, for example, you could have remote hashes of all relevant files a la tripwire, and remote access and programs to check said hashes. If the attacker can detect and adapt to a hash checking pattern then they can "provide the old file" for some purposes/etc. to hide their presence. To do so they have to also write the code to detect/conditionalize. The rationale of this defense mentality seems to hope for a "distribution of attacker laziness" that may at least "help", but sure - it is just a higher, finite bar. And once the work has been done..game over. But I do not mean to belabor the obvious. Anyway, thanks for clarifying your argument.

Aye, I think what you're describing is "security by obscurity" - i.e. the capability is still there, I'm just counting on the attacker not knowing that it is because I've hidden it so well. It can work really well in combination with actual security practices, but it absolutely shouldn't be considered one.
Certainly viable but your non-modular system might not support all the features you want. The Linux system I am using to post this comment is running without CONFIG_MODULES and have been so for years, but I am not using ZFS nor anything from NVIDIA.

     ~ # zgrep -F CONFIG_MODULES /proc/config.gz 
    CONFIG_MODULES_USE_ELF_RELA=y
    # CONFIG_MODULES is not set
    CONFIG_MODULES_TREE_LOOKUP=y
I'm not sure I understand, how OpenBSD load drivers then?
IIRC the *BSDs are much more likely to recompile the kernel and reboot if needed for new hardware, whereas most Linux distributions have gone the "build every possible driver in the world as a module, load as needed" route.
They're all statically linked into the kernel.

$ uname -sr

OpenBSD 7.1

$ du -sh /bsd

22.0M /bsd