feat: introduce guests as nested-container definitions
Add a third auto-loaded kind beside the host and the module: the guest, a reusable definition under guests/ that a host enables like a module and that realizes its interior as a systemd-nspawn nested container. Split the shared base config so a guest can stand on it. base.nix now holds the substrate both bases share — the primary user, home-manager, and the unstable/stable overlays. system.nix keeps the host-only machinery, and a new guest.nix is the slim guest-base: it imports the full modules tree, pins the interior release, and auto-enables the toolkit bundle and SSH so any guest is workable on sight. Give modules.ssh a guest flavor. A host restores its host keys from secrets as before, while a guest sets hostKeys.restore = false, names no sops files, and self-generates a host key, so it holds no age key of its own. The lib grows a guest helper that declares the guests.<path> namespace with an enable and a backend field. Only the container backend is built; microvm is a reserved value that trips a clear build-time assertion rather than silently building nothing. A sample guest exercises the whole path, and neogaia enables it, so the guest interior builds through the existing nix flake check seam.
This commit was merged in pull request #28.
This commit is contained in:
165
system.nix
165
system.nix
@@ -2,137 +2,74 @@
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
# Shared base config carried by every host.
|
||||
# The host base: the host-only machinery a physical machine needs on top of the
|
||||
# shared base — bootloader, secret decryption, and the maintenance timers.
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
user = config.user;
|
||||
|
||||
passwordSecret = "${user.name}-password";
|
||||
|
||||
# Args to instantiate an extra nixpkgs source on the base platform.
|
||||
pinArgs = prev: {
|
||||
inherit (prev.stdenv.hostPlatform) system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.user = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "alexion";
|
||||
description = ''
|
||||
The primary interactive user this host is built for. Drives both the
|
||||
system account and the home-manager user in lockstep.
|
||||
'';
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Alexion";
|
||||
description = "Human-readable description (GECOS field) for the primary user.";
|
||||
};
|
||||
};
|
||||
imports = [ ./base.nix ];
|
||||
|
||||
config = {
|
||||
# Reach fresher packages with `unstable.<name>` or pin with `stable.<name>`.
|
||||
# chaotic's overlay is added by its own module, not here.
|
||||
nixpkgs.overlays = [
|
||||
(_final: prev: {
|
||||
unstable = import inputs.nixpkgs-unstable (pinArgs prev);
|
||||
stable = import inputs.nixpkgs-stable (pinArgs prev);
|
||||
})
|
||||
];
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
# chaotic's binary cache, so the CachyOS kernel is fetched rather than compiled.
|
||||
# The `extra-` prefix keeps cache.nixos.org alongside it.
|
||||
nix.settings.extra-substituters = [ "https://nyx-cache.chaotic.cx/" ];
|
||||
nix.settings.extra-trusted-public-keys = [
|
||||
"nyx-cache.chaotic.cx:dJxTrgMC3V3cFfyIiBQDQorG6k1LsqurH/srpMSq7qk="
|
||||
];
|
||||
|
||||
# Flakes, so `nixos-rebuild switch` works from the console.
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
# A month of generations is kept, because on a rolling channel with a
|
||||
# third-party kernel an old generation is a known-good system to boot when
|
||||
# an update breaks something.
|
||||
nix.gc.automatic = true;
|
||||
nix.gc.dates = "Mon 03:15";
|
||||
nix.gc.options = "--delete-older-than 30d";
|
||||
|
||||
# chaotic's binary cache, so the CachyOS kernel is fetched rather than compiled.
|
||||
# The `extra-` prefix keeps cache.nixos.org alongside it.
|
||||
nix.settings.extra-substituters = [ "https://nyx-cache.chaotic.cx/" ];
|
||||
nix.settings.extra-trusted-public-keys = [
|
||||
"nyx-cache.chaotic.cx:dJxTrgMC3V3cFfyIiBQDQorG6k1LsqurH/srpMSq7qk="
|
||||
];
|
||||
# Deduplication runs on a timer, off the rebuild path, so it never adds
|
||||
# latency to a `nixos-rebuild switch`.
|
||||
# It falls on a different day from collection, so the two never contend.
|
||||
nix.optimise.automatic = true;
|
||||
nix.optimise.dates = [ "Thu 03:45" ];
|
||||
|
||||
# A month of generations is kept, because on a rolling channel with a
|
||||
# third-party kernel an old generation is a known-good system to boot when
|
||||
# an update breaks something.
|
||||
nix.gc.automatic = true;
|
||||
nix.gc.dates = "Mon 03:15";
|
||||
nix.gc.options = "--delete-older-than 30d";
|
||||
# The EFI system partition holds a kernel and an initrd per entry at roughly
|
||||
# 70 MiB apiece, and is fixed in size.
|
||||
# An exhausted one fails at bootloader installation, after the build has
|
||||
# already succeeded.
|
||||
boot.loader.systemd-boot.configurationLimit = 15;
|
||||
|
||||
# Deduplication runs on a timer, off the rebuild path, so it never adds
|
||||
# latency to a `nixos-rebuild switch`.
|
||||
# It falls on a different day from collection, so the two never contend.
|
||||
nix.optimise.automatic = true;
|
||||
nix.optimise.dates = [ "Thu 03:45" ];
|
||||
environment.systemPackages = [ pkgs.git ];
|
||||
|
||||
# The EFI system partition holds a kernel and an initrd per entry at roughly
|
||||
# 70 MiB apiece, and is fixed in size.
|
||||
# An exhausted one fails at bootloader installation, after the build has
|
||||
# already succeeded.
|
||||
boot.loader.systemd-boot.configurationLimit = 15;
|
||||
# Caps Lock is a second Escape.
|
||||
# Shift+Caps Lock still toggles Caps Lock.
|
||||
services.xserver.xkb.layout = "us";
|
||||
services.xserver.xkb.options = "caps:escape_shifted_capslock";
|
||||
|
||||
environment.systemPackages = [ pkgs.git ];
|
||||
# Compile the console keymap from the layout above, so the remap holds on a
|
||||
# bare TTY and not only under a graphical session.
|
||||
console.useXkbConfig = true;
|
||||
|
||||
# Caps Lock is a second Escape.
|
||||
# Shift+Caps Lock still toggles Caps Lock.
|
||||
services.xserver.xkb.layout = "us";
|
||||
services.xserver.xkb.options = "caps:escape_shifted_capslock";
|
||||
# Decryption machinery every host depends on.
|
||||
# The identity sits on the encrypted root, which is mounted early enough to
|
||||
# satisfy the secret below.
|
||||
# Clearing both `sshKeyPaths` defaults keeps the SSH host keys out of the
|
||||
# decryption path.
|
||||
sops.defaultSopsFile = ./secrets/shared.yaml;
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.sshKeyPaths = [ ];
|
||||
sops.gnupg.sshKeyPaths = [ ];
|
||||
|
||||
# Compile the console keymap from the layout above, so the remap holds on a
|
||||
# bare TTY and not only under a graphical session.
|
||||
console.useXkbConfig = true;
|
||||
# A password set by hand on a running machine otherwise takes precedence.
|
||||
# That leaves the declared `hashedPasswordFile` below silently inert.
|
||||
# Root has no declared password and is therefore locked.
|
||||
# `sudo` from the wheel group is the way in.
|
||||
users.mutableUsers = false;
|
||||
|
||||
# Decryption machinery every host depends on.
|
||||
# The identity sits on the encrypted root, which is mounted early enough to
|
||||
# satisfy the secret below.
|
||||
# Clearing both `sshKeyPaths` defaults keeps the SSH host keys out of the
|
||||
# decryption path.
|
||||
sops.defaultSopsFile = ./secrets/shared.yaml;
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.sshKeyPaths = [ ];
|
||||
sops.gnupg.sshKeyPaths = [ ];
|
||||
# Decrypted in an earlier activation stage than ordinary secrets.
|
||||
# That is early enough to precede the account that reads it.
|
||||
sops.secrets.${passwordSecret}.neededForUsers = true;
|
||||
|
||||
# A password set by hand on a running machine otherwise takes precedence.
|
||||
# That leaves the declared `hashedPasswordFile` below silently inert.
|
||||
# Root has no declared password and is therefore locked.
|
||||
# `sudo` from the wheel group is the way in.
|
||||
users.mutableUsers = false;
|
||||
|
||||
# Decrypted in an earlier activation stage than ordinary secrets.
|
||||
# That is early enough to precede the account that reads it.
|
||||
sops.secrets.${passwordSecret}.neededForUsers = true;
|
||||
|
||||
# Primary user.
|
||||
# The wheel group is the way in, since root is locked.
|
||||
users.users.${user.name} = {
|
||||
isNormalUser = true;
|
||||
description = user.description;
|
||||
extraGroups = [ "wheel" ];
|
||||
hashedPasswordFile = config.sops.secrets.${passwordSecret}.path;
|
||||
};
|
||||
|
||||
# home-manager as a NixOS module: one `nixos-rebuild switch` builds the
|
||||
# system and user environment together, sharing the system's pkgs and
|
||||
# installing user packages into the system profile.
|
||||
home-manager = {
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
extraSpecialArgs = {
|
||||
inherit inputs;
|
||||
my = inputs.self.lib;
|
||||
};
|
||||
users.${user.name} = {
|
||||
home.username = user.name;
|
||||
home.homeDirectory = "/home/${user.name}";
|
||||
home.stateVersion = "26.05";
|
||||
};
|
||||
};
|
||||
};
|
||||
users.users.${user.name}.hashedPasswordFile = config.sops.secrets.${passwordSecret}.path;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user