refactor: make bootstrap read config from flake

This commit is contained in:
Swarsel 2024-12-24 16:01:33 +01:00
parent 5637ab54fc
commit ae63e40f04
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
16 changed files with 481 additions and 428 deletions

View file

@ -7,7 +7,7 @@ keys:
- &swarsel 4BE7925262289B476DBBC17B76FD3810215AE097 - &swarsel 4BE7925262289B476DBBC17B76FD3810215AE097
- &hosts - &hosts
- &winters age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - &winters age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
- &toto age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - &toto age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
- &surface age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - &surface age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
- &nbl age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy - &nbl age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy
- &sync age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h - &sync age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h

View file

@ -982,7 +982,10 @@ The interesting part is in the start:
systemFunc = func; systemFunc = func;
in in
systemFunc { systemFunc {
specialArgs = { inherit inputs outputs self; }; specialArgs = {
inherit inputs outputs self;
lib = lib.extend (_: _: { swarselsystems = import ./lib { inherit lib; }; });
};
modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ]; modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ];
}; };
}; };
@ -1202,6 +1205,9 @@ My work machine. Built for more security, this is the gold standard of my config
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
@ -1279,19 +1285,17 @@ My work machine. Built for more security, this is the gold standard of my config
''; '';
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate {
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
hasBluetooth = true; hasBluetooth = true;
hasFingerprint = true; hasFingerprint = true;
impermanence = false; isImpermanence = false;
isBtrfs = true;
isCrypted = true; isCrypted = true;
}; } sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate {
isLaptop = true; isLaptop = true;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
cpuCount = 16; cpuCount = 16;
# temperatureHwmon = { # temperatureHwmon = {
@ -1416,7 +1420,7 @@ My work machine. Built for more security, this is the gold standard of my config
ans = ". ~/.venvs/ansible/bin/activate"; ans = ". ~/.venvs/ansible/bin/activate";
ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate"; ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate";
}; };
}; } sharedOptions;
} }
@ -1478,7 +1482,7 @@ This is my main server that I run at home. It handles most tasks that require bi
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
server = { server = {
@ -1713,7 +1717,7 @@ This machine mainly acts as an external sync helper. It manages the following th
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/root/.dotfiles"; flakePath = "/root/.dotfiles";
server = { server = {
@ -1735,21 +1739,15 @@ This is a slim setup for developing base configuration.
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
imports = [ imports = [
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
"${self}/hosts/nixos/toto/disk-config.nix" "${self}/hosts/nixos/toto/disk-config.nix"
{
_module.args = {
withSwap = true;
swapSize = "8";
rootDisk = "/dev/vda";
withImpermanence = true;
withEncryption = true;
};
}
./hardware-configuration.nix ./hardware-configuration.nix
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
@ -1810,20 +1808,21 @@ This is a slim setup for developing base configuration.
firewall.enable = false; firewall.enable = false;
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate {
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
impermanence = true; isImpermanence = true;
isBtrfs = true;
isCrypted = true; isCrypted = true;
initialSetup = true; initialSetup = true;
}; isSwap = true;
swapSize = "8G";
rootDisk = "/dev/vda";
} sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate {
isLaptop = false; isLaptop = false;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
}; } sharedOptions;
} }
@ -2800,8 +2799,6 @@ This program sets up a new NixOS host.
echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home." echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home."
echo " Default='${target_user}'." echo " Default='${target_user}'."
echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}." echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}."
echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path."
echo " --encryption Use this flag if the target machine has full disk encryption enabled."
echo " --debug Enable debug mode." echo " --debug Enable debug mode."
echo " -h | --help Print this help." echo " -h | --help Print this help."
exit 0 exit 0
@ -2856,14 +2853,14 @@ This program sets up a new NixOS host.
SOPS_FILE=".sops.yaml" SOPS_FILE=".sops.yaml"
sed -i "{ sed -i "{
# Remove any * and & entries for this host # Remove any * and & entries for this host
/[*&]$key_name/ d; /[*&]$key_name/ d;
# Inject a new age: entry # Inject a new age: entry
# n matches the first line following age: and p prints it, then we transform it while reusing the spacing # n matches the first line following age: and p prints it, then we transform it while reusing the spacing
/age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; /age:/{n; p; s/\(.*- \*\).*/\1$key_name/};
# Inject a new hosts or user: entry # Inject a new hosts or user: entry
/&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/}
}" $SOPS_FILE }" $SOPS_FILE
green "Updating .sops.yaml" green "Updating .sops.yaml"
cd - cd -
} }
@ -2886,16 +2883,6 @@ This program sets up a new NixOS host.
shift shift
ssh_port=$1 ssh_port=$1
;; ;;
--temp-override)
shift
temp=$1
;;
--impermanence)
persist_dir="/persist"
;;
--encryption)
disk_encryption=1
;;
--debug) --debug)
set -x set -x
;; ;;
@ -2908,6 +2895,37 @@ This program sets up a new NixOS host.
shift shift
done done
green "~SwarselSystems~ remote installer"
green "Reading system information for $target_hostname ..."
DISK="$(nix eval --raw ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.rootDisk)"
green "Root Disk: $DISK"
CRYPTED="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isCrypted)"
if [[ $CRYPTED == "true" ]]; then
green "Encryption: ✓"
disk_encryption=1
else
red "Encryption: X"
disk_encryption=0
fi
IMPERMANENCE="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isImpermanence)"
if [[ $IMPERMANENCE == "true" ]]; then
green "Impermanence: ✓"
persist_dir="/persist"
else
red "Impermanence: X"
persist_dir=""
fi
SWAP="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isSwap)"
if [[ $SWAP == "true" ]]; then
green "Swap: ✓"
else
red "Swap: X"
fi
ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination" ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination"
# ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value # ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value
ssh_root_cmd=${ssh_cmd/${target_user}@/root@} ssh_root_cmd=${ssh_cmd/${target_user}@/root@}
@ -3067,6 +3085,10 @@ This program sets up a new NixOS host.
fi fi
#+end_src #+end_src
#+RESULTS:
| trap: | undefined | signal: | exit | | | | |
| [ | Babel | evaluation | exited | with | code | 1 | ] |
#+begin_src nix :tangle pkgs/bootstrap/default.nix #+begin_src nix :tangle pkgs/bootstrap/default.nix
@ -3533,23 +3555,22 @@ Note: The structure of generating the packages was changed in commit =2cf03a3 re
Modules that need to be loaded on the NixOS level. Note that these will not be available on systems that are not running NixOS. Modules that need to be loaded on the NixOS level. Note that these will not be available on systems that are not running NixOS.
#+begin_src nix :tangle modules/nixos/default.nix #+begin_src nix :tangle modules/nixos/default.nix
let let
moduleNames = [ moduleNames = [
"wallpaper" "wallpaper"
"hardware" "hardware"
"setup" "setup"
"impermanence" "server"
"filesystem" "input"
"input" ];
];
mkImports = names: builtins.listToAttrs (map (name: { mkImports = names: builtins.listToAttrs (map (name: {
inherit name; inherit name;
value = import ./${name}.nix; value = import ./${name}.nix;
}) names); }) names);
in in
mkImports moduleNames mkImports moduleNames
#+end_src #+end_src
@ -3603,22 +3624,45 @@ I usually use =mutableUsers = false= in my NixOS configuration. However, on a ne
#+begin_src nix :tangle modules/nixos/setup.nix #+begin_src nix :tangle modules/nixos/setup.nix
{ lib, ... }: { lib, ... }:
let
inherit (lib) mkOption types;
in
{ {
options.swarselsystems.flakePath = mkOption { options.swarselsystems.user = lib.mkOption {
type = types.str; type = lib.types.str;
default = "swarsel";
};
options.swarselsystems.flakePath = lib.mkOption {
type = lib.types.str;
default = ""; default = "";
}; };
options.swarselsystems.withHomeManager = mkOption { options.swarselsystems.withHomeManager = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
}; };
options.swarselsystems.isSwap = lib.mkOption {
type = lib.types.bool;
default = true;
};
options.swarselsystems.swapSize = lib.mkOption {
type = lib.types.str;
default = "8G";
};
options.swarselsystems.rootDisk = lib.mkOption {
type = lib.types.str;
default = "";
};
options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption"; options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption";
options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)"; options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)";
options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)"; options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)";
options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
options.swarselsystems.isImpermanence = lib.mkEnableOption "use impermanence on this system";
}
#+end_src
***** Server
#+begin_src nix :tangle modules/nixos/server.nix
{ lib, ... }:
{
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine"; options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server"; options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server"; options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
@ -3661,36 +3705,6 @@ This section is for everything input-related on the NixOS side. At the moment, t
} }
#+end_src #+end_src
***** Impermanence
:PROPERTIES:
:CUSTOM_ID: h:e591075d-4a77-4add-bbc8-b711998fa97f
:END:
Option to enable impermanence configurations. This could also be done via optional imports, but impermanence is a "big enough" change to warrant a line in the machine =default.nix=.
#+begin_src nix :tangle modules/nixos/impermanence.nix
{ lib, ... }:
{
options.swarselsystems.impermanence = lib.mkEnableOption "use impermanence on this system";
}
#+end_src
***** Filesystem
:PROPERTIES:
:CUSTOM_ID: h:f77358ee-a80c-403a-be9d-04e7052bc556
:END:
This lets me quickly set flags for "special" file systems. These options mostly function in conjunction with other settings (for example, the =isBtrfs= function is mostly used for impermanence configuration).
#+begin_src nix :tangle modules/nixos/filesystem.nix
{ lib, ... }:
{
options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
}
#+end_src
**** home-manager **** home-manager
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: h:ced5841f-c088-4d88-b3a1-7d62aad8837b :CUSTOM_ID: h:ced5841f-c088-4d88-b3a1-7d62aad8837b
@ -5541,16 +5555,12 @@ Normally, doing that also resets the lecture that happens on the first use of =s
{ config, lib, ... }: { config, lib, ... }:
let let
mkIfElse = p: yes: no: if p then yes else no; mkIfElse = p: yes: no: if p then yes else no;
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos";
in in
{ {
security.sudo.extraConfig = lib.mkIf config.swarselsystems.impermanence '' security.sudo.extraConfig = lib.mkIf config.swarselsystems.isImpermanence ''
# rollback results in sudo lectures after each reboot # rollback results in sudo lectures after each reboot
Defaults lecture = never Defaults lecture = never
''; '';
@ -5561,12 +5571,12 @@ Normally, doing that also resets the lecture that happens on the first use of =s
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;
boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.impermanence { boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.isImpermanence {
description = "Rollback BTRFS root subvolume to a pristine state"; description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ]; wantedBy = [ "initrd.target" ];
# make sure it's done after encryption # make sure it's done after encryption
# i.e. LUKS/TPM process # i.e. LUKS/TPM process
after = mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ]; after = lib.swarselsystems.mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ];
requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ]; requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ];
# mount the root fs before clearing # mount the root fs before clearing
before = [ "sysroot.mount" ]; before = [ "sysroot.mount" ];
@ -5609,7 +5619,7 @@ Normally, doing that also resets the lecture that happens on the first use of =s
}; };
environment.persistence."/persist" = lib.mkIf config.swarselsystems.impermanence { environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
hideMounts = true; hideMounts = true;
directories = directories =
[ [

View file

@ -151,7 +151,10 @@
systemFunc = func; systemFunc = func;
in in
systemFunc { systemFunc {
specialArgs = { inherit inputs outputs self; }; specialArgs = {
inherit inputs outputs self;
lib = lib.extend (_: _: { swarselsystems = import ./lib { inherit lib; }; });
};
modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ]; modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ];
}; };
}; };

View file

@ -1,6 +1,9 @@
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
@ -78,142 +81,144 @@ in
''; '';
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate
wallpaper = self + /wallpaper/lenovowp.png; {
hasBluetooth = true; wallpaper = self + /wallpaper/lenovowp.png;
hasFingerprint = true; hasBluetooth = true;
impermanence = false; hasFingerprint = true;
isBtrfs = true; isImpermanence = false;
isCrypted = true; isCrypted = true;
}; }
sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate
isLaptop = true; {
isNixos = true; isLaptop = true;
isBtrfs = true; isNixos = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
cpuCount = 16; cpuCount = 16;
# temperatureHwmon = { # temperatureHwmon = {
# isAbsolutePath = true; # isAbsolutePath = true;
# path = "/sys/devices/platform/thinkpad_hwmon/hwmon/"; # path = "/sys/devices/platform/thinkpad_hwmon/hwmon/";
# input-filename = "temp1_input"; # input-filename = "temp1_input";
# }; # };
# ------ ----- # ------ -----
# | DP-4 | |eDP-1| # | DP-4 | |eDP-1|
# ------ ----- # ------ -----
startup = [ startup = [
{ command = "nextcloud --background"; } { command = "nextcloud --background"; }
{ command = "vesktop --start-minimized --enable-speech-dispatcher --ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime"; } { command = "vesktop --start-minimized --enable-speech-dispatcher --ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime"; }
{ command = "element-desktop --hidden --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu-driver-bug-workarounds"; } { command = "element-desktop --hidden --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu-driver-bug-workarounds"; }
{ command = "ANKI_WAYLAND=1 anki"; } { command = "ANKI_WAYLAND=1 anki"; }
{ command = "OBSIDIAN_USE_WAYLAND=1 obsidian"; } { command = "OBSIDIAN_USE_WAYLAND=1 obsidian"; }
{ command = "nm-applet"; } { command = "nm-applet"; }
{ command = "teams-for-linux"; } { command = "teams-for-linux"; }
{ command = "1password"; } { command = "1password"; }
{ command = "feishin"; } { command = "feishin"; }
]; ];
sharescreen = "eDP-2"; sharescreen = "eDP-2";
lowResolution = "1280x800"; lowResolution = "1280x800";
highResolution = "2560x1600"; highResolution = "2560x1600";
monitors = { monitors = {
main = { main = {
name = "BOE 0x0BC9 Unknown"; name = "BOE 0x0BC9 Unknown";
mode = "2560x1600"; # TEMPLATE mode = "2560x1600"; # TEMPLATE
scale = "1"; scale = "1";
position = "2560,0"; position = "2560,0";
workspace = "15:L"; workspace = "15:L";
output = "eDP-2"; output = "eDP-2";
};
homedesktop = {
name = "Philips Consumer Electronics Company PHL BDM3270 AU11806002320";
mode = "2560x1440";
scale = "1";
position = "0,0";
workspace = "1:";
output = "DP-11";
};
work_back_middle = {
name = "LG Electronics LG Ultra HD 0x000305A6";
mode = "2560x1440";
scale = "1";
position = "5120,0";
workspace = "1:";
output = "DP-10";
};
work_front_left = {
name = "LG Electronics LG Ultra HD 0x0007AB45";
mode = "3840x2160";
scale = "1";
position = "5120,0";
workspace = "1:";
output = "DP-7";
};
work_back_right = {
name = "HP Inc. HP Z32 CN41212T55";
mode = "3840x2160";
scale = "1";
position = "5120,0";
workspace = "1:";
output = "DP-3";
};
work_middle_middle_main = {
name = "HP Inc. HP 732pk CNC4080YL5";
mode = "3840x2160";
scale = "1";
position = "-1280,0";
workspace = "11:M";
output = "DP-8";
};
work_middle_middle_side = {
name = "Hewlett Packard HP Z24i CN44250RDT";
mode = "1920x1200";
transform = "270";
scale = "1";
position = "-2480,0";
workspace = "12:S";
output = "DP-9";
};
work_seminary = {
name = "Applied Creative Technology Transmitter QUATTRO201811";
mode = "1280x720";
scale = "1";
position = "10000,10000"; # i.e. this screen is inaccessible by moving the mouse
workspace = "12:S";
output = "DP-4";
};
}; };
homedesktop = { inputs = {
name = "Philips Consumer Electronics Company PHL BDM3270 AU11806002320"; "12972:18:Framework_Laptop_16_Keyboard_Module_-_ANSI_Keyboard" = {
mode = "2560x1440"; xkb_layout = "us";
scale = "1"; xkb_variant = "altgr-intl";
position = "0,0"; };
workspace = "1:"; "1133:45081:MX_Master_2S_Keyboard" = {
output = "DP-11"; xkb_layout = "us";
xkb_variant = "altgr-intl";
};
"2362:628:PIXA3854:00_093A:0274_Touchpad" = {
dwt = "enabled";
tap = "enabled";
natural_scroll = "enabled";
middle_emulation = "enabled";
};
"1133:50504:Logitech_USB_Receiver" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
"1133:45944:MX_KEYS_S" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
}; };
work_back_middle = { keybindings = {
name = "LG Electronics LG Ultra HD 0x000305A6"; "Mod4+Ctrl+Shift+p" = "exec screenshare";
mode = "2560x1440";
scale = "1";
position = "5120,0";
workspace = "1:";
output = "DP-10";
}; };
work_front_left = { shellAliases = {
name = "LG Electronics LG Ultra HD 0x0007AB45"; ans2-15_3-9 = ". ~/.venvs/ansible39_2_15_0/bin/activate";
mode = "3840x2160"; ans3-9 = ". ~/.venvs/ansible39/bin/activate";
scale = "1"; ans = ". ~/.venvs/ansible/bin/activate";
position = "5120,0"; ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate";
workspace = "1:";
output = "DP-7";
}; };
work_back_right = { }
name = "HP Inc. HP Z32 CN41212T55"; sharedOptions;
mode = "3840x2160";
scale = "1";
position = "5120,0";
workspace = "1:";
output = "DP-3";
};
work_middle_middle_main = {
name = "HP Inc. HP 732pk CNC4080YL5";
mode = "3840x2160";
scale = "1";
position = "-1280,0";
workspace = "11:M";
output = "DP-8";
};
work_middle_middle_side = {
name = "Hewlett Packard HP Z24i CN44250RDT";
mode = "1920x1200";
transform = "270";
scale = "1";
position = "-2480,0";
workspace = "12:S";
output = "DP-9";
};
work_seminary = {
name = "Applied Creative Technology Transmitter QUATTRO201811";
mode = "1280x720";
scale = "1";
position = "10000,10000"; # i.e. this screen is inaccessible by moving the mouse
workspace = "12:S";
output = "DP-4";
};
};
inputs = {
"12972:18:Framework_Laptop_16_Keyboard_Module_-_ANSI_Keyboard" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
"1133:45081:MX_Master_2S_Keyboard" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
"2362:628:PIXA3854:00_093A:0274_Touchpad" = {
dwt = "enabled";
tap = "enabled";
natural_scroll = "enabled";
middle_emulation = "enabled";
};
"1133:50504:Logitech_USB_Receiver" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
"1133:45944:MX_KEYS_S" = {
xkb_layout = "us";
xkb_variant = "altgr-intl";
};
};
keybindings = {
"Mod4+Ctrl+Shift+p" = "exec screenshare";
};
shellAliases = {
ans2-15_3-9 = ". ~/.venvs/ansible39_2_15_0/bin/activate";
ans3-9 = ". ~/.venvs/ansible39/bin/activate";
ans = ". ~/.venvs/ansible/bin/activate";
ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate";
};
};
} }

View file

@ -79,7 +79,7 @@ in
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/root/.dotfiles"; flakePath = "/root/.dotfiles";
server = { server = {

View file

@ -1,21 +1,15 @@
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
imports = [ imports = [
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
"${self}/hosts/nixos/toto/disk-config.nix" "${self}/hosts/nixos/toto/disk-config.nix"
{
_module.args = {
withSwap = true;
swapSize = "8";
rootDisk = "/dev/vda";
withImpermanence = true;
withEncryption = true;
};
}
./hardware-configuration.nix ./hardware-configuration.nix
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
@ -76,19 +70,24 @@ in
firewall.enable = false; firewall.enable = false;
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate
wallpaper = self + /wallpaper/lenovowp.png; {
impermanence = true; wallpaper = self + /wallpaper/lenovowp.png;
isBtrfs = true; isImpermanence = true;
isCrypted = true; isCrypted = true;
initialSetup = true; initialSetup = true;
}; isSwap = true;
swapSize = "8G";
rootDisk = "/dev/vda";
}
sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate
isLaptop = false; {
isNixos = true; isLaptop = false;
isBtrfs = true; isNixos = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
}; }
sharedOptions;
} }

View file

@ -1,11 +1,8 @@
# NOTE: ... is needed because dikso passes diskoFile # NOTE: ... is needed because dikso passes diskoFile
{ lib { lib
, pkgs , pkgs
, config
, rootDisk , rootDisk
, swapSize ? "8"
, withSwap ? true
, withEncryption ? true
, withImpermanence ? true
, ... , ...
}: }:
let let
@ -20,7 +17,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/home" = lib.mkIf withImpermanence { "/home" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/home"; mountpoint = "/home";
mountOptions = [ mountOptions = [
"subvol=home" "subvol=home"
@ -28,7 +25,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/persist" = lib.mkIf withImpermanence { "/persist" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/persist"; mountpoint = "/persist";
mountOptions = [ mountOptions = [
"subvol=persist" "subvol=persist"
@ -36,7 +33,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/log" = lib.mkIf withImpermanence { "/log" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/var/log"; mountpoint = "/var/log";
mountOptions = [ mountOptions = [
"subvol=log" "subvol=log"
@ -52,9 +49,9 @@ let
"noatime" "noatime"
]; ];
}; };
"/swap" = lib.mkIf withSwap { "/swap" = lib.mkIf config.swarselsystems.isSwap {
mountpoint = "/.swapvol"; mountpoint = "/.swapvol";
swap.swapfile.size = "${swapSize}G"; swap.swapfile.size = config.swarselsystems.swapSize;
}; };
}; };
in in
@ -63,7 +60,7 @@ in
disk = { disk = {
disk0 = { disk0 = {
type = "disk"; type = "disk";
device = rootDisk; device = config.swarselsystems.rootDisk;
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
@ -79,11 +76,11 @@ in
mountOptions = [ "defaults" ]; mountOptions = [ "defaults" ];
}; };
}; };
root = lib.mkIf (!withEncryption) { root = lib.mkIf (!config.swarselsystems.isCrypted) {
size = "100%"; size = "100%";
content = { content = {
inherit type subvolumes extraArgs; inherit type subvolumes extraArgs;
postCreateHook = lib.mkIf withImpermanence '' postCreateHook = lib.mkIf config.swarselsystems.isImpermanence ''
MNTPOINT=$(mktemp -d) MNTPOINT=$(mktemp -d)
mount "/dev/disk/by-label/nixos" "$MNTPOINT" -o subvolid=5 mount "/dev/disk/by-label/nixos" "$MNTPOINT" -o subvolid=5
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
@ -91,7 +88,7 @@ in
''; '';
}; };
}; };
luks = lib.mkIf withEncryption { luks = lib.mkIf config.swarselsystems.isCrypted {
size = "100%"; size = "100%";
content = { content = {
type = "luks"; type = "luks";
@ -107,7 +104,7 @@ in
}; };
content = { content = {
inherit type subvolumes extraArgs; inherit type subvolumes extraArgs;
postCreateHook = lib.mkIf withImpermanence '' postCreateHook = lib.mkIf config.swarselsystems.isImpermanence ''
MNTPOINT=$(mktemp -d) MNTPOINT=$(mktemp -d)
mount "/dev/mapper/cryptroot" "$MNTPOINT" -o subvolid=5 mount "/dev/mapper/cryptroot" "$MNTPOINT" -o subvolid=5
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
@ -122,8 +119,8 @@ in
}; };
}; };
fileSystems."/persist".neededForBoot = lib.mkIf withImpermanence true; fileSystems."/persist".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
fileSystems."/home".neededForBoot = lib.mkIf withImpermanence true; fileSystems."/home".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
environment.systemPackages = [ environment.systemPackages = [
pkgs.yubikey-manager pkgs.yubikey-manager

View file

@ -46,7 +46,7 @@ in
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
server = { server = {

7
lib/default.nix Normal file
View file

@ -0,0 +1,7 @@
{ lib, ... }:
{
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
}

View file

@ -3,8 +3,7 @@ let
"wallpaper" "wallpaper"
"hardware" "hardware"
"setup" "setup"
"impermanence" "server"
"filesystem"
"input" "input"
]; ];

22
modules/nixos/server.nix Normal file
View file

@ -0,0 +1,22 @@
{ lib, ... }:
{
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
options.swarselsystems.server.navidrome = lib.mkEnableOption "enable navidrome on server";
options.swarselsystems.server.spotifyd = lib.mkEnableOption "enable spotifyd on server";
options.swarselsystems.server.mpd = lib.mkEnableOption "enable mpd on server";
options.swarselsystems.server.matrix = lib.mkEnableOption "enable matrix on server";
options.swarselsystems.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
options.swarselsystems.server.immich = lib.mkEnableOption "enable immich on server";
options.swarselsystems.server.paperless = lib.mkEnableOption "enable paperless on server";
options.swarselsystems.server.transmission = lib.mkEnableOption "enable transmission and friends on server";
options.swarselsystems.server.syncthing = lib.mkEnableOption "enable syncthing on server";
options.swarselsystems.server.restic = lib.mkEnableOption "enable restic backups on server";
options.swarselsystems.server.monitoring = lib.mkEnableOption "enable monitoring on server";
options.swarselsystems.server.jenkins = lib.mkEnableOption "enable jenkins on server";
options.swarselsystems.server.emacs = lib.mkEnableOption "enable emacs server on server";
options.swarselsystems.server.forgejo = lib.mkEnableOption "enable forgejo on server";
options.swarselsystems.server.ankisync = lib.mkEnableOption "enable ankisync on server";
options.swarselsystems.server.freshrss = lib.mkEnableOption "enable freshrss on server";
}

View file

@ -1,37 +1,33 @@
{ lib, ... }: { lib, ... }:
let
inherit (lib) mkOption types;
in
{ {
options.swarselsystems.flakePath = mkOption { options.swarselsystems.user = lib.mkOption {
type = types.str; type = lib.types.str;
default = "swarsel";
};
options.swarselsystems.flakePath = lib.mkOption {
type = lib.types.str;
default = ""; default = "";
}; };
options.swarselsystems.withHomeManager = mkOption { options.swarselsystems.withHomeManager = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
}; };
options.swarselsystems.isSwap = lib.mkOption {
type = lib.types.bool;
default = true;
};
options.swarselsystems.swapSize = lib.mkOption {
type = lib.types.str;
default = "8G";
};
options.swarselsystems.rootDisk = lib.mkOption {
type = lib.types.str;
default = "";
};
options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption"; options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption";
options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)"; options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)";
options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)"; options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)";
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server"; options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server"; options.swarselsystems.isImpermanence = lib.mkEnableOption "use impermanence on this system";
options.swarselsystems.server.navidrome = lib.mkEnableOption "enable navidrome on server";
options.swarselsystems.server.spotifyd = lib.mkEnableOption "enable spotifyd on server";
options.swarselsystems.server.mpd = lib.mkEnableOption "enable mpd on server";
options.swarselsystems.server.matrix = lib.mkEnableOption "enable matrix on server";
options.swarselsystems.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
options.swarselsystems.server.immich = lib.mkEnableOption "enable immich on server";
options.swarselsystems.server.paperless = lib.mkEnableOption "enable paperless on server";
options.swarselsystems.server.transmission = lib.mkEnableOption "enable transmission and friends on server";
options.swarselsystems.server.syncthing = lib.mkEnableOption "enable syncthing on server";
options.swarselsystems.server.restic = lib.mkEnableOption "enable restic backups on server";
options.swarselsystems.server.monitoring = lib.mkEnableOption "enable monitoring on server";
options.swarselsystems.server.jenkins = lib.mkEnableOption "enable jenkins on server";
options.swarselsystems.server.emacs = lib.mkEnableOption "enable emacs server on server";
options.swarselsystems.server.forgejo = lib.mkEnableOption "enable forgejo on server";
options.swarselsystems.server.ankisync = lib.mkEnableOption "enable ankisync on server";
options.swarselsystems.server.freshrss = lib.mkEnableOption "enable freshrss on server";
} }

View file

@ -1,16 +1,12 @@
{ config, lib, ... }: { config, lib, ... }:
let let
mkIfElse = p: yes: no: if p then yes else no; mkIfElse = p: yes: no: if p then yes else no;
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos";
in in
{ {
security.sudo.extraConfig = lib.mkIf config.swarselsystems.impermanence '' security.sudo.extraConfig = lib.mkIf config.swarselsystems.isImpermanence ''
# rollback results in sudo lectures after each reboot # rollback results in sudo lectures after each reboot
Defaults lecture = never Defaults lecture = never
''; '';
@ -21,12 +17,12 @@ in
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;
boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.impermanence { boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.isImpermanence {
description = "Rollback BTRFS root subvolume to a pristine state"; description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ]; wantedBy = [ "initrd.target" ];
# make sure it's done after encryption # make sure it's done after encryption
# i.e. LUKS/TPM process # i.e. LUKS/TPM process
after = mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ]; after = lib.swarselsystems.mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ];
requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ]; requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ];
# mount the root fs before clearing # mount the root fs before clearing
before = [ "sysroot.mount" ]; before = [ "sysroot.mount" ];
@ -69,7 +65,7 @@ in
}; };
environment.persistence."/persist" = lib.mkIf config.swarselsystems.impermanence { environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
hideMounts = true; hideMounts = true;
directories = directories =
[ [

View file

@ -24,8 +24,6 @@ function help_and_exit() {
echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home." echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home."
echo " Default='${target_user}'." echo " Default='${target_user}'."
echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}." echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}."
echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path."
echo " --encryption Use this flag if the target machine has full disk encryption enabled."
echo " --debug Enable debug mode." echo " --debug Enable debug mode."
echo " -h | --help Print this help." echo " -h | --help Print this help."
exit 0 exit 0
@ -80,14 +78,14 @@ function update_sops_file() {
SOPS_FILE=".sops.yaml" SOPS_FILE=".sops.yaml"
sed -i "{ sed -i "{
# Remove any * and & entries for this host # Remove any * and & entries for this host
/[*&]$key_name/ d; /[*&]$key_name/ d;
# Inject a new age: entry # Inject a new age: entry
# n matches the first line following age: and p prints it, then we transform it while reusing the spacing # n matches the first line following age: and p prints it, then we transform it while reusing the spacing
/age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; /age:/{n; p; s/\(.*- \*\).*/\1$key_name/};
# Inject a new hosts or user: entry # Inject a new hosts or user: entry
/&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/}
}" $SOPS_FILE }" $SOPS_FILE
green "Updating .sops.yaml" green "Updating .sops.yaml"
cd - cd -
} }
@ -110,16 +108,6 @@ while [[ $# -gt 0 ]]; do
shift shift
ssh_port=$1 ssh_port=$1
;; ;;
--temp-override)
shift
temp=$1
;;
--impermanence)
persist_dir="/persist"
;;
--encryption)
disk_encryption=1
;;
--debug) --debug)
set -x set -x
;; ;;
@ -132,6 +120,37 @@ while [[ $# -gt 0 ]]; do
shift shift
done done
green "~SwarselSystems~ remote installer"
green "Reading system information for $target_hostname ..."
DISK="$(nix eval --raw ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.rootDisk)"
green "Root Disk: $DISK"
CRYPTED="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isCrypted)"
if [[ $CRYPTED == "true" ]]; then
green "Encryption: ✓"
disk_encryption=1
else
red "Encryption: X"
disk_encryption=0
fi
IMPERMANENCE="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isImpermanence)"
if [[ $IMPERMANENCE == "true" ]]; then
green "Impermanence: ✓"
persist_dir="/persist"
else
red "Impermanence: X"
persist_dir=""
fi
SWAP="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isSwap)"
if [[ $SWAP == "true" ]]; then
green "Swap: ✓"
else
red "Swap: X"
fi
ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination" ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination"
# ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value # ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value
ssh_root_cmd=${ssh_cmd/${target_user}@/root@} ssh_root_cmd=${ssh_cmd/${target_user}@/root@}

View file

@ -9,62 +9,62 @@ sops:
- recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpd0JYN2tiNW1IRHJtcXlX YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpRko1ZnZsMUM2L1VmNGQ4
SEQvOUVZYjJzb1ZVamdpbG91OXZpMkhWTkQ4CjVsTlp3eTR0OENhanh4clR5OXJX MnhOZTZmdlBLQ2dra0xMY3E0NmZPQUpuU1Q4CjNDSDNnVGNCV05aUWpWRnZkWEt2
NnU2QVdVbXVRTXRJWlgwSGFkdEMyWjAKLS0tIG5NQzFqREIrWmZBUGZSVVU5RWVE Y1FlbEdTZ290SjRKc1hKRzN0bUNwVDAKLS0tIHFjVEk0S045NGgzMUFDUitIdEx4
VzlnVmhzc2U3d3B4aEs2a0dCaHJQbHMKpDLehdlCXhnEKOjzFr7rliJtETSsyUDp dlRRWUtiYldYQ3V0QzFyRHNIblNFNm8KkPXunwFKo/4klAZhkAXikg7UpuFC1EP/
HixMSMosuN3xs7+7nodPBHsX7Z9kvbwV60inqzHUWsq4SzHrqQozbg== kf6roOcQx41hPSqAWzivySwPgRUO3iygFw4jonYaFZJik/wIo9OulA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVcTIwWVl5VHlnUUFuMkky YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYUmhXcS8rWmNQUGVudGxP
N1BBNnNBVkJVKzZqeXRiVVJ2OUNoUjIraWtrClE2NDJYTng3S2dHL29wcm5NRkxY STZPaVB3VlFHYXhDbUZYdjhENFFOL1FCUEZVClJ2cFhwNlRpVnAzRThQZ1FuNHNi
L1NZY21xd09FUDN0QTdsTEVpZjNoWXMKLS0tIFlTWFNEcmwwNnRuYmhCcTVUZXAx T3pyWm5aNDVaUnJHcjYzWVVOUHBwWUEKLS0tIFdKSXF5TGw2aDJ3NGdhZ0RGWGpu
cW04RVNxeDlkcDZmT3JadUNDQUdXRlEKnXWWqgObJStUF0DdWKX2M71UYk/6n4wX MjBYTktmeE9EcDNad0VKV1dhME9UbVkKYrDIQ8/DfeJ/3ITfw9/51i2N44hqnIi3
XdmPH0crXBelfwmSvX1+Pkwj+JowzXmMaVR0Y0XC2SMXdzWShhk40w== 4hHKaefSiHU0glUHUdYhg/F17Hgh5MuhZ0LB4dfIYngWNgFDwnC4aQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcUZGcTBmR2ZHRW0rcVRj YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNWFNvaE5VQms2VFNOK25D
cCt1Wi9JNWJpbEtNVmVwWmJBN2VLM05SVlRRCkdOMVhFTXd3QUJFWGZyTys0NXgv NTdocVF5QTMwSDEreE02cWVVaHIyQ2dqcVRnClB4QzAzTkRVb1hSL1VjYW5IOEIr
Y1MwL2pWNmZiUGdCTnpDR1RqOHQ2UDgKLS0tIFFrUDg5eFpWcXpNOERWZzJTa1ph SG9adlpacGd1ck8vTU1IOTNyS2Y4cjAKLS0tIDQxT3lOUjhpQ1FNNGZKbnpXOVJG
SlQxWE5VWlFHVlY0VE1zTjFUa054bG8KjXhLyIPnL/nGAR9l2d+J2s29ZxjzH5+U aS9qY0crYXpsSFRxT280VzllTE1kdWcKoaATszZ1H4b05vpEzQXkffJuwQESbTyW
BtavBDsS1/Ldsu4OQsY7TFJrTdOzMMRsNVWedprFyJjx0o8FO9AwWw== nBE5WYRqUHBFWeve1Ssq3AaYpNEht+MAYA0YlA3TsJc5scSbkFXi4g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2024-07-16T23:56:59Z" lastmodified: "2024-07-16T23:56:59Z"
mac: ENC[AES256_GCM,data:bo8SHGmkNGQqR8nnlIKvAMzd+4vWJ19u9Kga2U1mOEYKMCyZ2nTXju6e327ppmx6KJUnzzieS7F8myE/5jzfd1+LyAN7QlL1xixtyLZH784Eh3c3Rd3sXKO/Tuj00gSsz8PsXzq8VK5RdR6NggxhMM6l3Mji4mTQibEzFQ0XPwo=,iv:6mAVBuMwxkO/ms0O/lpLEAg9lzVtZywMbwhL7diB4Z4=,tag:oGnwY5Ikc8qOrwNyiWqtGg==,type:str] mac: ENC[AES256_GCM,data:bo8SHGmkNGQqR8nnlIKvAMzd+4vWJ19u9Kga2U1mOEYKMCyZ2nTXju6e327ppmx6KJUnzzieS7F8myE/5jzfd1+LyAN7QlL1xixtyLZH784Eh3c3Rd3sXKO/Tuj00gSsz8PsXzq8VK5RdR6NggxhMM6l3Mji4mTQibEzFQ0XPwo=,iv:6mAVBuMwxkO/ms0O/lpLEAg9lzVtZywMbwhL7diB4Z4=,tag:oGnwY5Ikc8qOrwNyiWqtGg==,type:str]
pgp: pgp:
- created_at: "2024-12-24T11:28:02Z" - created_at: "2024-12-24T14:59:45Z"
enc: |- enc: |-
-----BEGIN PGP MESSAGE----- -----BEGIN PGP MESSAGE-----
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
=HWX2 =8uFY
-----END PGP MESSAGE----- -----END PGP MESSAGE-----
fp: 4BE7925262289B476DBBC17B76FD3810215AE097 fp: 4BE7925262289B476DBBC17B76FD3810215AE097
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted

View file

@ -34,71 +34,71 @@ sops:
- recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0WXZ1RC9WV2lUQnNkOCs5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLbm9WeGd0U0NEMzhpTDhE
Y0xIQzdsSlhnS2RMaTE1NGhPREkyblVvMVdvCnpaRXVDOXAwK0ErVU9EVEoxZnUy SzBpRG5Xc1VUeFdSaXRjMHBKNVltNTY4dHcwCm5DRzFSdG9ML2h2QVlYNm5pUC9Z
eXFid0g1VXJLSFJIbklrYWpiMWZiWFEKLS0tIGdzZzNyam5kM3BROXJCL0ZCemxT TTh4TDB2bjhWdWFNQ2ozN09sZWw1WTAKLS0tIDUrcHVmWUc3dkxSS3MrZXB6ZHNi
VjF4RHQ2anZUbGNXUlNCUEJXNkhBUUEK0m8++qCry3GPrVy3AKieBWBUjX01Bexe QzZVR3p2VlRyZjNqZk9ZTDBXOWdxYnMK3ZT951oj6lSP+3a1sQL88GUE/jlhfoWy
82GKZqeezxZIud3BJmWKW7KtoAQ5KxC8rLhJzJIczGPbHiVgKx1WNw== tkoKh2wNofg3BX9jMCgWm2LFcYxX1fMOoxhXxK2XNEV5et8gxHIxEw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVVXo1T2lWdEEwWjRPRUxk YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiZTlsOG02b2h3a1FaVW03
S3gvcGN0cXRLaW0yOUNqUUUvMEh1TzhsZlhBCmZFUFhIRVZGL2c2RmR2UHNRUVZC RTFHOXhnaU94elNxNU5jV3hYUWczS25NOGdNClFRYUdoN2pDeEQyenhyYTNDMGxE
RWlMTnVIMjJGaFU2andyejk3dWttUm8KLS0tIFBoRDNDQ1MrNVhXRzRtTTdveDNI U3Z1elFwVlRXNCtpSlF6TmNYMEgyem8KLS0tIHYrZjB2NVhUdFFXcmFVNnJyd3NM
eWdaL0ZGczNodVEySWxvVUlqc2hBN00KRjokYKoTskN+d1yhzeOBfFFA3csnMsmx eHpZOStRRWZROU9qd3FZUXU5amhsWjgKIx6s5IpwAkcdRgqjlmMqQTGgx7abZ7OU
UYtzuycE2+9qPv0nrQ5AtNqkdsPuLNnF9D0QXRyzSsqJ67axELehpw== C5BWpFIARLNUcBOKdORT8fT1m1EnmXKawxitVPHrhAJibvi9XuZIWw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJRUd6SkFyaHZLTnJGK05v YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNm5GWTZabmx0Lzd4VFNH
ZGZ4NGgreE9ueWZ5b2tzVHRUcmdZTnYxRzJJCmRkbmhXQXBXaG1Hc3ovem9PSUdE cFRRYUpCeTMxQ01LOThNRzY1T1FiWmt2TUVjClNWbDBnQk9lSmE1MVhieXh0ZENw
dGZrcERvTlBIOFpHQUNmUXNpcHJaN1EKLS0tIDJtVC94YXh0dUZjcjhDcFJpbTdN TjN0NnZlTlFMYmoyRTh0NDJHWTRUU2sKLS0tIEVCZElydnI3V0pBQ205b1hqV1U3
alNjWnJLSVlTNVliRERwdlpQYU9EaWMKynCfIpvVraG6HZMtExcE+qf7EbXPra3t aDdsUWc0bkdrcFBZeHhFc3hHeGhvL3MKt1sJlwjY4zc07tIp6qihcGu8UMdgr968
Bo0BO1fgh+Z1pdxYa71ruFdfBF4BqtRlLlJKqKfIjLmNU1mEh3Wv1Q== KYSO3fGr6XfRWwfzVb9h7FBsWK8ttar0tCK1JF9Hjjp0W9Oqu+AQGA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy - recipient: age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuUzhpKzdZdHNZRXNFTG1Z YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2cFFqMDJ5SUQrT1c0VHRH
OHVMTUNrMmNnWU9JQTFFbExpTkZ5ZnlDUEdRCmk3eEZHZlBuTTIxVS84Q2ExNzVo MmhYbFJBcjZJTjlFNHBtTk9uWTl0dnNud0Q0ClJBVkhzWXNqdGZIWE9DR3pzMDdq
OGhOU0NUTCsweVZYcStRd0NXOEhkYm8KLS0tIE50NTExZFM3akJXMDNHMVhkaysz cXBwWWJKWlhwM2RVTCtHbU4yZjdCdVEKLS0tIEFwZFIzakNHT2ovNmoxUGtXTUFD
OFBJMWZNcEtUWkVWdk1SRTVreUJaVFUK6oMy5pliMIBJ3juN7E2UKU+OzN7iYJy/ RVdWbGt0aHcrUGlNWTdUZ2xMa25mYlUKY6AOmHg9+ApJXeoyliXxvqtjwaVLSjH0
aB2MpEUc0ABd47X5aWgUTmz+bK6hSewiji9LM1mPQOeaqLMeNrrKpg== 6cuZSd05iOSHpR2vbg9jvRiXKXBS6DSN/BoIn+JUif8jY8cTQCMqDQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2024-12-18T22:08:25Z" lastmodified: "2024-12-18T22:08:25Z"
mac: ENC[AES256_GCM,data:vU+7/VLEzwDOrScD/HTo9JBf4ixtmcBmjtSUEtaVHwZuPMJ0OpydwcBPYKvhvU6z9xNecCcuiY7beJ5sq7fnv8XY66TOZWq/2tTZPXdJwpfAyHqBhf7uoCiOmSl53tpWzUFbfT8fQQwjKmBO1079Op24WNWzG3w0i9BsoYTYrpY=,iv:9bLlRg28paoitcK1lFc4ipsgMVvr/zECNozwXU2qJTM=,tag:uRviXeIlwUnvJqHwWuoYcA==,type:str] mac: ENC[AES256_GCM,data:vU+7/VLEzwDOrScD/HTo9JBf4ixtmcBmjtSUEtaVHwZuPMJ0OpydwcBPYKvhvU6z9xNecCcuiY7beJ5sq7fnv8XY66TOZWq/2tTZPXdJwpfAyHqBhf7uoCiOmSl53tpWzUFbfT8fQQwjKmBO1079Op24WNWzG3w0i9BsoYTYrpY=,iv:9bLlRg28paoitcK1lFc4ipsgMVvr/zECNozwXU2qJTM=,tag:uRviXeIlwUnvJqHwWuoYcA==,type:str]
pgp: pgp:
- created_at: "2024-12-24T11:28:03Z" - created_at: "2024-12-24T14:59:46Z"
enc: |- enc: |-
-----BEGIN PGP MESSAGE----- -----BEGIN PGP MESSAGE-----
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
=KXeu =pQM4
-----END PGP MESSAGE----- -----END PGP MESSAGE-----
fp: 4BE7925262289B476DBBC17B76FD3810215AE097 fp: 4BE7925262289B476DBBC17B76FD3810215AE097
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted