This commit is contained in:
Raphael Numbus
2025-12-06 22:22:16 +01:00
parent 0946cc7b08
commit 17eb579a20
13 changed files with 451 additions and 299 deletions
+168
View File
@@ -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";
}
View File
+125
View File
@@ -0,0 +1,125 @@
{ lib, utils, config, ... }:
let
inherit (lib)
head
optional
foldl'
nameValuePair
listToAttrs
optionals
concatStringsSep
sortOn
mkIf
mkEnableOption
mkOption
types
;
in
{
options = {
systemIdentity = {
enable = mkEnableOption "hashing of Luks values into PCR 15 and subsequent checks";
pcr15 = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The expected value of PCR 15 after all luks partitions have been unlocked
Should be a 64 character hex string as ouput by the sha256 field of
'systemd-analyze pcrs 15 --json=short'
If set to null (the default) it will not check the value.
If the check fails the boot will abort and you will be dropped into an emergency shell, if enabled.
In ermergency shell type:
'systemctl disable check-pcrs'
'systemctl default'
to continue booting
'';
example = "6214de8c3d861c4b451acc8c4e24294c95d55bcec516bbf15c077ca3bffb6547";
};
};
boot.initrd.luks.devices = lib.mkOption {
type =
with lib.types;
attrsOf (submodule {
config.crypttabExtraOpts = optionals config.systemIdentity.enable [
"tpm2-device=auto"
"tpm2-measure-pcr=yes"
];
});
};
};
config = mkIf config.systemIdentity.enable {
boot.kernelParams = [
"rd.luks=no"
];
boot.initrd.systemd.services =
{
check-pcrs = mkIf (config.systemIdentity.pcr15 != null) {
script = ''
echo "Checking PCR 15 value"
if [[ $(systemd-analyze pcrs 15 --json=short | jq -r ".[0].sha256") != "${config.systemIdentity.pcr15}" ]] ; then
echo "PCR 15 check failed"
exit 1
else
echo "PCR 15 check succeeded"
fi
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
unitConfig.DefaultDependencies = "no";
after = [ "cryptsetup.target" ];
before = [ "sysroot.mount" ];
requiredBy = [ "sysroot.mount" ];
};
}
// (listToAttrs (
foldl' (
acc: attrs:
let
extraOpts = attrs.value.crypttabExtraOpts ++ (optional attrs.value.allowDiscards "discard");
cfg = config.boot.initrd.systemd;
in
[
(nameValuePair "cryptsetup-${attrs.name}" {
unitConfig = {
Description = "Cryptography setup for ${attrs.name}";
DefaultDependencies = "no";
IgnoreOnIsolate = true;
Conflicts = [ "umount.target" ];
BindsTo = "${utils.escapeSystemdPath attrs.value.device}.device";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
TimeoutSec = "infinity";
KeyringMode = "shared";
OOMScoreAdjust = 500;
ImportCredential = "cryptsetup.*";
ExecStart = ''${cfg.package}/bin/systemd-cryptsetup attach '${attrs.name}' '${attrs.value.device}' '-' '${concatStringsSep "," extraOpts}' '';
ExecStop = ''${cfg.package}/bin/systemd-cryptsetup detach '${attrs.name}' '';
};
after =
[
"cryptsetup-pre.target"
"systemd-udevd-kernel.socket"
"${utils.escapeSystemdPath attrs.value.device}.device"
]
++ (optional cfg.tpm2.enable "systemd-tpm2-setup-early.service")
++ optional (acc != [ ]) "${(head acc).name}.service";
before = [
"blockdev@dev-mapper-${attrs.name}.target"
"cryptsetup.target"
"umount.target"
];
wants = [ "blockdev@dev-mapper-${attrs.name}.target" ];
requiredBy = [ "sysroot.mount" ];
})
]
++ acc
) [ ] (sortOn (x: x.name) (lib.attrsets.attrsToList config.boot.initrd.luks.devices))
));
};
}
+50
View File
@@ -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 <--
}
+51
View File
@@ -0,0 +1,51 @@
{
inputs = {
# Core Nixpkgs
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
# Diskpartitioning helper
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
# Secrets handling
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
# Power savings
autoaspm.url = "github:notthebee/AutoASPM";
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, disko, sops-nix, ... }@inputs: let
# System definition
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
# Helper: collect every *.nix file inside ./docker as a list
dockerModules = let
dir = ../docker;
entries = builtins.readDir dir;
names = builtins.attrNames entries;
nixNames = builtins.filter (n: builtins.match ".*\\.nix" n != null) names;
in map (name: "${dir}/${name}") nixNames;
in {
nixosConfigurations = {
numbus-server = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit inputs; };
modules = [
# Diskpartitioning helper
disko.nixosModules.disko
# Secrets handling
sops-nix.nixosModules.sops
# Power savings
inputs.autoaspm.nixosModules.autoaspm
# Core host configuration
./configuration.nix
./hardware-configuration.nix
# Docker services automatically added from ./docker/*.nix
] ++ dockerModules;
};
};
};
}