The current profile enables a wide range of hardening options by default, but over time it has become clear that:
It lacks a consistent and transparent baseline or standard,
It may introduce unexpected breakage or degrade performance without clear benefit,
It is difficult to manage user expectations, especially since the implications of enabling it are not always obvious,
and as multiple contributors have noted, it is often more of a “grab bag” of settings than a cohesive security policy.
We’ve discussed this extensively in the nixpkgs / NixOS contributions matrix channel. The consensus among several maintainers (including K900 and emilazy) is that it’s time to remove this profile entirely rather than continue maintaining something that may give users a false sense of security or create unpredictable outcomes.
If you are currently using this module, I’d really appreciate hearing your perspective on this change. The idea is to post this proposal here, give it about a week for feedback, and then proceed with the merge if there are no strong objections.
So I, uh, just enabled this on my servers last week. Didn’t realize it was slated for deprecation.
I’m not relying on it to be any sort of perfect security shield or anything. I was expecting it to be an opinionated batch of changes that have been generally considered to be good (or at least not counterproductive) ideas in contexts where security is more important than performance. I smoke-tested all my services, found one that really didn’t like the scudo allocator, changed the allocator back to libc, and moved on with all the ‘sense of security’ that I get when taking an immune system vitamin supplement—there’s some outside chance it catches something but it’s no license to slack off.
I guess if someone more knowledgeable than me adopts it out of tree, I can migrate to that the same way I migrated to nixos-modules/modules/no-graphics-packages.nix at a361718a47791401a2825dcd41550e9332fb2e20 · NuschtOS/nixos-modules · GitHub when that got booted out of tree. But I do wish that, even when they aren’t getting as much love as they should, these add-in modules could stick around in tree for new people to make one-off improvements to when they find things to add, instead of becoming one person’s largely undiscoverable personal project.
I see these opinionated modules as not a great fit for nixpkgs. Everyone may have a different threat model, I think it’s a bit unreasonable to have this maintained in nixpkgs, which I believe is meant to be approximate consensus (based on the existence of things like RFCs in the first place).
If there were some generally agreed-upon guidelines for this profile, then I’d be for keeping it around. But like with the headless config, there aren’t.
For discoverability, maybe nix-community is an option.
The profiles, like the default NixOS configuration itself, are just starting points, not one-size-fits-all solutions. I don’t think we should hold them to the standard of needing to fit some specified threat model.
Here’s my pitch for a guideline: NixOS is already opinionated. Just like any other distro, we make choices about what services, kernel options, etc. to enable by default. Sometimes there isn’t an obvious best choice, and in those cases where one choice would make a system ‘harder’ while the alternatives result in a system that is better for other reasons, if we choose one of the alternatives for the default NixOS configuration, toss the hardened option into the hardened profile. The result is likely going to need to be trimmed back by configurations into something that is usable—but that’s still more useful as a starting point than starting from the base NixOS configuration and wondering what in all of those options could have been done differently!
It’s not as if wanting a more attack-resistant OS is a niche desire or anything! What’s the downside to keeping it around? @K900 was really enthusiastic for killing it with fire in the quoted Matrix conversation and… why? It’s one small module that isn’t imported by default; you have to already know a bit about what you’re doing in order to enable it. The maintenance burden has to be negligible. What do we get by destroying this useful rallying point or making it less discoverable by punting it somewhere else?
Don’t really have time for a properly‐structured reply, but FWIW as a (neglectful) maintainer of the hardened profile for years who carried the half‐hearted intent to renovate it to be significantly better for some years:
It is not in a good state at present and I do not recommend anyone who doesn’t fully understand it from applying it to their machines.
In particular, it almost certainly significantly lowers the security of the average desktop system because it disables unprivileged user namespaces which, while lowering kernel attack surface, breaks browser sandboxing unless additional setup (which it does not do) is arranged – I do not think the reduction in kernel attack surface the hardened profile provides compensates for undermining the #1 most important front line of defence on desktop systems. On a server it’s more likely to be net positive, but there are still complicated trade‐offs.
There are also more minor issues like it configuring things in ways that conflict with features the linux-hardened patchset is meant to provide and somewhat conflating debugging and hardening features.
The hardened kernels haven’t always been updated in a timely manner in Nixpkgs in the past, which is a security issue of its own.
These have been known issues for years but it has been barely maintained for years. The maintenance burden is only negligible because nobody – me included – has put any effort into addressing the significant issues.
I think it is sensible to have a profile/option/whatever that says “when there is a relatively uncomplicated performance‐vs.‐security trade‐off within reasonable bounds, I’d like to pick the latter, and I am also willing to sacrifice some amount of functionality for it as long as the resulting system still maintains wide compatibility with common use cases”. I know some people think this line is hard to draw in general and that you can’t just offer “go faster stripes” for security without a concrete threat model, leading it to inevitably offer something it can’t deliver on for many users, and I respect that, but I do think that there is in principle a reasonable middle ground it could offer.
Nevertheless the profile does not currently do a good job of satisfying this preference and is quite a footgun. It sacrifices too much functionality to be usable out of the box on most systems, and even some of its non‐obsolete choices can undermine practical security. It also does not implement @rhendric’s notion of “flip the ambiguous defaults to the more secure option”; it doesn’t even touch most of the things you’d want to do in practice for that. Insofar as #6 is true, #7 is why there’s a problem: enabling an option for a non‐niche desire should not make things worse.
Therefore despite my belief that it is sensible in theory and my long‐postponed wishes to make the profile better I can’t object to a proposal to deprecate it in its current state.
“It’s one small module that isn’t imported by default; you have to already know a bit about what you’re doing in order to enable it” is true and why I’ve mostly just shrugged off the current state of the profile other than giving people heads up when they talk about it on Matrix. The context is a proposal to convert it into a proper enable toggle option that is documented in the NixOS manual, though; I said that that option should come with a warning of the pitfalls of its current state if it is added, which developed into a discussion of whether it should just be removed. I’m agnostic to the ultimate outcome there, but I must admit that if I was not still telling myself I might one day renovate the entire thing it would be pretty obvious to me that it should go.
Sorry for not reacting to any of this sooner, I get way too much email.
Just to substantiate this a little so that I’m not using browser sandboxes as the only example here, something that’s relevant on servers too: it disables net.core.bpf_jit_enable to mitigate BPF JIT spray attacks. However other distros have moved in the opposite direction: for instance the Arch kernel sets CONFIG_BPF_JIT_ALWAYS_ON to disable the interpreter and always use JIT compilation, which was introduced as a security option after the interpreter was used as part of the initial demonstration of Spectre as it provides a wide range of ROP gadgets to use in kernel exploits. That was in 2018.
And, yeah, sure, the JIT is also something of a security hotspot and it’s sensible to be concerned about it – but the hardened kernel patchset already disables it for non‐root users, so this defends against root performing JIT spray attacks at the expense of leaving a ton of gadgets in the kernel. Net negative, I think, especially as the JIT presumably gets far more maintenance and security attention now that everyone is turning off the interpreter and given its performance advantages.
And yeah, it would be a one line patch to fix this, but I pulled up the details of this just now from a NixOS security/hardening todo list I was maintaining half a decade ago and I didn’t do most of that stuff either. It’s hard to maintain a good profile like this in general but NixOS’s is currently pretty bad, especially given the relatively small number of defaults it does tweak that have a net‐positive effect. (Potentially significant positive effects, sure! But you can get most of the bang‐for‐buck in a dozen lines of your own configuration, which has the advantage that you take ownership of understanding and maintaining them!)
Also the hardened patchset is really designed to be used with a strong system‐wide MAC implementation and has been explicitly disclaimed as such for years, and that’s just not something we have at all. (With all respect to the AppArmor work going on – there’s just a large gap between even the best existing desktop AppArmor implementations and the kind of system‐wide pervasive SELinux lockdown used in the Android setting the hardened patchset originates from.)
All that sounds very reasonable, and thank you for your perspective and time!
So what about just emptying the profile and starting fresh?
Here’s where I’m coming from on this: Yesterday (thanks to some corpo press release that made the rounds but isn’t worth linking) I learned about io_uring. Clearly many people involved in Linux infosec have known about io_uring since years ago, but I’m not one of those people. In particular, I learned that Google considers io_uring to be a major source of vulnerabilities and consequently disables it on ChromeOS and their production servers, and I believe RHEL ships with it disabled as well.
Now, I freely admit to being a cargo-cult infosec amateur, but I see those things and I think, maybe I want this disabled too. Does NixOS disable it by default? No, it turns out, though grepping Nixpkgs throws up some comments that suggest that the Nix sandbox does. Should NixOS disable it by default? I have no idea! On the one hand, it seems like this is a feature that is connected to performance, and someone out there will be unhappy if disabling io_uring costs them 5 FPS on whatever the shooter game of the day is. On the other hand, I don’t know what I’m doing, but Google and RHEL probably do?
But I don’t want to just sit on my new-found amateur-level knowledge and hoard it in my own config, leaving other people to learn it for themselves the same way I did or remain in blissful ignorance. That’s not the FOSS way. So I opened this PR, because hardened.nix seems like exactly the right place for this kind of speculative-but-also-not-entirely-ungrounded config tweak that might make your system more secure, no guarantees, this does not constitute professional infosec advice.
So… if we gut all the cruft out of hardened.nix, but still leave it around for PRs like mine, then I still have a place to share this and have other community members participate in the conversation about whether it’s appropriate and helpful or not, just like everything else in Nixpkgs.
If we remove it entirely, I’m left ‘owning’ my own configuration, and it helps nobody else, and it doesn’t even help me as much as it could if other people were looking at it—even very occasionally!—and helping to come up with reasons like the ones you go into for why it does or doesn’t make sense. Who benefits from doing that?
Probably removing it without prejudice to re‐adding a better one later if an appropriate and motivated maintainer comes along is the best way to do that? Just hollowing it out would be a silent breaking change for every existing user that might be relying on it, which would be pretty bad – at least a removed module lets people know they have to replace it. And of course an empty profile is not very useful.
Many of the things it sets are sensible enough, too; the problem is sifting the ones that are from the ones that aren’t, and wiping it wouldn’t solve that. Or more specifically, the problem is someone qualified to do that having the time to do it. And a profile like this does need designated maintainers, I think, precisely because it will consist – almost in its entirety – of non‐trivial judgement calls. (Yeah, Nixpkgs and NixOS have to make a lot of judgement calls already. But they are more dense here and generally harder, without “let’s just do the default / what other distros do” to fall back on, and with an advertisement of enhanced security on the line. And that’s an example of why I don’t entirely dismiss the idea that in‐tree isn’t the place for it – it may be less possible to reach consensus on this than on the kinds of things we normally do, and it is more prone to becoming incoherent without a strong vision behind it.)
Yeah I’m not sure how I feel about this one. io_uring has indeed been a huge vulnerability hotspot and if you don’t need any programs that rely on it and performance isn’t a concern then turning it off seems clearly net positive for that at present.
OTOH it is also sort of the only reasonable Linux I/O interface, likely to be the future of all Linux asynchronous I/O or even just the majority of userspace–kernel interaction, and is just generally on a trajectory to being increasingly important to the Linux platform going forwards. On a general‐purpose distro designed to run software at large, I worry that it sets us up to have a bunch of things simply fail to run with a cryptic error in a couple years’ time. (ChromeOS doesn’t have to worry about running arbitrary code; RHEL already needs extending with third‐party repositories for many tasks and also you have a support contract I suppose, but I suspect another factor is “mostly RHEL users are running old code”.)
Probably it makes sense to toggle off by default for a configuration opting in to the hardened profile and we can hope that it gets better over the next couple years? But these are really not easy calls to make, and also someone would have to actually follow up once it changes, and – I already see the “hey why doesn’t my web server start up when the hardened profile is on?” issue reports in my head and that’s the kind of thing where we really need an active maintainer putting time into the profile who keeps up with the evolving threat landscape and understands Linux security in depth otherwise nobody is going to notice that the default no longer makes sense. (See: the BPF thing.)
I think the difference is that “a profile (or, even moreso, an option) provided with NixOS” and “some random guy’s config you can use, if you trust him I guess, don’t sue” have very different optics and expectations. It’s precisely the discoverability that gives it presently undue weight. It’s pretty obvious what you’re asking for when you tell NixOS to enable Plasma, and we have a good amount of maintainer resources dedicated to keeping it doing a reasonable thing, and while there are judgement calls involved in that, it offers an experience broadly comparable to other Plasma distros.
“Hardened” isn’t quite like that. It means many things and in the absence of guidance from someone with the expertise to understand what’s going into it and how the various settings interact with each other and the rest of the system, it can easily end up being placebo or actively harmful. And it does require ongoing maintenance, because the security landscape changes from year to year – disabling the BPF JIT in favour of the interpreter probably seemed like a pretty good idea at one point before the attack landscape evolved.
So like, if I felt we had enough of that expertise interested in maintaining it, that would be great! If you figure out a way to clone me (and also make me reliably interested in doing one thing for an extended period of time, which is probably the hard part), then I’m certainly not the best choice on the planet, but I think I could do a reasonable job of shepherding it. Still in practice it’s mostly been rotting for years and unless something changes I think hitting the reset button would only be inviting in a new set of cobwebs.
(This circles back to the “hardened kernel is designed for use with a complete MAC setup” thing by the way: Ubuntu has it both ways by using AppArmor to grant things like browsers access to unprivileged user namespaces. There’s a lot of missing pieces to make that work for something with NixOS’s architecture and the failure mode is ~silently degraded browser sandboxing which is scary.)