diff --git a/modules/common/hardware/definition.nix b/modules/common/hardware/definition.nix index 2e99fdfb5..db9a09f1a 100644 --- a/modules/common/hardware/definition.nix +++ b/modules/common/hardware/definition.nix @@ -84,11 +84,18 @@ Path to the disk ''; }; + options.imageSize = mkOption { + type = types.str; + description = '' + Size of the image + ''; + }; }); default = {}; example = literalExpression '' { disk1.device = "/dev/nvme0n1"; + disk1.imageSize = "100G" } ''; }; diff --git a/modules/common/hardware/lenovo-x1/definitions/default.nix b/modules/common/hardware/lenovo-x1/definitions/default.nix index 8b28c75aa..474c89f40 100644 --- a/modules/common/hardware/lenovo-x1/definitions/default.nix +++ b/modules/common/hardware/lenovo-x1/definitions/default.nix @@ -9,10 +9,17 @@ in { inherit (hwDefinition) mouse; inherit (hwDefinition) touchpad; - inherit (hwDefinition) disks; inherit (hwDefinition) network; inherit (hwDefinition) gpu; + disks = { + disk1.device = "/dev/nvme0n1"; + # 250G is the size of the 1st LVM pool, 10G is reserved for the second LVM pool + # Second LVM pool can be extended safely to the end of the disk, and its restricted size + # makes flashing quicker. + disk1.imageSize = "260G"; + }; + # Notes: # 1. This assembles udev rules for different hw configurations (i.e., different mice/touchpads) by adding # all of them to the configuration. This was chosen for simplicity to not have to provide hw identifier at build, diff --git a/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix b/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix index 8cfc01b10..253e63e55 100644 --- a/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix +++ b/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix @@ -7,10 +7,6 @@ mouse = ["ELAN067B:00 04F3:31F8 Mouse" "SYNA8016:00 06CB:CEB3 Mouse"]; touchpad = ["ELAN067B:00 04F3:31F8 Touchpad" "SYNA8016:00 06CB:CEB3 Touchpad"]; - disks = { - disk1.device = "/dev/nvme0n1"; - }; - network.pciDevices = [ { # Passthrough Intel WiFi card diff --git a/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix b/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix index 72818febd..c8a1ff294 100644 --- a/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix +++ b/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix @@ -16,10 +16,6 @@ "ELAN067B:00 04F3:31F8 Touchpad" ]; - disks = { - disk1.device = "/dev/nvme0n1"; - }; - network.pciDevices = [ { # Passthrough Intel WiFi card diff --git a/modules/disko/disko-ab-partitions.nix b/modules/disko/disko-ab-partitions.nix new file mode 100644 index 000000000..05483c3b8 --- /dev/null +++ b/modules/disko/disko-ab-partitions.nix @@ -0,0 +1,181 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# !!! To utilize this partition scheme, the disk size must be >252G !!! +# +# This partition scheme contains three common partitions and two LVM pools. +# First LVM pool occupies 250G and second one occupies the rest of the disk space. +# Some paritions are duplicated for the future AB SWupdate implementation. +# +# First three partitions are related to the boot process: +# - boot : Bootloader partition +# - ESP-A : (500M) Kernel and initrd +# - ESP-B : (500M) +# +# First LVM pool contains next partitions: +# - root-A : (50G) Root FS +# - root-B : (50G) +# - vm-storage-A : (30G) Possible standalone pre-built VM images are stored here +# - vm-storage-B : (30G) +# - reserved-A : (10G) Reserved partition, no use +# - reserved-B : (10G) +# - gp-storage : (50G) General purpose storage for some common insecure cases +# - recovery : (rest of the LVM pool) Recovery factory image is stored here +# +# Second LVM pool is dedicated for Storage VM completely. +_: { + disko.memSize = 2048; + disko.devices = { + disk.disk1 = { + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + name = "boot"; + size = "1M"; + type = "EF02"; + }; + esp_a = { + name = "ESP_A"; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ + "umask=0077" + "nofail" + ]; + }; + }; + esp_b = { + name = "ESP_B"; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountOptions = [ + "umask=0077" + "nofail" + ]; + }; + }; + other_1 = { + name = "lvm_pv_1"; + size = "250G"; + content = { + type = "lvm_pv"; + vg = "pool"; + }; + }; + # LVM pool that is going to be passed to the Storage VM + other_2 = { + name = "lvm_pv_2"; + size = "100%"; + content = { + type = "lvm_pv"; + vg = "vmstore"; + }; + }; + }; + }; + }; + lvm_vg = { + pool = { + type = "lvm_vg"; + lvs = { + root_a = { + size = "50G"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + vm_storage_a = { + size = "30G"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/vmstore"; + mountOptions = [ + "defaults" + "nofail" + "noatime" + ]; + }; + }; + reserved_a = { + size = "10G"; + }; + root_b = { + size = "50G"; + content = { + type = "filesystem"; + format = "ext4"; + mountOptions = [ + "defaults" + "noatime" + ]; + }; + }; + vm_storage_b = { + size = "30G"; + content = { + type = "filesystem"; + format = "ext4"; + mountOptions = [ + "defaults" + "nofail" + "noatime" + ]; + }; + }; + reserved_b = { + size = "10G"; + }; + gp_storage = { + size = "50G"; + content = { + type = "filesystem"; + format = "ext4"; + mountOptions = [ + "defaults" + "nofail" + "noatime" + ]; + }; + }; + recovery = { + size = "100%FREE"; + }; + }; + }; + vmstore = { + # Dedicated partition for StorageVM + type = "lvm_vg"; + lvs = { + storagevm = { + size = "100%FREE"; + content = { + type = "filesystem"; + format = "ext4"; + mountOptions = [ + "defaults" + "nofail" + "noatime" + ]; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/disko/disko-basic-postboot.nix b/modules/disko/disko-basic-postboot.nix index c0b48f731..28ff777c8 100644 --- a/modules/disko/disko-basic-postboot.nix +++ b/modules/disko/disko-basic-postboot.nix @@ -1,6 +1,39 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{pkgs, ...}: let +{ + pkgs, + config, + lib, + ... +}: let + cfg = config.ghaf.disk.encryption; + encryptionCmds = + if cfg.enable + then '' + partitions_to_encrypt=("/dev/mapper/pool-vm_storage_a" "/dev/mapper/pool-vm_storage_b") + readarray -t pd_partitions <<<"$(${pkgs.util-linux}/bin/lsblk -no PATH,FSTYPE,UUID $PARENT_DISK)" + + # Find out the partition which needs to be encrypted in a loop + for pp in "''${pd_partitions[@]}" + do + P_DEVPATH=$(echo "$pp" | ${pkgs.gawk}/bin/awk '{print $1}') + P_FSTYPE=$(echo "$pp" | ${pkgs.gawk}/bin/awk '{print $2}') + P_DEVUUID=$(echo "$pp" | ${pkgs.gawk}/bin/awk '{print $3}') + if (${pkgs.coreutils}/bin/printf '%s\n' "''${partitions_to_encrypt[@]}" | ${pkgs.gnugrep}/bin/grep -q -x "$P_DEVPATH"); then + FOUND=1 + else + FOUND=0 + fi + [[ "$FOUND" != 0 && "$P_FSTYPE" == "ext4" ]] && { + echo -n "ghaf" | ${pkgs.cryptsetup}/bin/cryptsetup luksFormat -q $P_DEVPATH + echo -n "ghaf" | ${pkgs.cryptsetup}/bin/cryptsetup open $P_DEVPATH crypt-$P_DEVUUID + ${pkgs.e2fsprogs}/bin/mkfs.ext4 /dev/mapper/crypt-$P_DEVUUID + PASSWORD=ghaf ${pkgs.systemd}/bin/systemd-cryptenroll --fido2-device=auto --fido2-with-client-pin=yes $P_DEVPATH + } + done + '' + else ""; + postBootCmds = '' set -xeuo pipefail @@ -38,6 +71,9 @@ } done + # Currently with new disk partitioning scheme resizing won't have impact, so encrypting here + ${encryptionCmds} + # If boot device was not found, exit at this point, but still return 0, so it # won't stop device from booting. [[ "$FOUND_PARENT" != 1 ]] && echo "Did not find boot device" && exit 0 @@ -59,4 +95,8 @@ ''; in { boot.postBootCommands = postBootCmds; + boot.initrd.luks.devices = lib.mkIf cfg.enable { + "crypt-pool-vm_storage_b".device = "/dev/mapper/pool-vm_storage_b"; + "crypt-pool-vm_storage_a".device = "/dev/mapper/pool-vm_storage_a"; + }; } diff --git a/modules/disko/flake-module.nix b/modules/disko/flake-module.nix index 34353f96a..859162bae 100644 --- a/modules/disko/flake-module.nix +++ b/modules/disko/flake-module.nix @@ -9,5 +9,12 @@ ./lenovo-x1-disko-basic.nix ./disko-basic-postboot.nix ]; + + disko-ab-partitions-v1.imports = [ + inputs.disko.nixosModules.disko + ./disko-ab-partitions.nix + ./lenovo-x1-disk-encryption.nix + ./disko-basic-postboot.nix + ]; }; } diff --git a/modules/disko/lenovo-x1-disk-encryption.nix b/modules/disko/lenovo-x1-disk-encryption.nix new file mode 100644 index 000000000..a80144b24 --- /dev/null +++ b/modules/disko/lenovo-x1-disk-encryption.nix @@ -0,0 +1,10 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{lib, ...}: +with lib; { + options.ghaf.disk.encryption.enable = mkOption { + description = "Enable Ghaf disk encryption"; + type = types.bool; + default = false; + }; +} diff --git a/targets/lenovo-x1/everything.nix b/targets/lenovo-x1/everything.nix index 7ac6890e4..4bfecf0eb 100644 --- a/targets/lenovo-x1/everything.nix +++ b/targets/lenovo-x1/everything.nix @@ -33,7 +33,7 @@ self.nixosModules.lanzaboote self.nixosModules.microvm - self.nixosModules.disko-lenovo-x1-basic-v1 + self.nixosModules.disko-ab-partitions-v1 ./sshkeys.nix ({