Skip to content

Commit 14c3272

Browse files
committed
lenovo-x1-gen11-hardening: build image with dm-verity
Signed-off-by: Humaid Alqasimi <humaid.alqassimi@tii.ae>
1 parent 1d7d320 commit 14c3272

File tree

14 files changed

+452
-82
lines changed

14 files changed

+452
-82
lines changed

docs/src/release_notes/ghaf-25.04.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,3 @@ Fixed bugs that were present in the [ghaf-25.03](../release_notes/ghaf-25.03.md)
123123
Released images are available at [archive.vedenemo.dev/ghaf-25.04](https://archive.vedenemo-dev/ghaf-25.04).
124124

125125
Download the required image and use the following instructions: [Build and Run](../ref_impl/build_and_run.md).
126-
127-

modules/common/systemd/base.nix

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ let
5959
inherit (cfg) withUkify;
6060
withUserDb = cfg.withHomed;
6161
withUtmp = cfg.withJournal || cfg.withAudit;
62+
withSysupdate = true;
6263
}
6364
// lib.optionalAttrs (lib.strings.versionAtLeast pkgs.systemdMinimal.version "255.0") {
6465
withVmspawn = cfg.withMachines;
@@ -230,7 +231,7 @@ in
230231
withRepart = mkOption {
231232
description = "Enable systemd repart functionality.";
232233
type = types.bool;
233-
default = false;
234+
default = true;
234235
};
235236

236237
withHomed = mkOption {
@@ -290,13 +291,13 @@ in
290291
withCryptsetup = mkOption {
291292
description = "Enable systemd LUKS2 functionality.";
292293
type = types.bool;
293-
default = false;
294+
default = true;
294295
};
295296

296297
withFido2 = mkOption {
297298
description = "Enable systemd Fido2 token functionality.";
298299
type = types.bool;
299-
default = false;
300+
default = true;
300301
};
301302

302303
withTpm2Tss = mkOption {

modules/hardware/common/input.nix

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ let
88
in
99
{
1010
config = {
11-
# Disk configuration
12-
# TODO Remove or move this
13-
disko.devices.disk = cfg.disks;
14-
1511
# Host udev rules for input devices
1612
services.udev.extraRules = ''
1713
# Misc

modules/hardware/x86_64-generic/x86_64-linux.nix

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ in
2323
hardware.enableAllFirmware = true;
2424

2525
boot = {
26-
# Enable normal Linux console on the display
27-
kernelParams = [ "console=tty0" ];
26+
# Enable normal Linux console on the display, and QR code kernel panic
27+
kernelParams = [
28+
"console=tty0"
29+
"drm.panic_screen=qr_code"
30+
];
2831

2932
# To enable installation of ghaf into NVMe drives
3033
initrd.availableKernelModules = [
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors
22
# SPDX-License-Identifier: Apache-2.0
33
{
4-
config,
54
pkgs,
5+
config,
66
lib,
77
...
88
}:
99
let
10-
cfg = config.ghaf.partitioning.disko;
11-
1210
postBootCmds = pkgs.writeShellApplication {
1311
name = "postBootScript";
1412
runtimeInputs = with pkgs; [
@@ -47,9 +45,13 @@ let
4745
parted -s -a opt "$PARENT_DISK" "resizepart $PARTNUM 100%"
4846
'';
4947
};
48+
49+
enable =
50+
((builtins.hasAttr "verity" config.ghaf.partitioning) && config.ghaf.partitioning.verity.enable)
51+
|| ((builtins.hasAttr "disko" config.ghaf.partitioning) && config.ghaf.partitioning.disko.enable);
5052
in
5153
{
52-
config = lib.mkIf cfg.enable {
54+
config = lib.mkIf enable {
5355
# To debug postBootCommands, one may run
5456
# journalctl -u initrd-nixos-activation.service
5557
# inside the running Ghaf host.

modules/partitioning/disko-debug-partition.nix

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let
2323
in
2424
{
2525
options.ghaf.partitioning.disko = {
26-
enable = lib.mkEnableOption "Enable the disko partitioning scheme";
26+
enable = lib.mkEnableOption "the disko partitioning scheme";
2727

2828
imageBuilder.compression = lib.mkOption {
2929
type = lib.types.enum [
@@ -36,6 +36,7 @@ in
3636
};
3737

3838
config = lib.mkIf cfg.enable {
39+
system.build.ghafImage = config.system.build.diskoImages;
3940
disko = {
4041
imageBuilder = {
4142
extraPostVM = lib.mkIf (cfg.imageBuilder.compression == "zstd") ''
@@ -44,71 +45,73 @@ in
4445
'';
4546
};
4647
devices = {
47-
disk.disk1 = {
48-
type = "disk";
49-
imageSize = "60G";
50-
content = {
51-
type = "gpt";
52-
partitions = {
53-
boot = {
54-
name = "boot";
55-
size = "1M";
56-
type = "EF02";
57-
priority = 1; # Needs to be first partition
58-
};
59-
esp = {
60-
name = "ESP";
61-
size = "500M";
62-
type = "EF00";
63-
content = {
64-
type = "filesystem";
65-
format = "vfat";
66-
mountpoint = "/boot";
67-
mountOptions = [
68-
"umask=0077"
69-
"nofail"
70-
];
48+
disk = {
49+
disk1 = {
50+
type = "disk";
51+
imageSize = "60G";
52+
content = {
53+
type = "gpt";
54+
partitions = {
55+
boot = {
56+
name = "boot";
57+
size = "1M";
58+
type = "EF02";
59+
priority = 1; # Needs to be first partition
7160
};
72-
priority = 2;
73-
};
74-
swap = {
75-
size = "12G";
76-
type = "8200";
77-
content = {
78-
type = "swap";
79-
resumeDevice = true; # resume from hiberation from this device
80-
randomEncryption = true;
61+
esp = {
62+
name = "ESP";
63+
size = "500M";
64+
type = "EF00";
65+
content = {
66+
type = "filesystem";
67+
format = "vfat";
68+
mountpoint = "/boot";
69+
mountOptions = [
70+
"umask=0077"
71+
"nofail"
72+
];
73+
};
74+
priority = 2;
8175
};
82-
priority = 3;
83-
};
84-
root = {
85-
size = "40G";
86-
content = {
87-
type = "filesystem";
88-
format = "ext4";
89-
mountpoint = "/";
90-
mountOptions = [
91-
"noatime"
92-
"nodiratime"
93-
];
76+
swap = {
77+
size = "12G";
78+
type = "8200";
79+
content = {
80+
type = "swap";
81+
resumeDevice = true; # resume from hiberation from this device
82+
randomEncryption = true;
83+
};
84+
priority = 3;
9485
};
95-
priority = 4;
96-
};
97-
persist = {
98-
size = "100%";
99-
content = {
100-
type = "filesystem";
101-
format = "btrfs";
102-
mountpoint = "/persist";
103-
mountOptions = [
104-
"noatime"
105-
"nodiratime"
106-
];
86+
root = {
87+
size = "40G";
88+
content = {
89+
type = "filesystem";
90+
format = "ext4";
91+
mountpoint = "/";
92+
mountOptions = [
93+
"noatime"
94+
"nodiratime"
95+
];
96+
};
97+
priority = 4;
98+
};
99+
persist = {
100+
size = "100%";
101+
content = {
102+
type = "filesystem";
103+
format = "btrfs";
104+
mountpoint = "/persist";
105+
mountOptions = [
106+
"noatime"
107+
"nodiratime"
108+
];
109+
};
107110
};
108111
};
109112
};
110113
};
111-
};
114+
} // (if (builtins.hasAttr "disks" cfg) then cfg.disks else { });
112115
};
113116
};
114117
};

modules/partitioning/flake-module.nix

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
disko-debug-partition.imports = [
77
inputs.disko.nixosModules.disko
88
./disko-debug-partition.nix
9-
./disko-postboot.nix
9+
./btrfs-postboot.nix
10+
];
11+
verity-release-partition.imports = [
12+
./verity-partition.nix
13+
./verity-repart.nix
14+
./verity-sysupdate.nix
15+
./btrfs-postboot.nix
1016
];
1117
};
1218
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Copyright 2025 TII (SSRC) and the Ghaf contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
{
4+
config,
5+
lib,
6+
pkgs,
7+
modulesPath,
8+
...
9+
}:
10+
11+
let
12+
roothashPlaceholder = "61fe0f0c98eff2a595dd2f63a5e481a0a25387261fa9e34c37e3a4910edf32b8";
13+
cfg = config.ghaf.partitioning.verity;
14+
debugEnable = config.ghaf.profiles.debug.enable;
15+
in
16+
{
17+
options.ghaf.partitioning.verity = {
18+
enable = lib.mkEnableOption "the verity (image-based) partitioning scheme";
19+
20+
split = lib.mkOption {
21+
description = "Whether to split the partitions to separate files instead of a single image";
22+
type = lib.types.bool;
23+
default = false;
24+
};
25+
};
26+
27+
imports = [
28+
"${modulesPath}/image/repart.nix"
29+
"${modulesPath}/system/boot/uki.nix"
30+
];
31+
32+
config = lib.mkIf cfg.enable {
33+
34+
system.build.ghafImage = config.system.build.image.overrideAttrs (oldAttrs: {
35+
nativeBuildInputs = oldAttrs.nativeBuildInputs ++ [ pkgs.jq ];
36+
postInstall = ''
37+
# Extract the roothash from the JSON
38+
repartRoothash="$(
39+
${lib.getExe pkgs.jq} -r \
40+
'[.[] | select(.roothash != null)] | .[0].roothash' \
41+
"$out/repart-output.json"
42+
)"
43+
44+
# Replace the placeholder with the real roothash in the target .raw file
45+
sed -i \
46+
"0,/${roothashPlaceholder}/ s/${roothashPlaceholder}/$repartRoothash/" \
47+
"$out/${oldAttrs.pname}_${oldAttrs.version}.raw"
48+
49+
# Compress the image
50+
${pkgs.zstd}/bin/zstd --compress $out/*raw
51+
rm $out/*raw
52+
'';
53+
});
54+
55+
image.repart.split = cfg.split;
56+
57+
systemd.enableEmergencyMode = debugEnable;
58+
59+
boot = {
60+
kernelParams = [
61+
"roothash=${roothashPlaceholder}"
62+
"systemd.verity_root_options=panic-on-corruption"
63+
] ++ lib.optional debugEnable "systemd.setenv=SYSTEMD_SULOGIN_FORCE=1";
64+
65+
# No bootloaders needed yet
66+
loader = {
67+
grub.enable = false;
68+
systemd-boot.enable = lib.mkForce false;
69+
};
70+
71+
# Enable dm-verity and compress initrd
72+
initrd = {
73+
systemd = {
74+
enable = true;
75+
dmVerity.enable = true;
76+
emergencyAccess = debugEnable;
77+
};
78+
79+
compressor = "zstd";
80+
compressorArgs = [ "-6" ];
81+
82+
supportedFilesystems = {
83+
btrfs = true;
84+
erofs = true;
85+
};
86+
};
87+
};
88+
89+
environment.systemPackages = with pkgs; [
90+
cryptsetup
91+
];
92+
93+
# System is now immutable
94+
system.switch.enable = false;
95+
96+
fileSystems =
97+
let
98+
tmpfsConfig = {
99+
neededForBoot = true;
100+
fsType = "tmpfs";
101+
};
102+
in
103+
{
104+
"/" = {
105+
fsType = "erofs";
106+
# for systemd-remount-fs
107+
options = [ "ro" ];
108+
device = "/dev/mapper/root";
109+
};
110+
111+
"/persist" =
112+
let
113+
partConf = config.image.repart.partitions."50-persist".repartConfig;
114+
in
115+
{
116+
device = "/dev/disk/by-partuuid/${partConf.UUID}";
117+
fsType = partConf.Format;
118+
};
119+
}
120+
// builtins.listToAttrs (
121+
builtins.map
122+
(path: {
123+
name = path;
124+
value = tmpfsConfig;
125+
})
126+
[
127+
"/bin" # /bin/sh symlink needs to be created
128+
"/etc"
129+
"/home"
130+
"/root"
131+
"/tmp"
132+
"/usr" # /usr/bin/env symlink needs to be created
133+
"/var"
134+
]
135+
);
136+
};
137+
}

0 commit comments

Comments
 (0)