From 21b6bec0ff14f55c37a655529d6ad2e81f1c4212 Mon Sep 17 00:00:00 2001 From: Mikaela Allan Date: Wed, 3 May 2023 16:11:45 -0400 Subject: [PATCH] nixos/config/swap: improve randomEncrytion * add sector size parameter to swap randomEncryption * add key size parameter to swap randomEncryption * allow deviceName to be overridden for encrypted swap * create test for swap random encryption * update release notes --- .../manual/release-notes/rl-2305.section.md | 20 +++++ nixos/modules/config/swap.nix | 39 ++++++++- nixos/tests/all-tests.nix | 1 + nixos/tests/swap-random-encryption.nix | 80 +++++++++++++++++++ 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 nixos/tests/swap-random-encryption.nix diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index 51d8136e203a..25d60402cb97 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -363,6 +363,26 @@ In addition to numerous new and upgraded packages, this release has the followin - `nextcloud` has an option to enable SSE-C in S3. +- NixOS swap partitions with random encryption can now control the sector size, cipher, and key size used to setup the plain encryption device over the + underlying block device rather than allowing them to be determined by `cryptsetup(8)`. One can use these features like so: + + ```nix + { + swapDevices = [ + { + device = "/dev/disk/by-partlabel/swapspace"; + + randomEncryption = { + enable = true; + cipher = "aes-xts-plain64"; + keySize = 512; + sectorSize = 4096; + }; + } + ]; + } + ``` + - `services.peertube` now requires you to specify the secret file `secrets.secretsFile`. It can be generated by running `openssl rand -hex 32`. Before upgrading, read the release notes for PeerTube: - [Release v5.0.0](https://github.com/Chocobozzz/PeerTube/releases/tag/v5.0.0) diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix index 2c9c4c9c1a2d..1b63741e86c4 100644 --- a/nixos/modules/config/swap.nix +++ b/nixos/modules/config/swap.nix @@ -38,6 +38,34 @@ let ''; }; + keySize = mkOption { + default = null; + example = "512"; + type = types.nullOr types.int; + description = lib.mdDoc '' + Set the encryption key size for the plain device. + + If not specified, the amount of data to read from `source` will be + determined by cryptsetup. + + See `cryptsetup-open(8)` for details. + ''; + }; + + sectorSize = mkOption { + default = null; + example = "4096"; + type = types.nullOr types.int; + description = lib.mdDoc '' + Set the sector size for the plain encrypted device type. + + If not specified, the default sector size is determined from the + underlying block device. + + See `cryptsetup-open(8)` for details. + ''; + }; + source = mkOption { default = "/dev/urandom"; example = "/dev/random"; @@ -157,11 +185,11 @@ let }; - config = rec { + config = { device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}"; deviceName = lib.replaceStrings ["\\"] [""] (escapeSystemdPath config.device); - realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device; + realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device; }; }; @@ -247,7 +275,12 @@ in ''} ${optionalString sw.randomEncryption.enable '' cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \ - ${optionalString sw.randomEncryption.allowDiscards "--allow-discards"} ${sw.device} ${sw.deviceName} + '' + concatLines (filter (s: s != "") [ + (optionalString (sw.randomEncryption.sectorSize != null) "--sector-size=${toString sw.randomEncryption.sectorSize} \\") + (optionalString (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize} \\") + (optionalString sw.randomEncryption.allowDiscards "--allow-discards \\") + ]) + '' + ${sw.device} ${sw.deviceName} mkswap ${sw.realDevice} ''} ''; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 9acb2dc7a45b..28ab2d46d67f 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -667,6 +667,7 @@ in { sudo = handleTest ./sudo.nix {}; swap-file-btrfs = handleTest ./swap-file-btrfs.nix {}; swap-partition = handleTest ./swap-partition.nix {}; + swap-random-encryption = handleTest ./swap-random-encryption.nix {}; sway = handleTest ./sway.nix {}; switchTest = handleTest ./switch-test.nix {}; sympa = handleTest ./sympa.nix {}; diff --git a/nixos/tests/swap-random-encryption.nix b/nixos/tests/swap-random-encryption.nix new file mode 100644 index 000000000000..9e919db65dde --- /dev/null +++ b/nixos/tests/swap-random-encryption.nix @@ -0,0 +1,80 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: +{ + name = "swap-random-encryption"; + + nodes.machine = + { config, pkgs, lib, ... }: + { + environment.systemPackages = [ pkgs.cryptsetup ]; + + virtualisation.useDefaultFilesystems = false; + + virtualisation.rootDevice = "/dev/vda1"; + + boot.initrd.postDeviceCommands = '' + if ! test -b /dev/vda1; then + ${pkgs.parted}/bin/parted --script /dev/vda -- mklabel msdos + ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary 1MiB -250MiB + ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary -250MiB 100% + sync + fi + + FSTYPE=$(blkid -o value -s TYPE /dev/vda1 || true) + if test -z "$FSTYPE"; then + ${pkgs.e2fsprogs}/bin/mke2fs -t ext4 -L root /dev/vda1 + fi + ''; + + virtualisation.fileSystems = { + "/" = { + device = "/dev/disk/by-label/root"; + fsType = "ext4"; + }; + }; + + swapDevices = [ + { + device = "/dev/vda2"; + + randomEncryption = { + enable = true; + cipher = "aes-xts-plain64"; + keySize = 512; + sectorSize = 4096; + }; + } + ]; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + with subtest("Swap is active"): + # Doesn't matter if the numbers reported by `free` are slightly off due to unit conversions. + machine.succeed("free -h | grep -E 'Swap:\s+2[45][0-9]Mi'") + + with subtest("Swap device has 4k sector size"): + import json + result = json.loads(machine.succeed("lsblk -Jo PHY-SEC,LOG-SEC /dev/mapper/dev-vda2")) + block_devices = result["blockdevices"] + if len(block_devices) != 1: + raise Exception ("lsblk output did not report exactly one block device") + + swapDevice = block_devices[0]; + if not (swapDevice["phy-sec"] == 4096 and swapDevice["log-sec"] == 4096): + raise Exception ("swap device does not have the sector size specified in the configuration") + + with subtest("Swap encrypt has assigned cipher and keysize"): + import re + + results = machine.succeed("cryptsetup status dev-vda2").splitlines() + + cipher_pattern = re.compile(r"\s*cipher:\s+aes-xts-plain64\s*") + if not any(cipher_pattern.fullmatch(line) for line in results): + raise Exception ("swap device encryption does not use the cipher specified in the configuration") + + key_size_pattern = re.compile(r"\s*keysize:\s+512\s+bits\s*") + if not any(key_size_pattern.fullmatch(line) for line in results): + raise Exception ("swap device encryption does not use the key size specified in the configuration") + ''; +})