Title: Harden your NixOS workstation | |
Author: Solène | |
Date: 13 January 2022 | |
Tags: nix nixos security | |
Description: This explains how to tweak your NixOS system to make it | |
hardened in regards to security | |
# Introduction | |
Coming from an OpenBSD background, I wanted to harden my NixOS system | |
for better security. As you may know (or not), security mitigations | |
must be thought against a security threat model. My model here is to | |
prevent web browsers to leak data, prevent services to be exploitable | |
remotely and prevent programs from being exploited to run malicious | |
code. | |
NixOS comes with a few settings to improve in these areas, I'll share a | |
sample of configuration to increase the default security. Unrelated to | |
security defense itself, but you should absolutely encrypt your | |
filesystem, so in case of physical access to your computer no data | |
could be extracted. | |
# Use the hardened profile | |
There are a few profiles available by default in NixOS which are files | |
with a set of definitions and one of them is named "hardened" because | |
it enables many security measures. | |
Link to the hardened profile definition | |
Here is a simplified list of important changes: | |
* use the hardened Linux kernel (different defaults and some extra | |
patches from https://github.com/anthraxx/linux-hardened/) | |
* use the memory allocator "scudo", protecting against some buffer | |
overflow exploits | |
* prevent kernel modules to be loaded after boot | |
* protect against rewriting kernel image | |
* increase containers/virtualization protection at a performance cost | |
(L1 flush or page table isolation) | |
* apparmor is enabled by default | |
* many filesystem modules are forbidden because old/rare/not audited | |
enough | |
* many other specific tweaks | |
Of course, using this mode will slightly reduce the system performance | |
and may trigger some runtime problems due to the memory management | |
being less permissive. On one hand, it's good because it allows to | |
catch programming errors, but on the other hand it's not fun to have | |
your programs crashing when you need them. | |
With the scudo memory allocator, I have troubles running Firefox, it | |
will only start after 2 or 3 crashes and then will work fine. There is | |
a less permissive allocator named graphene-hardened, but I had too much | |
troubles running programs with it. | |
# Use firewall | |
One simple rule is to block any incoming traffic that would connect to | |
listening services. It's way more secure to block everything and then | |
allow the services you know must be open to the outside than relying on | |
the service's configuration to not listen on public interfaces. | |
# Use Clamav | |
Clamav is an antivirus, and yes it can be useful on Linux. If it can | |
prevent you at least once to run a hostile binary, then it's worth | |
running it. | |
# Firejail | |
I featured firejail previously on my blog, I'm convinced of its | |
usefulnes. You can run a program using firejail, and it will restrict | |
its permissions and rights so in case of security breach, the program | |
will be restricted. | |
This is rather important to run web browsers with it because it will | |
prevent them any access to the filesystem except ~/Downloads/ and a few | |
required directories (local profile, /etc/resolv.conf, font cache | |
etc...). | |
# Enable this on NixOS | |
Because NixOS is declarative, it's easy to share the configuration. My | |
configuration supports both Firefox and Chromium, you can remove the | |
related lines you don't need. | |
Be careful about the import declaration, you certainly already have one | |
for the ./hardware-configuration.nix file. | |
```/etc/nixos/configuration.nix file | |
imports = | |
[ | |
./hardware-configuration.nix | |
<nixpkgs/nixos/modules/profiles/hardened.nix> | |
]; | |
# enable firewall and block all ports | |
networking.firewall.enable = true; | |
networking.firewall.allowedTCPPorts = []; | |
networking.firewall.allowedUDPPorts = []; | |
# disable coredump that could be exploited later | |
# and also slow down the system when something crash | |
systemd.coredump.enable = false; | |
# required to run chromium | |
security.chromiumSuidSandbox.enable = true; | |
# enable firejail | |
programs.firejail.enable = true; | |
# create system-wide executables firefox and chromium | |
# that will wrap the real binaries so everything | |
# work out of the box. | |
programs.firejail.wrappedBinaries = { | |
firefox = { | |
executable = "${pkgs.lib.getBin pkgs.firefox}/bin/firefox"; | |
profile = "${pkgs.firejail}/etc/firejail/firefox.profile"; | |
}; | |
chromium = { | |
executable = "${pkgs.lib.getBin pkgs.chromium}/bin/chromium"; | |
profile = "${pkgs.firejail}/etc/firejail/chromium.profile"; | |
}; | |
}; | |
# enable antivirus clamav and | |
# keep the signatures' database updated | |
services.clamav.daemon.enable = true; | |
services.clamav.updater.enable = true; | |
``` | |
Rebuild the system, reboot and enjoy your new secure system. | |
# Going further: network filtering | |
If you want to absolutely control your network connections, I'd | |
absolutely recommend the service OpenSnitch. This is a daemon that | |
will listen to all the network done on the system and allow you to | |
allow/block connections per executable/source/destination/protocol/many | |
parameters. | |
OpenSnitch comes with a GUI app called opensnitch-ui which is | |
mandatory, if the ui is not running, no filtering is done. When the ui | |
is running, every time a new connection is not matching an existing | |
rule, you will be prompted with information telling you what executable | |
is trying to do on which protocol with which host, then you can decide | |
how long you allow this (or block). | |
Just use `services.opensnitch.enable = true;` in the system | |
configuration and run opensnitch-ui program in your graphical session. | |
To have persistent rules, open opensnitch-ui, go in the Preferences | |
menu and tab Database, choose "Database type: File" and pick a path to | |
save it (it's a sqlite database). | |
From this point, you will have to allow / block all network done on | |
your system, it can be time-consuming at first, but it's user-friendly | |
enough and rules can be done like "allow this entire executable" so you | |
don't have to allow every website visited by your web browser (but you | |
could!). You may be surprised by the amount of traffic done by non | |
networking programs. After some time, the rule set should be able to | |
cope with most of your needs without needing to add new entries. | |
OpenSnitch wiki: getting started |