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.
       
 (HTM) 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.
       
 (HTM) OpenSnitch wiki: getting started