summary refs log tree commit diff
path: root/nixos/tests/hardened.nix
blob: 5ed0dfcf9ab8fd9b9d3b527556913817be190f39 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import ./make-test.nix ({ pkgs, latestKernel ? false, ... } : {
  name = "hardened";
  meta = with pkgs.stdenv.lib.maintainers; {
    maintainers = [ joachifm ];
  };

  machine =
    { lib, pkgs, config, ... }:
    with lib;
    { users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; };
      users.users.sybil = { isNormalUser = true; group = "wheel"; };
      imports = [ ../modules/profiles/hardened.nix ];
      boot.kernelPackages =
        lib.mkIf latestKernel pkgs.linuxPackages_latest_hardened;
      environment.memoryAllocator.provider = "graphene-hardened";
      nix.useSandbox = false;
      virtualisation.emptyDiskImages = [ 4096 ];
      boot.initrd.postDeviceCommands = ''
        ${pkgs.dosfstools}/bin/mkfs.vfat -n EFISYS /dev/vdb
      '';
      fileSystems = lib.mkVMOverride {
        "/efi" = {
          device = "/dev/disk/by-label/EFISYS";
          fsType = "vfat";
          options = [ "noauto" ];
        };
      };
      boot.extraModulePackages =
        optional (versionOlder config.boot.kernelPackages.kernel.version "5.6")
          config.boot.kernelPackages.wireguard;
      boot.kernelModules = [ "wireguard" ];
    };

  testScript =
    let
      hardened-malloc-tests = pkgs.stdenv.mkDerivation {
        name = "hardened-malloc-tests-${pkgs.graphene-hardened-malloc.version}";
        src = pkgs.graphene-hardened-malloc.src;
        buildPhase = ''
          cd test/simple-memory-corruption
          make -j4
        '';

        installPhase = ''
          find . -type f -executable -exec install -Dt $out/bin '{}' +
        '';
      };
    in
    ''
      $machine->waitForUnit("multi-user.target");

      subtest "apparmor-loaded", sub {
          $machine->succeed("systemctl status apparmor.service");
      };

      # AppArmor securityfs
      subtest "apparmor-securityfs", sub {
          $machine->succeed("mountpoint -q /sys/kernel/security");
          $machine->succeed("cat /sys/kernel/security/apparmor/profiles");
      };

      # Test loading out-of-tree modules
      subtest "extra-module-packages", sub {
          $machine->succeed("grep -Fq wireguard /proc/modules");
      };

      # Test hidepid
      subtest "hidepid", sub {
          $machine->succeed("grep -Fq hidepid=2 /proc/mounts");
          # cannot use pgrep -u here, it segfaults when access to process info is denied
          $machine->succeed("[ `su - sybil -c 'ps --no-headers --user root | wc -l'` = 0 ]");
          $machine->succeed("[ `su - alice -c 'ps --no-headers --user root | wc -l'` != 0 ]");
      };

      # Test kernel module hardening
      subtest "lock-modules", sub {
          # note: this better a be module we normally wouldn't load ...
          $machine->fail("modprobe dccp");
      };

      # Test userns
      subtest "userns", sub {
          $machine->succeed("unshare --user true");
          $machine->fail("su -l alice -c 'unshare --user true'");
      };

      # Test dmesg restriction
      subtest "dmesg", sub {
          $machine->fail("su -l alice -c dmesg");
      };

      # Test access to kcore
      subtest "kcore", sub {
          $machine->fail("cat /proc/kcore");
      };

      # Test deferred mount
      subtest "mount", sub {
        $machine->fail("mountpoint -q /efi"); # was deferred
        $machine->execute("mkdir -p /efi");
        $machine->succeed("mount /dev/disk/by-label/EFISYS /efi");
        $machine->succeed("mountpoint -q /efi"); # now mounted
      };

      # Test Nix dæmon usage
      subtest "nix-daemon", sub {
        $machine->fail("su -l nobody -s /bin/sh -c 'nix ping-store'");
        $machine->succeed("su -l alice -c 'nix ping-store'") =~ "OK";
      };

      # Test kernel image protection
      subtest "kernelimage", sub {
        $machine->fail("systemctl hibernate");
        $machine->fail("systemctl kexec");
      };

      # Test hardened memory allocator
      sub runMallocTestProg {
          my ($progName, $errorText) = @_;
          my $text = "fatal allocator error: " . $errorText;
          $machine->fail("${hardened-malloc-tests}/bin/" . $progName) =~ $text;
      };

      subtest "hardenedmalloc", sub {
        runMallocTestProg("double_free_large", "invalid free");
        runMallocTestProg("unaligned_free_small", "invalid unaligned free");
        runMallocTestProg("write_after_free_small", "detected write after free");
      };
    '';
})