feat: desktop host with /home on a second disk
Adds hosts/desktop: 512G system disk, 1T disk for /home, each in its own LUKS container. modules/disk.nix gains moonarch.disk.home, which moves the /home subvolume to the second disk and drops it from the first. New modules/user.nix declares moonarch.user without a default, so the login name exists once per host instead of once per reference. Both hosts set an initialPassword: swaylock authenticates through pam_unix and cannot unlock a screen for an account without one.
This commit is contained in:
1 parent
ec985a8f7f
commit
eb9b5fd3d1
7 files changed
+267
-57
No files matched your search
+84
-29
@@ -8,12 +8,15 @@ let
|
||||
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
|
||||
subvolumes = {
|
||||
systemSubvolumes = {
|
||||
"/root" = { mountpoint = "/"; inherit mountOptions; };
|
||||
"/home" = { mountpoint = "/home"; inherit mountOptions; };
|
||||
# Separate so the store stays out of the snapper snapshots of /. It is
|
||||
# reproducible from the flake and would only inflate them.
|
||||
"/nix" = { mountpoint = "/nix"; inherit mountOptions; };
|
||||
} // lib.optionalAttrs (!cfg.home.enable) {
|
||||
# Only here when there is no second disk. With one, /home lives over there
|
||||
# and the main disk carries the system alone.
|
||||
"/home" = { mountpoint = "/home"; inherit mountOptions; };
|
||||
} // lib.optionalAttrs (cfg.swapSize != "") {
|
||||
# No compression or noatime here: disko creates the swapfile inside this
|
||||
# subvolume, and btrfs requires it to be nodatacow, which it sets itself.
|
||||
@@ -23,7 +26,11 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
btrfs = {
|
||||
homeSubvolumes = {
|
||||
"/home" = { mountpoint = "/home"; inherit mountOptions; };
|
||||
};
|
||||
|
||||
btrfs = subvolumes: {
|
||||
type = "btrfs";
|
||||
# Overwrite an existing signature instead of asking. The device is erased
|
||||
# by this point either way.
|
||||
@@ -34,14 +41,24 @@ let
|
||||
# Neither passwordFile nor settings.keyFile is set, which makes disko's
|
||||
# askPassword default to true: it prompts for the passphrase twice while
|
||||
# formatting and never puts it on a command line.
|
||||
luks = {
|
||||
#
|
||||
# With a second disk each container is asked for separately while formatting.
|
||||
# Give both the same passphrase: systemd stage 1 caches the first one and
|
||||
# tries it on the second container, so the boot prompts once. If it ever asks
|
||||
# twice, boot.initrd.systemd.enable = false falls back to the script initrd,
|
||||
# whose boot.initrd.luks.reusePassphrases does the same thing explicitly.
|
||||
luks = name: content: {
|
||||
type = "luks";
|
||||
name = "crypted";
|
||||
inherit name;
|
||||
# Lets TRIM reach the SSD through the container. The tradeoff is that the
|
||||
# pattern of used blocks becomes visible on the raw device.
|
||||
settings.allowDiscards = true;
|
||||
content = btrfs;
|
||||
inherit content;
|
||||
};
|
||||
|
||||
# Wraps a filesystem in LUKS unless the host opted out of encryption.
|
||||
maybeEncrypted = name: subvolumes:
|
||||
if cfg.encrypt then luks name (btrfs subvolumes) else btrfs subvolumes;
|
||||
in
|
||||
{
|
||||
options.moonarch.disk = {
|
||||
@@ -63,6 +80,26 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
home = {
|
||||
enable = lib.mkEnableOption "a second disk carrying /home";
|
||||
|
||||
device = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "/dev/disk/by-id/nvme-eui.fedcba9876543210";
|
||||
description = ''
|
||||
Second whole disk, erased and partitioned like the first one. It takes
|
||||
the /home subvolume, which then no longer exists on the main disk.
|
||||
|
||||
Encryption follows moonarch.disk.encrypt, so both disks are either
|
||||
encrypted or neither is. The container is named crypted-home to keep
|
||||
it apart from the system one.
|
||||
|
||||
disko-install addresses it as `--disk home <path>`, the same way the
|
||||
main disk is `--disk main <path>`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
encrypt = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
@@ -97,31 +134,49 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
disko.devices.disk.main = {
|
||||
type = "disk";
|
||||
device = cfg.device;
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = cfg.espSize;
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
disko.devices.disk.main = {
|
||||
type = "disk";
|
||||
device = cfg.device;
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = cfg.espSize;
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
# Named the same whether or not it is encrypted, so the partlabel the
|
||||
# fileSystems entries point at does not depend on cfg.encrypt.
|
||||
root = {
|
||||
size = "100%";
|
||||
content = maybeEncrypted "crypted" systemSubvolumes;
|
||||
};
|
||||
};
|
||||
# Named the same whether or not it is encrypted, so the partlabel the
|
||||
# fileSystems entries point at does not depend on cfg.encrypt.
|
||||
root = {
|
||||
size = "100%";
|
||||
content = if cfg.encrypt then luks else btrfs;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# Second disk. No ESP on it: the firmware boots from the main disk, and a
|
||||
# second one would only be another thing to keep in sync.
|
||||
(lib.mkIf cfg.home.enable {
|
||||
disko.devices.disk.home = {
|
||||
type = "disk";
|
||||
device = cfg.home.device;
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions.home = {
|
||||
size = "100%";
|
||||
content = maybeEncrypted "crypted-home" homeSubvolumes;
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# ABOUTME: Declares the moonarch.user option; every host sets its own login name.
|
||||
# ABOUTME: Autologin, group membership and snapper read moonarch.user from here.
|
||||
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options.moonarch.user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "moon";
|
||||
description = ''
|
||||
Login name of the primary account, and therefore the directory name under
|
||||
/home. Autologin, the group memberships and snapper's ALLOW_USERS all read
|
||||
this, so the name is declared once instead of once per reference.
|
||||
|
||||
There is no default: the name belongs to the machine, so every host sets
|
||||
it. Changing it on an installed system does not rename the home directory —
|
||||
that stays behind under the old name.
|
||||
'';
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user