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");
};
'';
})
|