TEST
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"data-${DISK_NUMBER}" = {
|
||||
"content-${DISK_NUMBER}" = {
|
||||
type = "disk";
|
||||
device = "${DISK_PATH}";
|
||||
content = {
|
||||
@@ -8,12 +8,12 @@
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "luks";
|
||||
name = "crypted-data-${DISK_NUMBER}";
|
||||
settings.keyFile = "/run/secrets/disks/data-disk-${DISK_NUMBER}";
|
||||
name = "crypted-content-${DISK_NUMBER}";
|
||||
settings.keyFile = "/run/secrets/disks/content-disk-${DISK_NUMBER}";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "xfs";
|
||||
mountpoint = "/mnt/data-${DISK_NUMBER}";
|
||||
mountpoint = "/mnt/content-${DISK_NUMBER}";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -47,12 +47,12 @@ docker:
|
||||
DB_DATA_LOCATION=/mnt/config-storage/docker-data/immich/database
|
||||
|
||||
disks:
|
||||
data_disk_1: $DATA_DISK_1
|
||||
data_disk_2: $DATA_DISK_2
|
||||
data_disk_3: $DATA_DISK_3
|
||||
data_disk_4: $DATA_DISK_4
|
||||
data_disk_5: $DATA_DISK_5
|
||||
data_disk_6: $DATA_DISK_6
|
||||
parity_disk_1: $PARITY_DISK_1
|
||||
parity_disk_2: $PARITY_DISK_2
|
||||
parity_disk_3: $PARITY_DISK_3
|
||||
content-disk-1: $CONTENT_DISK_1_KEY
|
||||
content-disk-2: $CONTENT_DISK_2_KEY
|
||||
content-disk-3: $CONTENT_DISK_3_KEY
|
||||
content-disk-4: $CONTENT_DISK_4_KEY
|
||||
content-disk-5: $CONTENT_DISK_5_KEY
|
||||
content-disk-6: $CONTENT_DISK_6_KEY
|
||||
parity-disk-1: $PARITY_DISK_1_KEY
|
||||
parity-disk-2: $PARITY_DISK_2_KEY
|
||||
parity-disk-3: $PARITY_DISK_3_KEY
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
{ modulesPath, config, lib, pkgs, inputs, ... }:
|
||||
|
||||
let
|
||||
# Find all mount points that start with "/mnt/data-"
|
||||
dataDiskMounts = lib.attrsets.attrNames (
|
||||
lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "/mnt/data-" name) config.fileSystems
|
||||
);
|
||||
|
||||
# Find all mount points that start with "/mnt/parity-"
|
||||
parityDiskMounts = lib.attrsets.attrNames (
|
||||
lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "/mnt/parity-" name) config.fileSystems
|
||||
);
|
||||
|
||||
# Create an attribute set for snapraid data disks, e.g. { d1 = "/mnt/data-1"; d2 = "/mnt/data-2"; }
|
||||
snapraidDataDisks = lib.lists.foldl'
|
||||
(acc: path: acc // { "d${toString (acc.i + 1)}" = path; i = acc.i + 1; })
|
||||
{ i = 0; }
|
||||
dataDiskMounts;
|
||||
|
||||
# Dynamically create LUKS device entries for data and parity disks.
|
||||
# This assumes the keyfiles are stored at /etc/secrets/disks/data-disk-1, /etc/secrets/disks/parity-disk-1, etc.
|
||||
# and that the LUKS devices are named luks-data-1, luks-parity-1, etc. in disk-config.nix.
|
||||
luksDataDevices = lib.lists.foldl'
|
||||
(acc: path: let index = builtins.elemAt (lib.strings.splitString "-" path) 1; in
|
||||
acc // { "luks-data-${index}" = { keyFile = "/run/secrets/disks/data-disk-${index}"; }; })
|
||||
{ }
|
||||
dataDiskMounts;
|
||||
|
||||
luksParityDevices = lib.lists.foldl'
|
||||
(acc: path: let index = builtins.elemAt (lib.strings.splitString "-" path) 1; in
|
||||
acc // { "luks-parity-${index}" = { keyFile = "/run/secrets/disks/parity-disk-${index}"; }; })
|
||||
{ }
|
||||
parityDiskMounts;
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
./disk-config.nix
|
||||
./ensure-pcr.nix
|
||||
];
|
||||
|
||||
# Hardware settings
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
hardware.cpu.intel.updateMicrocode = true;
|
||||
hardware.cpu.amd.updateMicrocode = true;
|
||||
|
||||
# Secrets management
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ];
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.generateKey = true;
|
||||
sops.secrets."ssh_public_keys" = { owner = "numbus-admin"; path = "/etc/ssh/authorized_keys.d/numbus-admin"; };
|
||||
sops.secrets."docker/frigate" = { owner = "numbus-admin"; path = "/etc/docker-compose/frigate/.env"; };
|
||||
sops.secrets."docker/traefik" = { owner = "numbus-admin"; path = "/etc/docker-compose/traefik/.env"; };
|
||||
sops.secrets."docker/nextcloud" = { owner = "numbus-admin"; path = "/etc/docker-compose/nextcloud/.env"; };
|
||||
sops.secrets."docker/passbolt" = { owner = "numbus-admin"; path = "/etc/docker-compose/passbolt/.env"; };
|
||||
sops.secrets."docker/hass" = { owner = "numbus-admin"; path = "/etc/docker-compose/hass/.env"; };
|
||||
sops.secrets."docker/pihole" = { owner = "numbus-admin"; path = "/etc/docker-compose/pihole/.env"; };
|
||||
sops.secrets."docker/immich" = { owner = "numbus-admin"; path = "/etc/docker-compose/immich/.env"; };
|
||||
|
||||
# Bootloader options
|
||||
boot.initrd.systemd.enable = true;
|
||||
boot.initrd.systemd.tpm2.enable = true;
|
||||
boot.initrd.luks.devices = luksDataDevices // luksParityDevices;
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
# TPM2 PCR check
|
||||
systemIdentity.enable = true;
|
||||
# On first boot, get the value with: systemd-analyze pcrs 15 --json=short | jq -r ".[0].sha256"
|
||||
# and place it here.
|
||||
systemIdentity.pcr15 = null; # "6214de8c3d861c4b451acc8c4e24294c95d55bcec516bbf15c077ca3bffb6547";
|
||||
|
||||
# Timezone
|
||||
time.timeZone = "Europe/Paris";
|
||||
|
||||
# Internationalisation properties.
|
||||
i18n.defaultLocale = "fr_FR.UTF-8";
|
||||
i18n.extraLocaleSettings = {
|
||||
LC_ADDRESS = "fr_FR.UTF-8";
|
||||
LC_IDENTIFICATION = "fr_FR.UTF-8";
|
||||
LC_MEASUREMENT = "fr_FR.UTF-8";
|
||||
LC_MONETARY = "fr_FR.UTF-8";
|
||||
LC_NAME = "fr_FR.UTF-8";
|
||||
LC_NUMERIC = "fr_FR.UTF-8";
|
||||
LC_PAPER = "fr_FR.UTF-8";
|
||||
LC_TELEPHONE = "fr_FR.UTF-8";
|
||||
LC_TIME = "fr_FR.UTF-8";
|
||||
};
|
||||
|
||||
# Keyboard mapping
|
||||
console.keyMap = "fr";
|
||||
services.xserver.xkb = {
|
||||
layout = "fr";
|
||||
variant = "";
|
||||
};
|
||||
|
||||
# Enable SSH
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Allow unfree packages
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# Install packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
ncdu
|
||||
fastfetch
|
||||
tpm2-tss
|
||||
sops
|
||||
age
|
||||
powertop
|
||||
pciutils
|
||||
hdparm
|
||||
hd-idle
|
||||
hddtemp
|
||||
smartmontools
|
||||
cpufrequtils
|
||||
intel-gpu-tools
|
||||
];
|
||||
|
||||
# Power savings
|
||||
services.autoaspm.enable = true;
|
||||
powerManagement.powertop.enable = true;
|
||||
boot.kernelParams = [
|
||||
"pcie_aspm=force"
|
||||
"consoleblank=60"
|
||||
];
|
||||
|
||||
# Enable cron service
|
||||
services.cron = {
|
||||
enable = true;
|
||||
systemCronJobs = [
|
||||
];
|
||||
};
|
||||
|
||||
# Enable docker
|
||||
virtualisation.docker.enable = true;
|
||||
virtualisation.docker.daemon.settings = {
|
||||
data-root = "/mnt/config-storage/docker-volumes/";
|
||||
};
|
||||
|
||||
# Enable networking and firewall
|
||||
networking.interfaces.eth0.ipv4.addresses = [
|
||||
{
|
||||
address = "HOME_SERVER_IP";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
networking.defaultGateway = "HOME_ROUTER_IP";
|
||||
networking.nameservers = [ "HOME_SERVER_IP" "9.9.9.9" ];
|
||||
networking.networkmanager.enable = true;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# Open ports in the firewall
|
||||
networking.firewall.allowPing = false;
|
||||
networking.firewall.allowedTCPPorts = [ 53 80 443 ];
|
||||
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||
|
||||
# Hostname
|
||||
networking.hostName = "numbus-server";
|
||||
|
||||
# User account
|
||||
users.users.numbus-admin = {
|
||||
isNormalUser = true;
|
||||
description = "Numbus Admin";
|
||||
extraGroups = [ "networkmanager" "wheel" "docker" ];
|
||||
uid = 1000;
|
||||
initialPassword = "nixos-at-numbus";
|
||||
};
|
||||
|
||||
# Login message
|
||||
environment.loginShellInit = ''
|
||||
if [ "$(id -u)" -eq 1000 ]; then
|
||||
if [ -n "$SSH_TTY" ]; then
|
||||
fastfetch
|
||||
echo -e "\n\nWelcome to numbus.eu server !\n\n- This system is managed by NixOS\n- All changes are futile\n- Please consider buying support if you can't get your server running\n- Have a nice day and enjoy !"
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
# Enable auto updates
|
||||
system.autoUpgrade = {
|
||||
enable = true;
|
||||
allowReboot = true;
|
||||
flake = inputs.self.outPath;
|
||||
flags = [ "--print-build-logs" ];
|
||||
dates = "02:00";
|
||||
randomizedDelaySec = "45min";
|
||||
};
|
||||
|
||||
# Enable NixOS flakes
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
# --- Storage Configuration (MergerFS & SnapRAID) ---
|
||||
|
||||
# Create the mount point for the MergerFS pool
|
||||
fileSystems."/mnt/data-storage" = {
|
||||
device = "mergerfs";
|
||||
fsType = "fuse";
|
||||
options = [
|
||||
"defaults"
|
||||
"allow_other"
|
||||
"use_ino"
|
||||
"cache.files=off"
|
||||
"moveonenospc=true"
|
||||
"category.create=mfs"
|
||||
"srcmounts=${lib.strings.concatStringsSep ":" dataDiskMounts}"
|
||||
];
|
||||
};
|
||||
|
||||
# SnapRAID for data redundancy
|
||||
services.snapraid = {
|
||||
enable = true;
|
||||
contentFiles = map (disk: "${disk}/snapraid.content") dataDiskMounts;
|
||||
parityFiles = map (disk: "${disk}/snapraid.parity") parityDiskMounts;
|
||||
dataDisks = builtins.removeAttrs snapraidDataDisks [ "i" ];
|
||||
# Using default sync and scrub schedules:
|
||||
# Sync runs daily at 01:00.
|
||||
# Scrub runs weekly on Monday at 02:00.
|
||||
};
|
||||
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ services_selection() {
|
||||
|
||||
files_generation() {
|
||||
echo -e "\n\n ✅ Generating necessary folder tree..."
|
||||
mkdir -p extra-files/run/secrets/disks/
|
||||
mkdir -p extra-files/etc/secrets/disks/
|
||||
mkdir -p extra-files/var/lib/sops-nix/
|
||||
mkdir -p extra-files/etc/nixos/secrets/
|
||||
mkdir -p extra-files/mnt/config-storage/traefik/config/conf/
|
||||
@@ -175,12 +175,12 @@ files_generation() {
|
||||
export IMMICH_DB_DATABASE_NAME="$(openssl rand -hex 10)"
|
||||
export IMMICH_DB_USERNAME="$(openssl rand -hex 10)"
|
||||
export IMMICH_DB_PASSWORD="$(openssl rand -base64 32 | tr -d '\=+/')"
|
||||
export DATA_DISK_1_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export DATA_DISK_2_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export DATA_DISK_3_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export DATA_DISK_4_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export DATA_DISK_5_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export DATA_DISK_6_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_1_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_2_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_3_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_4_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_5_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export CONTENT_DISK_6_KEY="$(openssl rand -base64 10 | tr -d '\=+/')"
|
||||
export PARITY_DISK_1_KEY="$(openssl rand -base64 10 | tr -d '\=+/ ')"
|
||||
export PARITY_DISK_2_KEY="$(openssl rand -base64 10 | tr -d '\=+/ ')"
|
||||
export PARITY_DISK_3_KEY="$(openssl rand -base64 10 | tr -d '\=+/ ')"
|
||||
@@ -188,23 +188,23 @@ files_generation() {
|
||||
export BOOT_DISK_2_KEY="$(openssl rand -base64 10 | tr -d '\=+/ ')"
|
||||
|
||||
echo -e "\n ✅ Generating disk keyfiles in extra-files/etc/secrets/disks/..."
|
||||
for i in {1..6}; do var="DATA_DISK_${i}_KEY"; [[ -n "${!var}" ]] && echo -n "${!var}" > "extra-files/etc/secrets/disks/data-disk-$i"; done
|
||||
for i in {1..6}; do var="CONTENT_DISK_${i}_KEY"; [[ -n "${!var}" ]] && echo -n "${!var}" > "extra-files/etc/secrets/disks/content-disk-$i"; done
|
||||
for i in {1..3}; do var="PARITY_DISK_${i}_KEY"; [[ -n "${!var}" ]] && echo -n "${!var}" > "extra-files/etc/secrets/disks/parity-disk-$i"; done
|
||||
for i in {1..2}; do var="BOOT_DISK_${i}_KEY"; [[ -n "${!var}" ]] && echo -n "${!var}" > "extra-files/etc/secrets/disks/boot-disk-$i"; done
|
||||
|
||||
echo "$REMOTE_PASS" | ssh_to_host """
|
||||
sudo -S mkdir -p /run/secrets/disks/
|
||||
echo -n $DATA_DISK_1_KEY | sudo -S tee /run/secrets/disks/data-disk-1 > /dev/null
|
||||
echo -n $DATA_DISK_2_KEY | sudo -S tee /run/secrets/disks/data-disk-2 > /dev/null
|
||||
echo -n $DATA_DISK_3_KEY | sudo -S tee /run/secrets/disks/data-disk-3 > /dev/null
|
||||
echo -n $DATA_DISK_4_KEY | sudo -S tee /run/secrets/disks/data-disk-4 > /dev/null
|
||||
echo -n $DATA_DISK_5_KEY | sudo -S tee /run/secrets/disks/data-disk-5 > /dev/null
|
||||
echo -n $DATA_DISK_6_KEY | sudo -S tee /run/secrets/disks/data-disk-6 > /dev/null
|
||||
echo -n $PARITY_DISK_1_KEY | sudo -S tee /run/secrets/disks/parity-disk-1 > /dev/null
|
||||
echo -n $PARITY_DISK_2_KEY | sudo -S tee /run/secrets/disks/parity-disk-2 > /dev/null
|
||||
echo -n $PARITY_DISK_3_KEY | sudo -S tee /run/secrets/disks/parity-disk-3 > /dev/null
|
||||
echo -n $BOOT_DISK_1_KEY | sudo -S tee /run/secrets/disks/boot-disk-1 > /dev/null
|
||||
echo -n $BOOT_DISK_2_KEY | sudo -S tee /run/secrets/disks/boot-disk-2 > /dev/null
|
||||
sudo -S mkdir -p /etc/secrets/disks/
|
||||
echo -n $CONTENT_DISK_1_KEY | sudo -S tee /etc/secrets/disks/content-disk-1 > /dev/null
|
||||
echo -n $CONTENT_DISK_2_KEY | sudo -S tee /etc/secrets/disks/content-disk-2 > /dev/null
|
||||
echo -n $CONTENT_DISK_3_KEY | sudo -S tee /etc/secrets/disks/content-disk-3 > /dev/null
|
||||
echo -n $CONTENT_DISK_4_KEY | sudo -S tee /etc/secrets/disks/content-disk-4 > /dev/null
|
||||
echo -n $CONTENT_DISK_5_KEY | sudo -S tee /etc/secrets/disks/content-disk-5 > /dev/null
|
||||
echo -n $CONTENT_DISK_6_KEY | sudo -S tee /etc/secrets/disks/content-disk-6 > /dev/null
|
||||
echo -n $PARITY_DISK_1_KEY | sudo -S tee /etc/secrets/disks/parity-disk-1 > /dev/null
|
||||
echo -n $PARITY_DISK_2_KEY | sudo -S tee /etc/secrets/disks/parity-disk-2 > /dev/null
|
||||
echo -n $PARITY_DISK_3_KEY | sudo -S tee /etc/secrets/disks/parity-disk-3 > /dev/null
|
||||
echo -n $BOOT_DISK_1_KEY | sudo -S tee /etc/secrets/disks/boot-disk-1 > /dev/null
|
||||
echo -n $BOOT_DISK_2_KEY | sudo -S tee /etc/secrets/disks/boot-disk-2 > /dev/null
|
||||
"""
|
||||
|
||||
echo -e "\n ✅ Encrypting secrets in the correct file..."
|
||||
@@ -216,8 +216,8 @@ files_generation() {
|
||||
cp -avu extra-files/etc/nixos/secrets/secrets.yaml ./secrets/secrets.yaml
|
||||
|
||||
echo -e "\n ✅ Writing correct ips to configuration.nix..."
|
||||
sed -i s+HOME_SERVER_IP+$HOME_SERVER_IP+g configuration.nix
|
||||
sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g configuration.nix
|
||||
sed -i s+HOME_SERVER_IP+$HOME_SERVER_IP+g ./nix-config/configuration.nix
|
||||
sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g ./nix-config/configuration.nix
|
||||
|
||||
echo -e "\n ✅ Adapting the docker configuration to your hardware..."
|
||||
FRIGATE_DEVICES_BLOCK=""
|
||||
@@ -290,6 +290,7 @@ disk_config_generation() {
|
||||
|
||||
DISK_NAMES=$(ssh_to_host "lsblk -d -n -o NAME,TYPE | awk '\$2==\"disk\" {print \$1}'")
|
||||
|
||||
# --> Get disks info
|
||||
for name in $DISK_NAMES; do
|
||||
details=$(echo "$REMOTE_PASS" | ssh_to_host "
|
||||
set -e
|
||||
@@ -314,6 +315,7 @@ disk_config_generation() {
|
||||
size=\$(lsblk -b -d -n -o SIZE \"\$devpath\")
|
||||
echo \"\$size:::\$type:::\$health:::\$by_id\"
|
||||
")
|
||||
# Get disks info <--
|
||||
|
||||
mapfile -t parts < <(echo "$details" | tr ':' '\n')
|
||||
size="${parts[0]}"
|
||||
@@ -373,12 +375,19 @@ disk_config_generation() {
|
||||
|
||||
num_selected=${#selected_data_names[@]}
|
||||
num_parity=0
|
||||
if (( num_selected > 0 )); then
|
||||
num_parity=$(( (num_selected - 1) / 3 + 1 ))
|
||||
num_content=0
|
||||
|
||||
if (( num_selected == 1 )); then
|
||||
num_content=1
|
||||
num_parity=0
|
||||
elif (( num_selected > 1 )); then
|
||||
num_parity=$(( (num_selected + 2) / 3 ))
|
||||
num_content=$(( num_selected - num_parity ))
|
||||
fi
|
||||
|
||||
# Sort selected disks by size (largest first)
|
||||
sorted_disks=($(
|
||||
# shellcheck disable=SC2145
|
||||
for name in "${selected_data_names[@]}"; do
|
||||
echo "${DISK_SIZE_MAP[$name]} $name"
|
||||
done | sort -rn | awk '{print $2}'
|
||||
@@ -386,33 +395,33 @@ disk_config_generation() {
|
||||
|
||||
# Assign parity disks (the largest ones)
|
||||
parity_disks_final=()
|
||||
for i in $(seq 0 $((num_parity - 1))); do
|
||||
for i in $(seq 0 $((num_parity > 0 ? num_parity - 1 : -1))); do
|
||||
[[ -n "${sorted_disks[$i]}" ]] && parity_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}")
|
||||
done
|
||||
|
||||
# Assign data disks (the remaining ones)
|
||||
data_disks_final=()
|
||||
# Assign content disks (the remaining ones)
|
||||
content_disks_final=()
|
||||
for i in $(seq $num_parity $((num_selected - 1))); do
|
||||
[[ -n "${sorted_disks[$i]}" ]] && data_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}")
|
||||
[[ -n "${sorted_disks[$i]}" ]] && content_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}")
|
||||
done
|
||||
|
||||
# Set exported variables (up to 9 data disks and 2 parity disks)
|
||||
for i in {0..8}; do export "DATA_DISK_$((i+1))"="${data_disks_final[$i]:-}"; done
|
||||
# Set exported variables (up to 6 content disks and 3 parity disks)
|
||||
for i in {0..5}; do export "CONTENT_DISK_$((i+1))"="${content_disks_final[$i]:-}"; done
|
||||
for i in {0..2}; do export "PARITY_DISK_$((i+1))"="${parity_disks_final[$i]:-}"; done
|
||||
fi
|
||||
else
|
||||
echo -e "\n\n ⚠️ No remaining disks to select for data."
|
||||
fi
|
||||
|
||||
# --- Final Recap ---
|
||||
# --> Final recap
|
||||
NUMBER_OF_BOOT_DISKS=0
|
||||
[[ -n "$BOOT_DISK_1" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1)) && export BOOT_DISK_1
|
||||
[[ -n "$BOOT_DISK_2" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1)) && export BOOT_DISK_2
|
||||
|
||||
NUMBER_OF_DATA_DISKS=0
|
||||
for i in {1..9}; do
|
||||
disk_var="DATA_DISK_$i"
|
||||
[[ -n "${!disk_var}" ]] && NUMBER_OF_DATA_DISKS=$((NUMBER_OF_DATA_DISKS + 1))
|
||||
NUMBER_OF_CONTENT_DISKS=0
|
||||
for i in {1..6}; do
|
||||
disk_var="CONTENT_DISK_$i"
|
||||
[[ -n "${!disk_var}" ]] && NUMBER_OF_CONTENT_DISKS=$((NUMBER_OF_CONTENT_DISKS + 1))
|
||||
done
|
||||
|
||||
NUMBER_OF_PARITY_DISKS=0
|
||||
@@ -430,9 +439,9 @@ Please review the selected disk layout before proceeding.
|
||||
* **Boot 1:** \`$BOOT_DISK_1\`
|
||||
$( [[ -n "$BOOT_DISK_2" ]] && echo "* **Boot 2:** \`$BOOT_DISK_2\`" || echo "* **Boot 2:** *Not configured*")
|
||||
|
||||
**Data Disks ($NUMBER_OF_DATA_DISKS):**
|
||||
$(for i in {1..9}; do disk_var="DATA_DISK_$i"; [[ -n "${!disk_var}" ]] && echo "* **Data $i:** \`${!disk_var}\`"; done)
|
||||
$( [[ $NUMBER_OF_DATA_DISKS -eq 0 ]] && echo "* *Not configured*")
|
||||
**Data Disks ($NUMBER_OF_CONTENT_DISKS):**
|
||||
$(for i in {1..6}; do disk_var="CONTENT_DISK_$i"; [[ -n "${!disk_var}" ]] && echo "* **Data $i:** \`${!disk_var}\`"; done)
|
||||
$( [[ $NUMBER_OF_CONTENT_DISKS -eq 0 ]] && echo "* *Not configured*")
|
||||
|
||||
**Parity Disks ($NUMBER_OF_PARITY_DISKS):**
|
||||
$(for i in {1..3}; do disk_var="PARITY_DISK_$i"; [[ -n "${!disk_var}" ]] && echo "* **Parity $i:** \`${!disk_var}\`"; done)
|
||||
@@ -442,42 +451,81 @@ EOF
|
||||
|
||||
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "$RECAP_CONTENT")"
|
||||
gum confirm "Proceed with this disk configuration?" || { echo " ❌ Aborting as requested."; exit 1; }
|
||||
# Final recap <--
|
||||
|
||||
echo -e "\n\n ⚙️ Generating disko configuration from templates..."
|
||||
template_file="config-files/disks/boot-${NUMBER_OF_BOOT_DISKS}.nix"
|
||||
(envsubst < "$template_file") > disk-config.nix
|
||||
(envsubst < "$template_file") > ./nix-config/disks/disko.nix
|
||||
echo -e "\n ✅ Generated boot disk configuration."
|
||||
|
||||
for i in $(seq 1 $NUMBER_OF_DATA_DISKS); do
|
||||
disk_var="DATA_DISK_$i"
|
||||
for i in $(seq 1 $NUMBER_OF_CONTENT_DISKS); do
|
||||
disk_var="CONTENT_DISK_$i"
|
||||
export DISK_NUMBER=$i
|
||||
export DISK_PATH=${!disk_var}
|
||||
(envsubst < "config-files/disks/data.nix") >> disk-config.nix
|
||||
(envsubst < "config-files/disks/data.nix") >> ./nix-config/disks/disko.nix
|
||||
done
|
||||
[[ "$NUMBER_OF_DATA_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_DATA_DISKS data disk configuration(s)."
|
||||
[[ "$NUMBER_OF_CONTENT_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_CONTENT_DISKS data disk configuration(s)."
|
||||
|
||||
for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do
|
||||
disk_var="PARITY_DISK_$i"
|
||||
export DISK_NUMBER=$i
|
||||
export DISK_PATH=${!disk_var}
|
||||
(envsubst < "config-files/disks/parity.nix") >> disk-config.nix
|
||||
(envsubst < "config-files/disks/parity.nix") >> ./nix-config/disks/disko.nix
|
||||
done
|
||||
[[ "$NUMBER_OF_PARITY_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)."
|
||||
|
||||
# Close the imports block
|
||||
cat <<'EOF' >> disk-config.nix
|
||||
# Close the disko imports block
|
||||
echo '}' >> ./nix-config/disks/disko.nix
|
||||
echo -e "\n ✅ Final disko configuration created at './nix-config/disks/disko.nix'."
|
||||
|
||||
# --> Generate automatic unlock configuration in ./nix-config/disks/snapraid.nix
|
||||
if [[ "$NUMBER_OF_CONTENT_DISKS" -gt 0 || "$NUMBER_OF_PARITY_DISKS" -gt 0 ]]; then
|
||||
echo -e "\n ⚙️ Adding automatic disk unlocking configuration to './nix-config/disks/snapraid.nix'..."
|
||||
sed -i '$ d' ./nix-config/disks/snapraid.nix
|
||||
|
||||
cat <<EOF >> ./nix-config/disks/snapraid.nix
|
||||
# --> Automatic data disks unlock, generated by deploy.sh on $(date)
|
||||
boot.initrd.luks.devices = {
|
||||
EOF
|
||||
|
||||
for i in $(seq 1 $NUMBER_OF_CONTENT_DISKS); do
|
||||
disk_var="CONTENT_DISK_$i"
|
||||
cat <<EOF >> ./nix-config/disks/snapraid.nix
|
||||
"crypted-content-disk-${i}" = {
|
||||
device = "${!disk_var}";
|
||||
keyFile = "/etc/secrets/disks/content-disk-${i}";
|
||||
};
|
||||
EOF
|
||||
done
|
||||
|
||||
for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do
|
||||
disk_var="PARITY_DISK_$i"
|
||||
cat <<EOF >> ./nix-config/disks/snapraid.nix
|
||||
"crypted-parity-disk-${i}" = {
|
||||
device = "${!disk_var}";
|
||||
keyFile = "/etc/secrets/disks/parity-disk-${i}";
|
||||
};
|
||||
EOF
|
||||
done
|
||||
cat <<'EOF' >> ./nix-config/disks/snapraid.nix
|
||||
# Automatic data disks unlock <--
|
||||
};
|
||||
}
|
||||
EOF
|
||||
echo -e "\n ✅ Final disko configuration created at 'disk-config.nix'."
|
||||
|
||||
echo -e "\n\n ✅ Automatic disks unlock configuration added."
|
||||
fi
|
||||
# Generate automatic unlock configuration in ./nix-config/disks/snapraid.nix <--
|
||||
}
|
||||
|
||||
necessary_credentials() {
|
||||
}
|
||||
|
||||
deploy() {
|
||||
echo -e "\n\n 🔄 Deploying to the remote server..."
|
||||
nix run github:nix-community/nixos-anywhere -- \
|
||||
--generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
|
||||
--flake .#numbus-server \
|
||||
--generate-hardware-config nixos-generate-config ./nix-config/hardware-configuration.nix \
|
||||
--flake ./nix-config/flake.nix#numbus-server \
|
||||
--extra-files extra-files \
|
||||
--chown "/home/numbus-admin/" 1000:1000 \
|
||||
--target-host nixos@$TARGET_HOST
|
||||
@@ -487,16 +535,15 @@ deploy() {
|
||||
}
|
||||
|
||||
sum_up() {
|
||||
echo $DATA_DISK_1_KEY
|
||||
echo $DATA_DISK_2_KEY
|
||||
echo $DATA_DISK_3_KEY
|
||||
echo $DATA_DISK_4_KEY
|
||||
echo $DATA_DISK_5_KEY
|
||||
echo $DATA_DISK_6_KEY
|
||||
echo $CONTENT_DISK_1_KEY
|
||||
echo $CONTENT_DISK_2_KEY
|
||||
echo $CONTENT_DISK_3_KEY
|
||||
echo $CONTENT_DISK_4_KEY
|
||||
echo $CONTENT_DISK_5_KEY
|
||||
echo $CONTENT_DISK_6_KEY
|
||||
echo $PARITY_DISK_1_KEY
|
||||
echo $PARITY_DISK_2_KEY
|
||||
echo $PARITY_DISK_3_KEY
|
||||
|
||||
}
|
||||
|
||||
postrun_action() {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
I am working on a homelab deployer tool. The description of this homelab deployer tool is available in the README.md file, you shall read it to better understand your job. Your job as a NixOS expert will be to help me change the current broken configuration into my wanted configuration.
|
||||
|
||||
# Here is how my script is set up right now
|
||||
### Disks selection
|
||||
My script allows the selection of disks.
|
||||
|
||||
The disks are separated in two categories : boot and data disks. Data disks include content disks and parity disks.
|
||||
|
||||
First, the user choose the boot disks : he can choose one boot disk or two boot disks in a mirror setup. User has to choose at least one boot disk.
|
||||
|
||||
Then, the user chooses data disks. He can choose how many data disks he desires up to 9, or no disks at all. Then the scripts automatically assigns data disks to the content and parity disks according to 2 conditions : <br>
|
||||
- if more than one data disk is selected, the content/parity repartition must be 1 parity disk for up to 2 content disks. <br>
|
||||
- if more than one data disk is selected, the content/parity repartition must be 1 parity disk for up to 2 content disks. <br>
|
||||
If only one data disk is selected, it is a parity disk.
|
||||
|
||||
### RAID configuration
|
||||
If there is only one boot disk selected, the boot disk will be striped. If there are 2 boot disks selected, it will be a mirror.
|
||||
|
||||
The data disks mountpoints are logically set : <br>
|
||||
- /mnt/content-1 <br>
|
||||
- /mnt/content-2 <br>
|
||||
- /mnt/content-3 <br>
|
||||
- /mnt/content-4 <br>
|
||||
- /mnt/content-5 <br>
|
||||
- /mnt/content-6 <br>
|
||||
- /mnt/parity-1 <br>
|
||||
- /mnt/parity-2 <br>
|
||||
- /mnt/parity-3 <br>
|
||||
|
||||
SnapRAID is used to get a RAID configuration working with the content and parity drives even if their size is not the same. MergerFS is used to obtain one clean path to the data storage. The script uses a combination of code to find the UUIDs of the disks to reference them in the final disk-config.nix configuration file.
|
||||
|
||||
### Disks unlocking
|
||||
|
||||
The boot disks are unlocked manually by providing the passphrase on boot.
|
||||
|
||||
The data disks are unlocked manually too.
|
||||
|
||||
The configuration.nix file is broken and need to be fixed.
|
||||
|
||||
# Here is how I want my setup to be
|
||||
|
||||
### Disks selection
|
||||
My script allows the selection of disks.
|
||||
|
||||
The disks are separated in two categories : boot and data disks. Data disks include content disks and parity disks.
|
||||
|
||||
First, the user chooses the boot disks : he can choose one boot disk or two boot disks in a mirror setup. User has to choose at least one boot disk.
|
||||
|
||||
Then, the user chooses data disks. He can choose how many data disks he desires up to 9, or no disks at all. Then the scripts automatically assigns data disks to the content and parity disks according to 3 conditions : <br>
|
||||
- if there is only one data disk selected, it must be a content disk <br>
|
||||
- if more than one data disk is selected, the larger (or equal if all disks are the same size) disks are necessarily parity disks <br>
|
||||
- if more than one data disk is selected, the content/parity repartition must be 1 parity disk for up to 2 content disks. <br>
|
||||
|
||||
### RAID configuration
|
||||
If there is only one boot disk selected, the boot disk will be striped. If there are 2 boot disks selected, it will be a mirror.
|
||||
|
||||
The data disks mountpoints are dynamically and logically set : <br>
|
||||
- /mnt/content-1 <br>
|
||||
- /mnt/content-2 <br>
|
||||
- /mnt/content-3 <br>
|
||||
- /mnt/content-4 <br>
|
||||
- /mnt/content-5 <br>
|
||||
- /mnt/content-6 <br>
|
||||
- /mnt/parity-1 <br>
|
||||
- /mnt/parity-2 <br>
|
||||
- /mnt/parity-3 <br>
|
||||
|
||||
SnapRAID is used to get a RAID configuration working with the content and parity drives even if their size is not the same. MergerFS is used to obtain one clean path to the data storage. The script uses a combination of code to find the UUIDs of the disks to reference them in the final disk-config.nix configuration file.
|
||||
|
||||
### Disks unlocking
|
||||
The boot disks are unlocked manually by providing the passphrase on boot.
|
||||
|
||||
The data disks are unlocked automatically on boot using a keyfile that is located on the root partition. This means that I need to unlock the boot disk(s), and once it is unlocked the keyfiles for the data disks are decrypted and ready to be used.
|
||||
|
||||
The keyfile are dynamically and logically ordered and referenced on the root partition : <br>
|
||||
- /etc/secrets/disks/content-disk-1 <br>
|
||||
- /etc/secrets/disks/content-disk-2 <br>
|
||||
- /etc/secrets/disks/content-disk-3 <br>
|
||||
- /etc/secrets/disks/content-disk-4 <br>
|
||||
- /etc/secrets/disks/content-disk-5 <br>
|
||||
- /etc/secrets/disks/content-disk-6 <br>
|
||||
- /etc/secrets/disks/parity-disk-1 <br>
|
||||
- /etc/secrets/disks/parity-disk-2 <br>
|
||||
- /etc/secrets/disks/parity-disk-3 <br>
|
||||
|
||||
The LUKS partition on the disks are dynamically and logically ordered : <br>
|
||||
- crypted-content-disk-1 <br>
|
||||
- crypted-content-disk-2 <br>
|
||||
- crypted-content-disk-3 <br>
|
||||
- crypted-content-disk-4 <br>
|
||||
- crypted-content-disk-5 <br>
|
||||
- crypted-content-disk-6 <br>
|
||||
- crypted-parity-disk-1 <br>
|
||||
- crypted-parity-disk-2 <br>
|
||||
- crypted-parity-disk-3 <br>
|
||||
|
||||
To automatically unlock the data disks, you need to set a crypttab entry. Since we are using NixOS, we will set the boot.initrd.luks.devices option.
|
||||
|
||||
This option needs to following : volume-name (i.e. crypted-content-1 for example), encrypted-device (i.e. the UUID path), key-file (i.e. the keyfile path). Here is a static example for a disk.
|
||||
```
|
||||
boot.initrd.luks.devices = {
|
||||
"my-device-mapper" = {
|
||||
device = "/dev/disk/by-uuid/YOUR-UUID-HERE";
|
||||
keyFile = "/path/to/your-keyfile";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Volume names are logically ordered : crypted-data-1, crypted-data-2, crypted-data-3, [...], crypted-data-6, or crypted-parity-1, [...], crypted-parity-3.
|
||||
|
||||
Encrypted-device is the path to the device, using the /dev/by-id/YOUR-UUID-HERE UUID path. This is the tricky part since the disks are dynamically selected. The configuration needs to find the correct UUIDs, the same ones as those selected in the script.
|
||||
|
||||
Key-file is the path to the keyfile which are logically ordered : /etc/secrets/disks/data-disk-1, /etc/secrets/disks/data-disk-2, /etc/secrets/disks/data-disk-3, [...], /etc/secrets/disks/data-disk-6 or /etc/secrets/disks/parity-disk-1, [...], /etc/secrets/disks/parity-disk-3.
|
||||
@@ -0,0 +1,168 @@
|
||||
{ modulesPath, config, lib, pkgs, inputs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
./disks/disko.nix
|
||||
./disks/snapraid.nix
|
||||
./disks/pcr-check.nix
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
|
||||
# Hardware settings
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
hardware.cpu.intel.updateMicrocode = true;
|
||||
hardware.cpu.amd.updateMicrocode = true;
|
||||
|
||||
# Secrets management
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ];
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.generateKey = true;
|
||||
sops.secrets."ssh_public_keys" = { owner = "numbus-admin"; path = "/etc/ssh/authorized_keys.d/numbus-admin"; };
|
||||
sops.secrets."docker/frigate" = { owner = "numbus-admin"; path = "/etc/docker-compose/frigate/.env"; };
|
||||
sops.secrets."docker/traefik" = { owner = "numbus-admin"; path = "/etc/docker-compose/traefik/.env"; };
|
||||
sops.secrets."docker/nextcloud" = { owner = "numbus-admin"; path = "/etc/docker-compose/nextcloud/.env"; };
|
||||
sops.secrets."docker/passbolt" = { owner = "numbus-admin"; path = "/etc/docker-compose/passbolt/.env"; };
|
||||
sops.secrets."docker/hass" = { owner = "numbus-admin"; path = "/etc/docker-compose/hass/.env"; };
|
||||
sops.secrets."docker/pihole" = { owner = "numbus-admin"; path = "/etc/docker-compose/pihole/.env"; };
|
||||
sops.secrets."docker/immich" = { owner = "numbus-admin"; path = "/etc/docker-compose/immich/.env"; };
|
||||
|
||||
# Bootloader options
|
||||
boot.initrd.systemd.enable = true;
|
||||
boot.initrd.systemd.tpm2.enable = true;
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
# TPM2 PCR check
|
||||
systemIdentity.enable = true;
|
||||
# On first boot, get the value with: systemd-analyze pcrs 15 --json=short | jq -r ".[0].sha256"
|
||||
# and place it here.
|
||||
systemIdentity.pcr15 = null; # "6214de8c3d861c4b451acc8c4e24294c95d55bcec516bbf15c077ca3bffb6547";
|
||||
|
||||
# Timezone
|
||||
time.timeZone = "Europe/Paris";
|
||||
|
||||
# Internationalisation properties.
|
||||
i18n.defaultLocale = "fr_FR.UTF-8";
|
||||
i18n.extraLocaleSettings = {
|
||||
LC_ADDRESS = "fr_FR.UTF-8";
|
||||
LC_IDENTIFICATION = "fr_FR.UTF-8";
|
||||
LC_MEASUREMENT = "fr_FR.UTF-8";
|
||||
LC_MONETARY = "fr_FR.UTF-8";
|
||||
LC_NAME = "fr_FR.UTF-8";
|
||||
LC_NUMERIC = "fr_FR.UTF-8";
|
||||
LC_PAPER = "fr_FR.UTF-8";
|
||||
LC_TELEPHONE = "fr_FR.UTF-8";
|
||||
LC_TIME = "fr_FR.UTF-8";
|
||||
};
|
||||
|
||||
# Keyboard mapping
|
||||
console.keyMap = "fr";
|
||||
services.xserver.xkb = {
|
||||
layout = "fr";
|
||||
variant = "";
|
||||
};
|
||||
|
||||
# Enable SSH
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Allow unfree packages
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# Install packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
ncdu
|
||||
fastfetch
|
||||
tpm2-tss
|
||||
sops
|
||||
age
|
||||
powertop
|
||||
pciutils
|
||||
hdparm
|
||||
hd-idle
|
||||
hddtemp
|
||||
smartmontools
|
||||
cpufrequtils
|
||||
intel-gpu-tools
|
||||
];
|
||||
|
||||
# Power savings
|
||||
services.autoaspm.enable = true;
|
||||
powerManagement.powertop.enable = true;
|
||||
boot.kernelParams = [
|
||||
"pcie_aspm=force"
|
||||
"consoleblank=60"
|
||||
];
|
||||
|
||||
# Enable cron service
|
||||
services.cron = {
|
||||
enable = true;
|
||||
systemCronJobs = [
|
||||
];
|
||||
};
|
||||
|
||||
# Enable docker
|
||||
virtualisation.docker.enable = true;
|
||||
virtualisation.docker.daemon.settings = {
|
||||
data-root = "/mnt/config-storage/docker-volumes/";
|
||||
};
|
||||
|
||||
# Enable networking and firewall
|
||||
networking.interfaces.eth0.ipv4.addresses = [
|
||||
{
|
||||
address = "HOME_SERVER_IP";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
networking.defaultGateway = "HOME_ROUTER_IP";
|
||||
networking.nameservers = [ "HOME_SERVER_IP" "9.9.9.9" ];
|
||||
networking.networkmanager.enable = true;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# Open ports in the firewall
|
||||
networking.firewall.allowPing = false;
|
||||
networking.firewall.allowedTCPPorts = [ 53 80 443 ];
|
||||
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||
|
||||
# Hostname
|
||||
networking.hostName = "numbus-server";
|
||||
|
||||
# User account
|
||||
users.users.numbus-admin = {
|
||||
isNormalUser = true;
|
||||
description = "Numbus Admin";
|
||||
extraGroups = [ "networkmanager" "wheel" "docker" ];
|
||||
uid = 1000;
|
||||
initialPassword = "nixos-at-numbus";
|
||||
};
|
||||
|
||||
# Login message
|
||||
environment.loginShellInit = ''
|
||||
if [ "$(id -u)" -eq 1000 ]; then
|
||||
if [ -n "$SSH_TTY" ]; then
|
||||
fastfetch
|
||||
echo -e "\n\nWelcome to numbus.eu server !\n\n- This system is managed by NixOS\n- All changes are futile\n- Please consider buying support if you can't get your server running\n- Have a nice day and enjoy !"
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
# Enable auto updates
|
||||
system.autoUpgrade = {
|
||||
enable = true;
|
||||
allowReboot = true;
|
||||
flake = inputs.self.outPath;
|
||||
flags = [ "--print-build-logs" ];
|
||||
dates = "02:00";
|
||||
randomizedDelaySec = "45min";
|
||||
};
|
||||
|
||||
# Enable NixOS flakes
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
{ lib, utils, config, ... }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
head
|
||||
@@ -15,6 +16,7 @@ let
|
||||
types
|
||||
;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
systemIdentity = {
|
||||
@@ -60,7 +62,7 @@ in
|
||||
echo "PCR 15 check failed"
|
||||
exit 1
|
||||
else
|
||||
echo "PCR 15 check suceed"
|
||||
echo "PCR 15 check succeeded"
|
||||
fi
|
||||
'';
|
||||
serviceConfig = {
|
||||
@@ -0,0 +1,50 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
# --> SnapRAID disks research
|
||||
let
|
||||
contentDiskMounts = lib.attrsets.attrNames (
|
||||
lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "/mnt/content-" name) config.fileSystems
|
||||
);
|
||||
parityDiskMounts = lib.attrsets.attrNames (
|
||||
lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "/mnt/parity-" name) config.fileSystems
|
||||
);
|
||||
snapraidDataDisks = lib.lists.foldl'
|
||||
(acc: path: acc // { "d${toString (acc.i + 1)}" = path; i = acc.i + 1; })
|
||||
{ i = 0; }
|
||||
contentDiskMounts;
|
||||
in
|
||||
# SnapRAID disks research <--
|
||||
|
||||
|
||||
|
||||
# --> MergerFS setup
|
||||
{
|
||||
fileSystems."/mnt/data-storage" = {
|
||||
device = "mergerfs";
|
||||
fsType = "fuse";
|
||||
options = [
|
||||
"defaults"
|
||||
"allow_other"
|
||||
"use_ino"
|
||||
"cache.files=off"
|
||||
"moveonenospc=true"
|
||||
"category.create=mfs"
|
||||
"srcmounts=${lib.strings.concatStringsSep ":" contentDiskMounts}"
|
||||
];
|
||||
};
|
||||
# MergerFS setup <--
|
||||
|
||||
|
||||
|
||||
# --> SnapRAID setup
|
||||
services.snapraid = {
|
||||
enable = true;
|
||||
contentFiles = map (disk: "${disk}/snapraid.content") contentDiskMounts;
|
||||
parityFiles = map (disk: "${disk}/snapraid.parity") parityDiskMounts;
|
||||
dataDisks = builtins.removeAttrs snapraidDataDisks [ "i" ];
|
||||
};
|
||||
# SnapRAID setup <--
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
# Helper: collect every *.nix file inside ./docker as a list
|
||||
dockerModules = let
|
||||
dir = ./docker;
|
||||
dir = ../docker;
|
||||
entries = builtins.readDir dir;
|
||||
names = builtins.attrNames entries;
|
||||
nixNames = builtins.filter (n: builtins.match ".*\\.nix" n != null) names;
|
||||
Reference in New Issue
Block a user