This commit is contained in:
Patrick 2024-07-26 22:12:48 +02:00
parent 857140bbd2
commit 44d265ecdb
Signed by: patrick
GPG key ID: 451F95EFB8BECD0F
144 changed files with 3479 additions and 2930 deletions

View file

@ -3,7 +3,8 @@
lib, lib,
pkgs, pkgs,
... ...
}: { }:
{
boot = lib.mkIf (!config.boot.isContainer) { boot = lib.mkIf (!config.boot.isContainer) {
initrd.systemd = { initrd.systemd = {
enable = true; enable = true;
@ -11,12 +12,23 @@
extraBin.ip = "${pkgs.iproute}/bin/ip"; extraBin.ip = "${pkgs.iproute}/bin/ip";
extraBin.cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup"; extraBin.cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup";
users.root.shell = "${pkgs.bashInteractive}/bin/bash"; users.root.shell = "${pkgs.bashInteractive}/bin/bash";
storePaths = ["${pkgs.bashInteractive}/bin/bash"]; storePaths = [ "${pkgs.bashInteractive}/bin/bash" ];
}; };
initrd.availableKernelModules = ["xhci_pci" "nvme" "r8169" "usb_storage" "usbhid" "sd_mod" "rtsx_pci_sdmmc" "ahci" "uas" "tpm_crb"]; initrd.availableKernelModules = [
supportedFilesystems = ["ntfs"]; "xhci_pci"
kernelModules = ["kvm-intel"]; "nvme"
"r8169"
"usb_storage"
"usbhid"
"sd_mod"
"rtsx_pci_sdmmc"
"ahci"
"uas"
"tpm_crb"
];
supportedFilesystems = [ "ntfs" ];
kernelModules = [ "kvm-intel" ];
kernelParams = [ kernelParams = [
"rd.luks.options=timeout=0" "rd.luks.options=timeout=0"
"rootflags=x-systemd.device-timeout=0" "rootflags=x-systemd.device-timeout=0"

View file

@ -1,8 +1,5 @@
{ inputs, lib, ... }:
{ {
inputs,
lib,
...
}: {
imports = [ imports = [
./boot.nix ./boot.nix
./home-manager.nix ./home-manager.nix
@ -37,6 +34,6 @@
inputs.nixos-nftables-firewall.nixosModules.default inputs.nixos-nftables-firewall.nixosModules.default
inputs.nixvim.nixosModules.nixvim inputs.nixvim.nixosModules.nixvim
]; ];
age.identityPaths = ["/state/etc/ssh/ssh_host_ed25519_key"]; age.identityPaths = [ "/state/etc/ssh/ssh_host_ed25519_key" ];
boot.mode = lib.mkDefault "efi"; boot.mode = lib.mkDefault "efi";
} }

View file

@ -4,7 +4,8 @@
pkgs, pkgs,
nodes, nodes,
... ...
}: { }:
{
imports = [ imports = [
../../modules-hm/impermanence.nix ../../modules-hm/impermanence.nix
../../modules-hm/images.nix ../../modules-hm/images.nix
@ -18,9 +19,7 @@
spicePkgs = inputs.spicetify-nix.legacyPackages.${pkgs.system}; spicePkgs = inputs.spicetify-nix.legacyPackages.${pkgs.system};
}; };
sharedModules = [ sharedModules = [
{ { home.stateVersion = stateVersion; }
home.stateVersion = stateVersion;
}
inputs.nix-index-database.hmModules.nix-index inputs.nix-index-database.hmModules.nix-index
inputs.nixos-extra-modules.homeManagerModules.default inputs.nixos-extra-modules.homeManagerModules.default
inputs.nixvim.homeManagerModules.nixvim inputs.nixvim.homeManagerModules.nixvim
@ -38,5 +37,5 @@
# But still link all completions from all packages so they # But still link all completions from all packages so they
# can be found by zsh # can be found by zsh
environment.pathsToLink = ["/share/zsh"]; environment.pathsToLink = [ "/share/zsh" ];
} }

View file

@ -3,35 +3,23 @@
lib, lib,
pkgs, pkgs,
... ...
}: let }:
onlyHost = let
lib.mkIf (!config.boot.isContainer); onlyHost = lib.mkIf (!config.boot.isContainer);
prune = folder: prune =
pkgs.writers.writePython3Bin "impermanence-prune" {} '' folder:
pkgs.writers.writePython3Bin "impermanence-prune" { } ''
import os import os
import sys import sys
mounts = [${ mounts = [${
lib.concatStringsSep ", " lib.concatStringsSep ", " (
((map (x: (map (
"\"" x: "\"" + (if x.home != null then x.home + "/" else "") + x.directory + "\""
+ ( ) config.environment.persistence.${folder}.directories)
if x.home != null ++ (map (
then x.home + "/" x: "\"" + (if x.home != null then x.home + "/" else "") + x.file + "\""
else "" ) config.environment.persistence.${folder}.files)
) )
+ x.directory
+ "\"")
config.environment.persistence.${folder}.directories)
++ (map (x:
"\""
+ (
if x.home != null
then x.home + "/"
else ""
)
+ x.file
+ "\"")
config.environment.persistence.${folder}.files))
}] # noqa: E501 }] # noqa: E501
mounts = [os.path.normpath(x) for x in mounts] mounts = [os.path.normpath(x) for x in mounts]
mounts.sort() mounts.sort()
@ -53,11 +41,10 @@
file=sys.stderr) file=sys.stderr)
print("\n".join(erg)) print("\n".join(erg))
''; '';
in { in
{
# to allow all users to access hm managed persistent folders # to allow all users to access hm managed persistent folders
lib.scripts.impermanence.pruneScripts = lib.scripts.impermanence.pruneScripts = lib.mapAttrs (k: _: prune k) config.environment.persistence;
lib.mapAttrs (k: _: prune k)
config.environment.persistence;
programs.fuse.userAllowOther = true; programs.fuse.userAllowOther = true;
services.openssh.hostKeys = lib.mkForce [ services.openssh.hostKeys = lib.mkForce [
{ {
@ -68,15 +55,10 @@ in {
environment.persistence."/state" = { environment.persistence."/state" = {
hideMounts = true; hideMounts = true;
files = files = [
[
"/etc/ssh/ssh_host_ed25519_key" "/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub" "/etc/ssh/ssh_host_ed25519_key.pub"
] ] ++ lib.lists.optionals (!config.boot.isContainer) [ "/etc/machine-id" ];
++ lib.lists.optionals (!config.boot.isContainer)
[
"/etc/machine-id"
];
directories = [ directories = [
"/var/log" "/var/log"
"/var/lib/systemd" "/var/lib/systemd"
@ -93,18 +75,16 @@ in {
}; };
environment.persistence."/persist" = { environment.persistence."/persist" = {
hideMounts = true; hideMounts = true;
directories = []; directories = [ ];
}; };
fileSystems."/persist".neededForBoot = true; fileSystems."/persist".neededForBoot = true;
fileSystems."/state".neededForBoot = true; fileSystems."/state".neededForBoot = true;
# After importing the rpool, rollback the root system to be empty. # After importing the rpool, rollback the root system to be empty.
boot.initrd.systemd.services.impermanence-root = boot.initrd.systemd.services.impermanence-root = onlyHost {
onlyHost wantedBy = [ "initrd.target" ];
{ after = [ "zfs-import-rpool.service" ];
wantedBy = ["initrd.target"]; before = [ "sysroot.mount" ];
after = ["zfs-import-rpool.service"];
before = ["sysroot.mount"];
unitConfig.DefaultDependencies = "no"; unitConfig.DefaultDependencies = "no";
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";

View file

@ -1,17 +1,13 @@
{ lib, config, ... }:
{ {
lib,
config,
...
}: {
networking = { networking = {
useNetworkd = true; useNetworkd = true;
dhcpcd.enable = false; dhcpcd.enable = false;
useDHCP = false; useDHCP = false;
# allow mdns port # allow mdns port
firewall.allowedUDPPorts = [5353]; firewall.allowedUDPPorts = [ 5353 ];
renameInterfacesByMac = lib.mkIf (!config.boot.isContainer) ( renameInterfacesByMac = lib.mkIf (!config.boot.isContainer) (
lib.mapAttrs (_: v: v.mac) lib.mapAttrs (_: v: v.mac) (config.secrets.secrets.local.networking.interfaces or { })
(config.secrets.secrets.local.networking.interfaces or {})
); );
}; };
systemd.network = { systemd.network = {
@ -19,8 +15,8 @@
wait-online.anyInterface = true; wait-online.anyInterface = true;
}; };
system.nssDatabases.hosts = lib.mkMerge [ system.nssDatabases.hosts = lib.mkMerge [
(lib.mkBefore ["mdns_minimal [NOTFOUND=return]"]) (lib.mkBefore [ "mdns_minimal [NOTFOUND=return]" ])
(lib.mkAfter ["mdns"]) (lib.mkAfter [ "mdns" ])
]; ];
services.resolved = { services.resolved = {
enable = true; enable = true;

View file

@ -1,8 +1,5 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
networking.nftables = { networking.nftables = {
stopRuleset = lib.mkDefault '' stopRuleset = lib.mkDefault ''
table inet filter { table inet filter {
@ -36,20 +33,31 @@
nnf-ssh.enable = true; nnf-ssh.enable = true;
nnf-icmp = { nnf-icmp = {
enable = true; enable = true;
ipv6Types = ["echo-request" "destination-unreachable" "packet-too-big" "time-exceeded" "parameter-problem" "nd-router-advert" "nd-neighbor-solicit" "nd-neighbor-advert"]; ipv6Types = [
ipv4Types = ["echo-request" "destination-unreachable" "router-advertisement" "time-exceeded" "parameter-problem"]; "echo-request"
"destination-unreachable"
"packet-too-big"
"time-exceeded"
"parameter-problem"
"nd-router-advert"
"nd-neighbor-solicit"
"nd-neighbor-advert"
];
ipv4Types = [
"echo-request"
"destination-unreachable"
"router-advertisement"
"time-exceeded"
"parameter-problem"
];
}; };
}; };
rules.untrusted-to-local = { rules.untrusted-to-local = {
from = ["untrusted"]; from = [ "untrusted" ];
to = ["local"]; to = [ "local" ];
inherit inherit (config.networking.firewall) allowedTCPPorts allowedUDPPorts;
(config.networking.firewall)
allowedTCPPorts
allowedUDPPorts
;
}; };
}; };
}; };

View file

@ -1,14 +1,15 @@
{ inputs, stateVersion, ... }:
{ {
inputs,
stateVersion,
...
}: {
nix = { nix = {
settings = { settings = {
auto-optimise-store = true; auto-optimise-store = true;
allowed-users = ["@wheel"]; allowed-users = [ "@wheel" ];
trusted-users = ["root"]; trusted-users = [ "root" ];
system-features = ["recursive-nix" "repl-flake" "big-parallel"]; system-features = [
"recursive-nix"
"repl-flake"
"big-parallel"
];
substituters = [ substituters = [
"https://nix-community.cachix.org" "https://nix-community.cachix.org"
"https://cache.nixos.org" "https://cache.nixos.org"
@ -24,7 +25,7 @@
cores = 0; cores = 0;
max-jobs = "auto"; max-jobs = "auto";
# make agenix rekey find the secrets even without trusted user # make agenix rekey find the secrets even without trusted user
extra-sandbox-paths = ["/var/tmp/agenix-rekey?"]; extra-sandbox-paths = [ "/var/tmp/agenix-rekey?" ];
}; };
daemonCPUSchedPolicy = "batch"; daemonCPUSchedPolicy = "batch";
daemonIOSchedPriority = 5; daemonIOSchedPriority = 5;
@ -34,7 +35,7 @@
experimental-features = nix-command flakes recursive-nix experimental-features = nix-command flakes recursive-nix
flake-registry = /etc/nix/registry.json flake-registry = /etc/nix/registry.json
''; '';
nixPath = ["nixpkgs=/run/current-system/nixpkgs"]; nixPath = [ "nixpkgs=/run/current-system/nixpkgs" ];
optimise.automatic = true; optimise.automatic = true;
gc = { gc = {
automatic = true; automatic = true;

View file

@ -1,4 +1,5 @@
{lib, ...}: { { lib, ... }:
{
# Enable the OpenSSH daemon. # Enable the OpenSSH daemon.
services.openssh = { services.openssh = {
enable = true; enable = true;

View file

@ -5,24 +5,24 @@
pkgs, pkgs,
config, config,
... ...
}: { }:
{
system.stateVersion = stateVersion; system.stateVersion = stateVersion;
age.rekey = { age.rekey = {
inherit inherit (inputs.self.secretsConfig) masterIdentities extraEncryptionPubkeys;
(inputs.self.secretsConfig)
masterIdentities
extraEncryptionPubkeys
;
storageMode = "derivation"; storageMode = "derivation";
forceRekeyOnSystem = builtins.extraBuiltins.unsafeCurrentSystem; forceRekeyOnSystem = builtins.extraBuiltins.unsafeCurrentSystem;
hostPubkey = let hostPubkey =
let
pubkeyPath = config.node.secretsDir + "/host.pub"; pubkeyPath = config.node.secretsDir + "/host.pub";
in in
lib.mkIf (lib.pathExists pubkeyPath || lib.trace "Missing pubkey for ${config.node.name}: ${toString pubkeyPath} not found, using dummy replacement key for now." false) lib.mkIf (
pubkeyPath; lib.pathExists pubkeyPath
|| lib.trace "Missing pubkey for ${config.node.name}: ${toString pubkeyPath} not found, using dummy replacement key for now." false
) pubkeyPath;
generatedSecretsDir = config.node.secretsDir + "/generated/"; generatedSecretsDir = config.node.secretsDir + "/generated/";
cacheDir = "/var/tmp/agenix-rekey/\"$UID\""; cacheDir = "/var/tmp/agenix-rekey/\"$UID\"";
}; };
@ -38,16 +38,16 @@
# to create a link called /run/agenix. Agenix should probably fail in this case, # to create a link called /run/agenix. Agenix should probably fail in this case,
# but doesn't and instead puts the generation link into the existing directory. # but doesn't and instead puts the generation link into the existing directory.
# TODO See https://github.com/ryantm/agenix/pull/187. # TODO See https://github.com/ryantm/agenix/pull/187.
system.activationScripts = lib.mkIf (config.age.secrets != {}) { system.activationScripts = lib.mkIf (config.age.secrets != { }) {
removeAgenixLink.text = "[[ ! -L /run/agenix ]] && [[ -d /run/agenix ]] && rm -rf /run/agenix"; removeAgenixLink.text = "[[ ! -L /run/agenix ]] && [[ -d /run/agenix ]] && rm -rf /run/agenix";
agenixNewGeneration.deps = ["removeAgenixLink"]; agenixNewGeneration.deps = [ "removeAgenixLink" ];
}; };
time.timeZone = lib.mkDefault "Europe/Berlin"; time.timeZone = lib.mkDefault "Europe/Berlin";
i18n.defaultLocale = "C.UTF-8"; i18n.defaultLocale = "C.UTF-8";
console = { console = {
font = "${pkgs.terminus_font}/share/consolefonts/ter-v28n.psf.gz"; font = "${pkgs.terminus_font}/share/consolefonts/ter-v28n.psf.gz";
packages = with pkgs; [terminus_font]; packages = with pkgs; [ terminus_font ];
useXkbConfig = true; # use xkbOptions in tty. useXkbConfig = true; # use xkbOptions in tty.
keyMap = lib.mkDefault "de-latin1-nodeadkeys"; keyMap = lib.mkDefault "de-latin1-nodeadkeys";
}; };
@ -71,11 +71,12 @@
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
secrets.secretFiles = let secrets.secretFiles =
let
local = config.node.secretsDir + "/secrets.nix.age"; local = config.node.secretsDir + "/secrets.nix.age";
in in
{ {
global = ../../secrets/secrets.nix.age; global = ../../secrets/secrets.nix.age;
} }
// lib.optionalAttrs (config.node.name != null && lib.pathExists local) {inherit local;}; // lib.optionalAttrs (config.node.name != null && lib.pathExists local) { inherit local; };
} }

View file

@ -1,11 +1,13 @@
{ {
users.mutableUsers = false; users.mutableUsers = false;
users.deterministicIds = let users.deterministicIds =
let
uidGid = id: { uidGid = id: {
uid = id; uid = id;
gid = id; gid = id;
}; };
in { in
{
nscd = uidGid 201; nscd = uidGid 201;
sshd = uidGid 202; sshd = uidGid 202;
tss = uidGid 203; tss = uidGid 203;

View file

@ -1,10 +1,11 @@
{pkgs, ...}: { { pkgs, ... }:
environment.systemPackages = with pkgs; [bluetuith]; {
environment.systemPackages = with pkgs; [ bluetuith ];
hardware.bluetooth = { hardware.bluetooth = {
enable = true; enable = true;
powerOnBoot = false; powerOnBoot = false;
disabledPlugins = ["sap"]; disabledPlugins = [ "sap" ];
settings = { settings = {
General = { General = {
FastConnectable = "true"; FastConnectable = "true";
@ -16,15 +17,13 @@
}; };
hardware.pulseaudio = { hardware.pulseaudio = {
package = pkgs.pulseaudio.override {bluetoothSupport = true;}; package = pkgs.pulseaudio.override { bluetoothSupport = true; };
extraConfig = '' extraConfig = ''
load-module module-bluetooth-discover load-module module-bluetooth-discover
load-module module-bluetooth-policy load-module module-bluetooth-policy
load-module module-switch-on-connect load-module module-switch-on-connect
''; '';
extraModules = with pkgs; [pulseaudio-modules-bt]; extraModules = with pkgs; [ pulseaudio-modules-bt ];
}; };
environment.persistence."/state".directories = [ environment.persistence."/state".directories = [ "/var/lib/bluetooth" ];
"/var/lib/bluetooth"
];
} }

View file

@ -1,3 +1 @@
{ { services.joycond.enable = true; }
services.joycond.enable = true;
}

View file

@ -5,7 +5,7 @@
... ...
}: }:
lib.optionalAttrs (!minimal) { lib.optionalAttrs (!minimal) {
services.xserver.videoDrivers = lib.mkForce ["nvidia"]; services.xserver.videoDrivers = lib.mkForce [ "nvidia" ];
hardware = { hardware = {
graphics = { graphics = {

View file

@ -1,5 +1,6 @@
# Configuration for actual physical machines # Configuration for actual physical machines
{config, ...}: { { config, ... }:
{
hardware = { hardware = {
enableRedistributableFirmware = true; enableRedistributableFirmware = true;
enableAllFirmware = true; enableAllFirmware = true;
@ -8,6 +9,6 @@
services = { services = {
fwupd.enable = true; fwupd.enable = true;
smartd.enable = true; smartd.enable = true;
thermald.enable = builtins.elem config.nixpkgs.hostPlatform.system ["x86_64-linux"]; thermald.enable = builtins.elem config.nixpkgs.hostPlatform.system [ "x86_64-linux" ];
}; };
} }

View file

@ -13,7 +13,10 @@ lib.optionalAttrs (!minimal) {
# packages = pkgs.linuxPackages_6_6_rt; # packages = pkgs.linuxPackages_6_6_rt;
# }; # };
#}; #};
environment.systemPackages = with pkgs; [pulseaudio pulsemixer]; environment.systemPackages = with pkgs; [
pulseaudio
pulsemixer
];
hardware.pulseaudio.enable = lib.mkForce false; hardware.pulseaudio.enable = lib.mkForce false;
security.rtkit.enable = true; security.rtkit.enable = true;

View file

@ -1,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
yubikey-personalization yubikey-personalization
yubikey-manager yubikey-manager
@ -7,5 +8,8 @@
services.pcscd.enable = true; services.pcscd.enable = true;
services.udev.packages = with pkgs; [yubikey-personalization libu2f-host]; services.udev.packages = with pkgs; [
yubikey-personalization
libu2f-host
];
} }

View file

@ -4,13 +4,11 @@
pkgs, pkgs,
lib, lib,
... ...
}: let }:
inherit let
(lib) inherit (lib) mkOption types;
mkOption in
types {
;
in {
options.hidpi = mkOption { options.hidpi = mkOption {
default = false; default = false;
type = types.bool; type = types.bool;
@ -18,14 +16,10 @@ in {
}; };
# stylix acceses stylix options on import meaning you can only import this module when you're actually setting stylix options # stylix acceses stylix options on import meaning you can only import this module when you're actually setting stylix options
imports = [ imports = [ inputs.stylix.nixosModules.stylix ];
inputs.stylix.nixosModules.stylix
];
config = { config = {
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [ xdg-utils ];
xdg-utils
];
xdg.portal = { xdg.portal = {
xdgOpenUsePortal = true; xdgOpenUsePortal = true;
enable = true; enable = true;
@ -38,13 +32,11 @@ in {
"gtk" "gtk"
"hyprland" "hyprland"
]; ];
sway.default = [ sway.default = [ "wlr" ];
"wlr"
];
}; };
}; };
# needed for gnome pinentry # needed for gnome pinentry
services.dbus.packages = [pkgs.gcr]; services.dbus.packages = [ pkgs.gcr ];
fonts = { fonts = {
enableGhostscriptFonts = false; enableGhostscriptFonts = false;
fontDir.enable = false; fontDir.enable = false;
@ -75,7 +67,7 @@ in {
''; '';
}; };
packages = with pkgs; [ packages = with pkgs; [
(nerdfonts.override {fonts = ["FiraCode"];}) (nerdfonts.override { fonts = [ "FiraCode" ]; })
ibm-plex ibm-plex
dejavu_fonts dejavu_fonts
unifont unifont
@ -160,20 +152,19 @@ in {
}; };
home-manager.sharedModules = [ home-manager.sharedModules = [
({ (
{
pkgs, pkgs,
config, config,
nixosConfig, nixosConfig,
... ...
}: { }:
{
stylix = { stylix = {
cursor = { cursor = {
package = pkgs.openzone-cursors; package = pkgs.openzone-cursors;
name = "OpenZone_White_Slim"; name = "OpenZone_White_Slim";
size = size = if nixosConfig.hidpi then 48 else 18;
if nixosConfig.hidpi
then 48
else 18;
}; };
inherit (nixosConfig.stylix) polarity; inherit (nixosConfig.stylix) polarity;
targets = { targets = {
@ -194,7 +185,8 @@ in {
"Xft.rgba" = "rgb"; "Xft.rgba" = "rgb";
}; };
gtk = let gtk =
let
gtk34extraConfig = { gtk34extraConfig = {
gtk-application-prefer-dark-theme = 1; gtk-application-prefer-dark-theme = 1;
gtk-cursor-theme-size = 18; gtk-cursor-theme-size = 18;
@ -205,7 +197,8 @@ in {
gtk-xft-hintstyle = "hintfull"; gtk-xft-hintstyle = "hintfull";
gtk-xft-rgba = "rgb"; gtk-xft-rgba = "rgb";
}; };
in { in
{
enable = true; enable = true;
iconTheme = { iconTheme = {
name = "Vimix-Doder"; name = "Vimix-Doder";
@ -224,7 +217,8 @@ in {
platformTheme.name = "adwaita"; platformTheme.name = "adwaita";
style.name = "Adwaita-Dark"; style.name = "Adwaita-Dark";
}; };
}) }
)
]; ];
}; };
} }

View file

@ -1,8 +1,5 @@
{ config, pkgs, ... }:
{ {
config,
pkgs,
...
}: {
age.secrets.initrd_host_ed25519_key.generator.script = "ssh-ed25519"; age.secrets.initrd_host_ed25519_key.generator.script = "ssh-ed25519";
boot.initrd.network.enable = true; boot.initrd.network.enable = true;
@ -14,7 +11,7 @@
# need two activations to change as well as that to enable this # need two activations to change as well as that to enable this
# module you need to set hostKeys to a dummy value and generate # module you need to set hostKeys to a dummy value and generate
# and invalid initrd once # and invalid initrd once
hostKeys = [config.age.secrets.initrd_host_ed25519_key.path]; hostKeys = [ config.age.secrets.initrd_host_ed25519_key.path ];
}; };
# Make sure that there is always a valid initrd hostkey available that can be installed into # Make sure that there is always a valid initrd hostkey available that can be installed into
@ -30,7 +27,10 @@
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f "${config.age.secrets.initrd_host_ed25519_key.path}" ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f "${config.age.secrets.initrd_host_ed25519_key.path}"
fi fi
''; '';
deps = ["agenixInstall" "users"]; deps = [
"agenixInstall"
"users"
];
}; };
system.activationScripts.agenixChown.deps = ["agenixEnsureInitrdHostkey"]; system.activationScripts.agenixChown.deps = [ "agenixEnsureInitrdHostkey" ];
} }

View file

@ -1,7 +1,11 @@
{pkgs, ...}: { { pkgs, ... }:
{
services.printing = { services.printing = {
enable = true; enable = true;
drivers = [pkgs.hplipWithPlugin pkgs.hplip]; drivers = [
pkgs.hplipWithPlugin
pkgs.hplip
];
}; };
environment.persistence."/state".directories = [ environment.persistence."/state".directories = [
{ {

View file

@ -8,10 +8,7 @@
lib.optionalAttrs (!minimal) { lib.optionalAttrs (!minimal) {
environment.systemPackages = [ environment.systemPackages = [
# For debugging and troubleshooting Secure Boot. # For debugging and troubleshooting Secure Boot.
(pkgs.sbctl.override (pkgs.sbctl.override { databasePath = "/run/secureboot"; })
{
databasePath = "/run/secureboot";
})
]; ];
age.secrets.secureboot.rekeyFile = ../../hosts/${config.node.name}/secrets/secureboot.tar.age; age.secrets.secureboot.rekeyFile = ../../hosts/${config.node.name}/secrets/secureboot.tar.age;
system.activationScripts.securebootuntar = { system.activationScripts.securebootuntar = {
@ -21,7 +18,7 @@ lib.optionalAttrs (!minimal) {
chmod 700 /run/secureboot chmod 700 /run/secureboot
${pkgs.gnutar}/bin/tar xf ${config.age.secrets.secureboot.path} -C /run/secureboot || true ${pkgs.gnutar}/bin/tar xf ${config.age.secrets.secureboot.path} -C /run/secureboot || true
''; '';
deps = ["agenix"]; deps = [ "agenix" ];
}; };
# Lanzaboote currently replaces the systemd-boot module. # Lanzaboote currently replaces the systemd-boot module.

View file

@ -8,8 +8,8 @@ lib.optionalAttrs (!minimal) {
programs.steam = { programs.steam = {
enable = true; enable = true;
package = pkgs.steam.override { package = pkgs.steam.override {
extraPkgs = pkgs: extraPkgs =
with pkgs; [ pkgs: with pkgs; [
# vampir überlebende braucht diese pkgs # vampir überlebende braucht diese pkgs
libgdiplus libgdiplus
cups cups

View file

@ -14,10 +14,10 @@ lib.optionalAttrs (!minimal) {
enable = true; enable = true;
xdgOpenUsePortal = true; xdgOpenUsePortal = true;
config.common = { config.common = {
"org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; "org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
"org.freedesktop.impl.portal.ScreenCast" = ["hyprland"]; "org.freedesktop.impl.portal.ScreenCast" = [ "hyprland" ];
"org.freedesktop.impl.portal.Screenshot" = ["hyprland"]; "org.freedesktop.impl.portal.Screenshot" = [ "hyprland" ];
"org.freedesktop.portal.FileChooser" = ["xdg-desktop-portal-gtk"]; "org.freedesktop.portal.FileChooser" = [ "xdg-desktop-portal-gtk" ];
}; };
extraPortals = [ extraPortals = [
pkgs.xdg-desktop-portal-hyprland pkgs.xdg-desktop-portal-hyprland

View file

@ -12,7 +12,7 @@ lib.optionalAttrs (!minimal) {
displayManager.startx.enable = true; displayManager.startx.enable = true;
autoRepeatDelay = 235; autoRepeatDelay = 235;
autoRepeatInterval = 60; autoRepeatInterval = 60;
videoDrivers = ["modesetting"]; videoDrivers = [ "modesetting" ];
}; };
services.libinput = { services.libinput = {
enable = true; enable = true;
@ -28,10 +28,9 @@ lib.optionalAttrs (!minimal) {
disableWhileTyping = true; disableWhileTyping = true;
}; };
}; };
services.udev.extraRules = let services.udev.extraRules =
exe = let
pkgs.writeShellScript "set-key-repeat" exe = pkgs.writeShellScript "set-key-repeat" ''
''
if [ -d "/tmp/.X11-unix" ]; then if [ -d "/tmp/.X11-unix" ]; then
for D in /tmp/.X11-unix/*; do for D in /tmp/.X11-unix/*; do
file=$(${pkgs.coreutils}/bin/basename $D) file=$(${pkgs.coreutils}/bin/basename $D)
@ -43,7 +42,8 @@ lib.optionalAttrs (!minimal) {
done done
fi fi
''; '';
in '' in
''
ACTION=="add", SUBSYSTEM=="input", ATTRS{bInterfaceClass}=="03", RUN+="${exe}" ACTION=="add", SUBSYSTEM=="input", ATTRS{bInterfaceClass}=="03", RUN+="${exe}"
''; '';
} }

View file

@ -3,15 +3,16 @@
config, config,
lib, lib,
... ...
}: { }:
boot.supportedFilesystems = ["zfs"]; {
boot.supportedFilesystems = [ "zfs" ];
boot.kernelPackages = lib.mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages; boot.kernelPackages = lib.mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages;
# The root pool should never be imported forcefully. # The root pool should never be imported forcefully.
# Failure to import is important to notice! # Failure to import is important to notice!
boot.zfs.forceImportRoot = false; boot.zfs.forceImportRoot = false;
environment.systemPackages = with pkgs; [zfs]; environment.systemPackages = with pkgs; [ zfs ];
# Might help with hangs mainly atuin # Might help with hangs mainly atuin
#boot.kernelPatches = [ #boot.kernelPatches = [
@ -40,5 +41,5 @@
}; };
}; };
# TODO remove once this is upstreamed # TODO remove once this is upstreamed
boot.initrd.systemd.services."zfs-import-rpool".after = ["cryptsetup.target"]; boot.initrd.systemd.services."zfs-import-rpool".after = [ "cryptsetup.target" ];
} }

View file

@ -1,16 +1,12 @@
{ {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
imports = [../actual.nix]; imports = [ ../actual.nix ];
services.actual = { services.actual = {
enable = true; enable = true;
settings.port = 3000; settings.port = 3000;
}; };
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [ { directory = "/var/lib/private/actual"; } ];
{
directory = "/var/lib/private/actual";
}
];
} }

View file

@ -1,11 +1,8 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.adguardhome.port]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.adguardhome.port ];
}; };
services.adguardhome = { services.adguardhome = {
enable = true; enable = true;
@ -16,8 +13,12 @@
settings = { settings = {
dns = { dns = {
bind_hosts = [ bind_hosts = [
(lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name} config.secrets.secrets.global.net.privateSubnetv4) (lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name}
(lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name} config.secrets.secrets.global.net.privateSubnetv6) config.secrets.secrets.global.net.privateSubnetv4
)
(lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name}
config.secrets.secrets.global.net.privateSubnetv6
)
]; ];
anonymize_client_ip = false; anonymize_client_ip = false;
upstream_dns = [ upstream_dns = [
@ -61,8 +62,8 @@
}; };
}; };
networking.firewall = { networking.firewall = {
allowedTCPPorts = [53]; allowedTCPPorts = [ 53 ];
allowedUDPPorts = [53]; allowedUDPPorts = [ 53 ];
}; };
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
{ {

View file

@ -1,4 +1,5 @@
{config, ...}: { { config, ... }:
{
age.secrets.cloudflare_token_dns = { age.secrets.cloudflare_token_dns = {
rekeyFile = config.node.secretsDir + "/cloudflare_api_token.age"; rekeyFile = config.node.secretsDir + "/cloudflare_api_token.age";
mode = "440"; mode = "440";
@ -13,6 +14,6 @@
usev4 = "webv4, webv4='https://cloudflare.com/cdn-cgi/trace', webv4-skip='ip='"; usev4 = "webv4, webv4='https://cloudflare.com/cdn-cgi/trace', webv4-skip='ip='";
usev6 = ""; usev6 = "";
passwordFile = config.age.secrets.cloudflare_token_dns.path; passwordFile = config.age.secrets.cloudflare_token_dns.path;
domains = [config.secrets.secrets.global.domains.web]; domains = [ config.secrets.secrets.global.domains.web ];
}; };
} }

View file

@ -1,12 +1,9 @@
{ config, nodes, ... }:
{ {
config, i18n.supportedLocales = [ "all" ];
nodes,
...
}: {
i18n.supportedLocales = ["all"];
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [80]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ];
}; };
age.secrets.appKey = { age.secrets.appKey = {

View file

@ -4,9 +4,11 @@
pkgs, pkgs,
lib, lib,
... ...
}: let }:
let
forgejoDomain = "forge.${config.secrets.secrets.global.domains.web}"; forgejoDomain = "forge.${config.secrets.secrets.global.domains.web}";
in { in
{
age.secrets.resticpasswd = { age.secrets.resticpasswd = {
generator.script = "alnum"; generator.script = "alnum";
}; };
@ -29,7 +31,7 @@ in {
inherit (config.secrets.secrets.global.hetzner.users.forgejo) subUid path; inherit (config.secrets.secrets.global.hetzner.users.forgejo) subUid path;
sshAgeSecret = "forgejoHetznerSsh"; sshAgeSecret = "forgejoHetznerSsh";
}; };
paths = [config.services.forgejo.stateDir]; paths = [ config.services.forgejo.stateDir ];
pruneOpts = [ pruneOpts = [
"--keep-daily 10" "--keep-daily 10"
"--keep-weekly 7" "--keep-weekly 7"
@ -42,7 +44,7 @@ in {
# Recommended by forgejo: https://forgejo.org/docs/latest/admin/recommendations/#git-over-ssh # Recommended by forgejo: https://forgejo.org/docs/latest/admin/recommendations/#git-over-ssh
services.openssh.settings.AcceptEnv = "GIT_PROTOCOL"; services.openssh.settings.AcceptEnv = "GIT_PROTOCOL";
users.groups.git = {}; users.groups.git = { };
users.users.git = { users.users.git = {
isSystemUser = true; isSystemUser = true;
useDefaultShell = true; useDefaultShell = true;
@ -52,9 +54,11 @@ in {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.forgejo.settings.server.HTTP_PORT]; firewallRuleForNode.elisabeth.allowedTCPPorts = [
config.services.forgejo.settings.server.HTTP_PORT
];
}; };
networking.firewall.allowedTCPPorts = [config.services.forgejo.settings.server.SSH_PORT]; networking.firewall.allowedTCPPorts = [ config.services.forgejo.settings.server.SSH_PORT ];
environment.persistence."/panzer".directories = [ environment.persistence."/panzer".directories = [
{ {
@ -145,7 +149,8 @@ in {
# see https://github.com/go-gitea/gitea/issues/21376. # see https://github.com/go-gitea/gitea/issues/21376.
systemd.services.forgejo = { systemd.services.forgejo = {
serviceConfig.RestartSec = "60"; # Retry every minute serviceConfig.RestartSec = "60"; # Retry every minute
preStart = let preStart =
let
exe = lib.getExe config.services.forgejo.package; exe = lib.getExe config.services.forgejo.package;
providerName = "kanidm"; providerName = "kanidm";
clientId = "forgejo"; clientId = "forgejo";

View file

@ -1,8 +1,8 @@
{ {
imports = [../../modules/homebox.nix]; imports = [ ../../modules/homebox.nix ];
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
services.homebox = { services.homebox = {
enable = true; enable = true;

View file

@ -4,7 +4,8 @@
nodes, nodes,
config, config,
... ...
}: let }:
let
version = "v1.106.4"; version = "v1.106.4";
immichDomain = "immich.${config.secrets.secrets.global.domains.web}"; immichDomain = "immich.${config.secrets.secrets.global.domains.web}";
@ -136,21 +137,14 @@
serviceConfig = { serviceConfig = {
Restart = "always"; Restart = "always";
}; };
after = [ after = [ "podman-network-immich-default.service" ];
"podman-network-immich-default.service" requires = [ "podman-network-immich-default.service" ];
]; partOf = [ "podman-compose-immich-root.target" ];
requires = [ wantedBy = [ "podman-compose-immich-root.target" ];
"podman-network-immich-default.service"
];
partOf = [
"podman-compose-immich-root.target"
];
wantedBy = [
"podman-compose-immich-root.target"
];
}; };
processedConfigFile = "/run/agenix/immich.config.json"; processedConfigFile = "/run/agenix/immich.config.json";
in { in
{
age.secrets.resticpasswd = { age.secrets.resticpasswd = {
generator.script = "alnum"; generator.script = "alnum";
}; };
@ -206,7 +200,7 @@ in {
system.activationScripts.agenixRooterDerivedSecrets = { system.activationScripts.agenixRooterDerivedSecrets = {
# Run after agenix has generated secrets # Run after agenix has generated secrets
deps = ["agenix"]; deps = [ "agenix" ];
text = '' text = ''
immichClientSecret=$(< ${config.age.secrets.immich-oauth2-client-secret.path}) immichClientSecret=$(< ${config.age.secrets.immich-oauth2-client-secret.path})
${pkgs.jq}/bin/jq --arg immichClientSecret "$immichClientSecret" '.oauth.clientSecret = $immichClientSecret' ${configFile} > ${processedConfigFile} ${pkgs.jq}/bin/jq --arg immichClientSecret "$immichClientSecret" '.oauth.clientSecret = $immichClientSecret' ${configFile} > ${processedConfigFile}
@ -221,11 +215,11 @@ in {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
networking.nftables.chains.forward.into-immich-container = { networking.nftables.chains.forward.into-immich-container = {
after = ["conntrack"]; after = [ "conntrack" ];
rules = [ rules = [
"iifname elisabeth ip saddr ${nodes.elisabeth.config.wireguard.elisabeth.ipv4} tcp dport 3001 accept" "iifname elisabeth ip saddr ${nodes.elisabeth.config.wireguard.elisabeth.ipv4} tcp dport 3001 accept"
"iifname podman1 oifname lan accept" "iifname podman1 oifname lan accept"
@ -313,9 +307,7 @@ in {
"${upload_folder}:/usr/src/app/upload:rw" "${upload_folder}:/usr/src/app/upload:rw"
"${environment.DB_PASSWORD_FILE}:${environment.DB_PASSWORD_FILE}:ro" "${environment.DB_PASSWORD_FILE}:${environment.DB_PASSWORD_FILE}:ro"
]; ];
ports = [ ports = [ "3000:3001/tcp" ];
"3000:3001/tcp"
];
dependsOn = [ dependsOn = [
"immich_postgres" "immich_postgres"
"immich_redis" "immich_redis"
@ -327,9 +319,7 @@ in {
"--ip=${ipImmichServer}" "--ip=${ipImmichServer}"
]; ];
}; };
systemd.services."podman-immich_server" = systemd.services."podman-immich_server" = serviceConfig // {
serviceConfig
// {
unitConfig.UpheldBy = [ unitConfig.UpheldBy = [
"podman-immich_postgres.service" "podman-immich_postgres.service"
"podman-immich_redis.service" "podman-immich_redis.service"
@ -338,7 +328,7 @@ in {
# Networks # Networks
systemd.services."podman-network-immich-default" = { systemd.services."podman-network-immich-default" = {
path = [pkgs.podman]; path = [ pkgs.podman ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
@ -347,8 +337,8 @@ in {
script = '' script = ''
podman network inspect immich-default || podman network create immich-default --opt isolate=true --disable-dns --subnet=10.89.0.0/24 podman network inspect immich-default || podman network create immich-default --opt isolate=true --disable-dns --subnet=10.89.0.0/24
''; '';
partOf = ["podman-compose-immich-root.target"]; partOf = [ "podman-compose-immich-root.target" ];
wantedBy = ["podman-compose-immich-root.target"]; wantedBy = [ "podman-compose-immich-root.target" ];
}; };
# Root service # Root service
@ -358,6 +348,6 @@ in {
unitConfig = { unitConfig = {
Description = "Root target generated by compose2nix."; Description = "Root target generated by compose2nix.";
}; };
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
}; };
} }

View file

@ -1,12 +1,14 @@
{config, ...}: let { config, ... }:
let
kanidmdomain = "auth.${config.secrets.secrets.global.domains.web}"; kanidmdomain = "auth.${config.secrets.secrets.global.domains.web}";
in { in
imports = [../../modules/kanidm.nix]; {
imports = [ ../../modules/kanidm.nix ];
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
disabledModules = ["services/security/kanidm.nix"]; disabledModules = [ "services/security/kanidm.nix" ];
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
{ {
directory = "/var/lib/kanidm"; directory = "/var/lib/kanidm";
@ -74,42 +76,50 @@ in {
inherit (config.secrets.secrets.local.kanidm) persons; inherit (config.secrets.secrets.local.kanidm) persons;
groups."paperless.access" = { groups."paperless.access" = {
members = ["paperless.admins"]; members = [ "paperless.admins" ];
}; };
# currently not usable # currently not usable
groups."paperless.admins" = { groups."paperless.admins" = {
members = ["administrator"]; members = [ "administrator" ];
}; };
systems.oauth2.paperless = { systems.oauth2.paperless = {
displayName = "paperless"; displayName = "paperless";
originUrl = "https://ppl.${config.secrets.secrets.global.domains.web}/"; originUrl = "https://ppl.${config.secrets.secrets.global.domains.web}/";
basicSecretFile = config.age.secrets.oauth2-paperless.path; basicSecretFile = config.age.secrets.oauth2-paperless.path;
scopeMaps."paperless.access" = ["openid" "email" "profile"]; scopeMaps."paperless.access" = [
"openid"
"email"
"profile"
];
preferShortUsername = true; preferShortUsername = true;
}; };
groups."nextcloud.access" = { groups."nextcloud.access" = {
members = ["nextcloud.admins"]; members = [ "nextcloud.admins" ];
}; };
# currently not usable # currently not usable
groups."nextcloud.admins" = { groups."nextcloud.admins" = {
members = ["administrator"]; members = [ "administrator" ];
}; };
systems.oauth2.nextcloud = { systems.oauth2.nextcloud = {
displayName = "nextcloud"; displayName = "nextcloud";
originUrl = "https://nc.${config.secrets.secrets.global.domains.web}/"; originUrl = "https://nc.${config.secrets.secrets.global.domains.web}/";
basicSecretFile = config.age.secrets.oauth2-nextcloud.path; basicSecretFile = config.age.secrets.oauth2-nextcloud.path;
allowInsecureClientDisablePkce = true; allowInsecureClientDisablePkce = true;
scopeMaps."nextcloud.access" = ["openid" "email" "profile"]; scopeMaps."nextcloud.access" = [
"openid"
"email"
"profile"
];
preferShortUsername = true; preferShortUsername = true;
}; };
groups."immich.access" = { groups."immich.access" = {
members = ["immich.admins"]; members = [ "immich.admins" ];
}; };
# currently not usable # currently not usable
groups."immich.admins" = { groups."immich.admins" = {
members = ["administrator"]; members = [ "administrator" ];
}; };
systems.oauth2.immich = { systems.oauth2.immich = {
displayName = "Immich"; displayName = "Immich";
@ -117,57 +127,84 @@ in {
basicSecretFile = config.age.secrets.oauth2-immich.path; basicSecretFile = config.age.secrets.oauth2-immich.path;
allowInsecureClientDisablePkce = true; allowInsecureClientDisablePkce = true;
enableLegacyCrypto = true; enableLegacyCrypto = true;
scopeMaps."immich.access" = ["openid" "email" "profile"]; scopeMaps."immich.access" = [
"openid"
"email"
"profile"
];
preferShortUsername = true; preferShortUsername = true;
}; };
groups."rss.access" = {}; groups."rss.access" = { };
groups."firefly.access" = {}; groups."firefly.access" = { };
groups."ollama.access" = {}; groups."ollama.access" = { };
groups."adguardhome.access" = {}; groups."adguardhome.access" = { };
groups."octoprint.access" = {}; groups."octoprint.access" = { };
systems.oauth2.oauth2-proxy = { systems.oauth2.oauth2-proxy = {
displayName = "Oauth2-Proxy"; displayName = "Oauth2-Proxy";
originUrl = "https://oauth2.${config.secrets.secrets.global.domains.web}/"; originUrl = "https://oauth2.${config.secrets.secrets.global.domains.web}/";
basicSecretFile = config.age.secrets.oauth2-proxy.path; basicSecretFile = config.age.secrets.oauth2-proxy.path;
scopeMaps."adguardhome.access" = ["openid" "email" "profile"]; scopeMaps."adguardhome.access" = [
scopeMaps."rss.access" = ["openid" "email" "profile"]; "openid"
scopeMaps."firefly.access" = ["openid" "email" "profile"]; "email"
scopeMaps."ollama.access" = ["openid" "email" "profile"]; "profile"
scopeMaps."octoprint.access" = ["openid" "email" "profile"]; ];
scopeMaps."rss.access" = [
"openid"
"email"
"profile"
];
scopeMaps."firefly.access" = [
"openid"
"email"
"profile"
];
scopeMaps."ollama.access" = [
"openid"
"email"
"profile"
];
scopeMaps."octoprint.access" = [
"openid"
"email"
"profile"
];
preferShortUsername = true; preferShortUsername = true;
claimMaps.groups = { claimMaps.groups = {
joinType = "array"; joinType = "array";
valuesByGroup."adguardhome.access" = ["adguardhome_access"]; valuesByGroup."adguardhome.access" = [ "adguardhome_access" ];
valuesByGroup."rss.access" = ["ttrss_access"]; valuesByGroup."rss.access" = [ "ttrss_access" ];
valuesByGroup."firefly.access" = ["firefly_access"]; valuesByGroup."firefly.access" = [ "firefly_access" ];
valuesByGroup."ollama.access" = ["ollama_access"]; valuesByGroup."ollama.access" = [ "ollama_access" ];
valuesByGroup."octoprint.access" = ["octoprint_access"]; valuesByGroup."octoprint.access" = [ "octoprint_access" ];
}; };
}; };
groups."forgejo.access" = { groups."forgejo.access" = {
members = ["forgejo.admins"]; members = [ "forgejo.admins" ];
}; };
groups."forgejo.admins" = { groups."forgejo.admins" = {
members = ["administrator"]; members = [ "administrator" ];
}; };
systems.oauth2.forgejo = { systems.oauth2.forgejo = {
displayName = "Forgejo"; displayName = "Forgejo";
originUrl = "https://forge.${config.secrets.secrets.global.domains.web}/"; originUrl = "https://forge.${config.secrets.secrets.global.domains.web}/";
basicSecretFile = config.age.secrets.oauth2-forgejo.path; basicSecretFile = config.age.secrets.oauth2-forgejo.path;
scopeMaps."forgejo.access" = ["openid" "email" "profile"]; scopeMaps."forgejo.access" = [
"openid"
"email"
"profile"
];
allowInsecureClientDisablePkce = true; allowInsecureClientDisablePkce = true;
preferShortUsername = true; preferShortUsername = true;
claimMaps.groups = { claimMaps.groups = {
joinType = "array"; joinType = "array";
valuesByGroup."forgejo.admins" = ["admin"]; valuesByGroup."forgejo.admins" = [ "admin" ];
}; };
}; };
groups."netbird.access" = { groups."netbird.access" = { };
};
systems.oauth2.netbird = { systems.oauth2.netbird = {
public = true; public = true;
displayName = "Netbird"; displayName = "Netbird";
@ -175,7 +212,11 @@ in {
preferShortUsername = true; preferShortUsername = true;
enableLocalhostRedirects = true; enableLocalhostRedirects = true;
enableLegacyCrypto = true; enableLegacyCrypto = true;
scopeMaps."netbird.access" = ["openid" "email" "profile"]; scopeMaps."netbird.access" = [
"openid"
"email"
"profile"
];
}; };
}; };
}; };

View file

@ -5,12 +5,17 @@
pkgs, pkgs,
lib, lib,
... ...
}: let }:
let
priv_domain = config.secrets.secrets.global.domains.mail_private; priv_domain = config.secrets.secrets.global.domains.mail_private;
domain = config.secrets.secrets.global.domains.mail_public; domain = config.secrets.secrets.global.domains.mail_public;
mailDomains = [priv_domain domain]; mailDomains = [
priv_domain
domain
];
maddyBackupDir = "/var/cache/backups/maddy"; maddyBackupDir = "/var/cache/backups/maddy";
in { in
{
systemd.tmpfiles.settings = { systemd.tmpfiles.settings = {
"10-maddy".${maddyBackupDir}.d = { "10-maddy".${maddyBackupDir}.d = {
inherit (config.services.maddy) user group; inherit (config.services.maddy) user group;
@ -40,7 +45,10 @@ in {
inherit (config.secrets.secrets.global.hetzner.users.maddy) subUid path; inherit (config.secrets.secrets.global.hetzner.users.maddy) subUid path;
sshAgeSecret = "maddyHetznerSsh"; sshAgeSecret = "maddyHetznerSsh";
}; };
paths = ["/var/lib/maddy/messages" maddyBackupDir]; paths = [
"/var/lib/maddy/messages"
maddyBackupDir
];
pruneOpts = [ pruneOpts = [
"--keep-daily 10" "--keep-daily 10"
"--keep-weekly 7" "--keep-weekly 7"
@ -49,21 +57,20 @@ in {
]; ];
}; };
}; };
systemd.services.maddy-backup = let systemd.services.maddy-backup =
let
cfg = config.systemd.services.maddy; cfg = config.systemd.services.maddy;
in { in
description = "Maddy db backup";
serviceConfig =
lib.recursiveUpdate
cfg.serviceConfig
{ {
description = "Maddy db backup";
serviceConfig = lib.recursiveUpdate cfg.serviceConfig {
ExecStart = "${pkgs.sqlite}/bin/sqlite3 /var/lib/maddy/imapsql.db \".backup '${maddyBackupDir}/imapsql.sqlite3'\""; ExecStart = "${pkgs.sqlite}/bin/sqlite3 /var/lib/maddy/imapsql.db \".backup '${maddyBackupDir}/imapsql.sqlite3'\"";
Restart = "no"; Restart = "no";
Type = "oneshot"; Type = "oneshot";
}; };
inherit (cfg) environment; inherit (cfg) environment;
requiredBy = ["restic-backups-main.service"]; requiredBy = [ "restic-backups-main.service" ];
before = ["restic-backups-main.service"]; before = [ "restic-backups-main.service" ];
}; };
age.secrets.patrickPasswd = { age.secrets.patrickPasswd = {
@ -73,7 +80,10 @@ in {
}; };
# Opening ports for additional TLS listeners. This is not yet # Opening ports for additional TLS listeners. This is not yet
# implemented in the module. # implemented in the module.
networking.firewall.allowedTCPPorts = [993 465]; networking.firewall.allowedTCPPorts = [
993
465
];
services.maddy = { services.maddy = {
enable = true; enable = true;
hostname = "mx1." + domain; hostname = "mx1." + domain;
@ -91,9 +101,7 @@ in {
ensureCredentials = { ensureCredentials = {
"patrick@${domain}".passwordFile = config.age.secrets.patrickPasswd.path; "patrick@${domain}".passwordFile = config.age.secrets.patrickPasswd.path;
}; };
ensureAccounts = [ ensureAccounts = [ "patrick@${domain}" ];
"patrick@${domain}"
];
openFirewall = true; openFirewall = true;
config = '' config = ''
## Maddy Mail Server - default configuration file (2022-06-18) ## Maddy Mail Server - default configuration file (2022-06-18)
@ -288,9 +296,7 @@ in {
useACMEWildcardHost = true; useACMEWildcardHost = true;
locations."=/mail/config-v1.1.xml".alias = locations."=/mail/config-v1.1.xml".alias =
pkgs.writeText "autoconfig.${domain}.xml" pkgs.writeText "autoconfig.${domain}.xml"
/* # xml
xml
*/
'' ''
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1"> <clientConfig version="1.1">

View file

@ -1,5 +1,6 @@
{config, ...}: { { config, ... }:
networking.firewall.allowedUDPPorts = [config.services.teamspeak3.defaultVoicePort]; {
networking.firewall.allowedUDPPorts = [ config.services.teamspeak3.defaultVoicePort ];
services.teamspeak3 = { services.teamspeak3 = {
enable = true; enable = true;
}; };

View file

@ -1,11 +1,12 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [80 3000 3001]; firewallRuleForNode.elisabeth.allowedTCPPorts = [
80
3000
3001
];
}; };
age.secrets.coturnPassword = { age.secrets.coturnPassword = {
@ -19,14 +20,20 @@
}; };
age.secrets.dataEnc = { age.secrets.dataEnc = {
generator.script = {pkgs, ...}: '' generator.script =
{ pkgs, ... }:
''
${lib.getExe pkgs.openssl} rand -base64 32 ${lib.getExe pkgs.openssl} rand -base64 32
''; '';
group = "netbird"; group = "netbird";
}; };
networking.firewall.allowedTCPPorts = [80 3000 3001]; networking.firewall.allowedTCPPorts = [
networking.firewall.allowedUDPPorts = [3478]; 80
3000
3001
];
networking.firewall.allowedUDPPorts = [ 3478 ];
services.netbird = { services.netbird = {
server = { server = {
enable = true; enable = true;

View file

@ -4,9 +4,11 @@
config, config,
nodes, nodes,
... ...
}: let }:
let
hostName = "nc.${config.secrets.secrets.global.domains.web}"; hostName = "nc.${config.secrets.secrets.global.domains.web}";
in { in
{
age.secrets.maddyPasswd = { age.secrets.maddyPasswd = {
generator.script = "alnum"; generator.script = "alnum";
mode = "440"; mode = "440";
@ -20,7 +22,8 @@ in {
mode = "640"; mode = "640";
}; };
services.maddy.ensureCredentials = { services.maddy.ensureCredentials = {
"nextcloud@${config.secrets.secrets.global.domains.mail_public}".passwordFile = nodes.maddy.config.age.secrets.nextcloudPasswd.path; "nextcloud@${config.secrets.secrets.global.domains.mail_public}".passwordFile =
nodes.maddy.config.age.secrets.nextcloudPasswd.path;
}; };
}; };
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
@ -54,7 +57,15 @@ in {
config.adminpassFile = config.age.secrets.ncpasswd.path; # Kinda ok just remember to instanly change after first setup config.adminpassFile = config.age.secrets.ncpasswd.path; # Kinda ok just remember to instanly change after first setup
config.adminuser = "admin"; config.adminuser = "admin";
extraApps = with config.services.nextcloud.package.packages.apps; { extraApps = with config.services.nextcloud.package.packages.apps; {
inherit contacts calendar tasks notes maps phonetrack user_oidc; inherit
contacts
calendar
tasks
notes
maps
phonetrack
user_oidc
;
}; };
maxUploadSize = "4G"; maxUploadSize = "4G";
extraAppsEnable = true; extraAppsEnable = true;
@ -62,7 +73,7 @@ in {
phpOptions."opcache.interned_strings_buffer" = "32"; phpOptions."opcache.interned_strings_buffer" = "32";
settings = { settings = {
default_phone_region = "DE"; default_phone_region = "DE";
trusted_proxies = [nodes.elisabeth.config.wireguard.elisabeth.ipv4]; trusted_proxies = [ nodes.elisabeth.config.wireguard.elisabeth.ipv4 ];
overwriteprotocol = "https"; overwriteprotocol = "https";
maintenance_window_start = 2; maintenance_window_start = 2;
enabledPreviewProviders = [ enabledPreviewProviders = [
@ -93,20 +104,22 @@ in {
dbtype = "pgsql"; dbtype = "pgsql";
}; };
}; };
systemd.tmpfiles.rules = let systemd.tmpfiles.rules =
let
mailer-passwd-conf = pkgs.writeText "nextcloud-config.php" '' mailer-passwd-conf = pkgs.writeText "nextcloud-config.php" ''
<?php <?php
$CONFIG = [ $CONFIG = [
'mail_smtppassword' => trim(file_get_contents('${config.age.secrets.maddyPasswd.path}')), 'mail_smtppassword' => trim(file_get_contents('${config.age.secrets.maddyPasswd.path}')),
]; ];
''; '';
in [ in
[
"L+ ${config.services.nextcloud.datadir}/config/mailer.config.php - - - - ${mailer-passwd-conf}" "L+ ${config.services.nextcloud.datadir}/config/mailer.config.php - - - - ${mailer-passwd-conf}"
]; ];
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [80]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ];
}; };
networking = { networking = {
# Use systemd-resolved inside the container # Use systemd-resolved inside the container

View file

@ -1,11 +1,8 @@
{ config, nodes, ... }:
{ {
config,
nodes,
...
}: {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
age.secrets.oauth2-cookie-secret = { age.secrets.oauth2-cookie-secret = {
@ -46,7 +43,7 @@
redeemURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/token"; redeemURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/token";
validateURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/oauth2-proxy/userinfo"; validateURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/oauth2-proxy/userinfo";
clientID = "oauth2-proxy"; clientID = "oauth2-proxy";
email.domains = ["*"]; email.domains = [ "*" ];
}; };
systemd.services.oauth2-proxy.serviceConfig = { systemd.services.oauth2-proxy.serviceConfig = {
@ -72,15 +69,15 @@
# it includes the newline terminating the file which # it includes the newline terminating the file which
# makes kanidm reject the secret # makes kanidm reject the secret
age.secrets.oauth2-client-secret-env = { age.secrets.oauth2-client-secret-env = {
generator.dependencies = [ generator.dependencies = [ nodes.elisabeth-kanidm.config.age.secrets.oauth2-proxy ];
nodes.elisabeth-kanidm.config.age.secrets.oauth2-proxy generator.script =
]; {
generator.script = {
lib, lib,
decrypt, decrypt,
deps, deps,
... ...
}: '' }:
''
echo -n "OAUTH2_PROXY_CLIENT_SECRET=" echo -n "OAUTH2_PROXY_CLIENT_SECRET="
${decrypt} ${lib.escapeShellArg (lib.head deps).file} ${decrypt} ${lib.escapeShellArg (lib.head deps).file}
''; '';

View file

@ -1,12 +1,13 @@
{config, ...}: { { config, ... }:
{
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.octoprint.port]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.octoprint.port ];
}; };
services.octoprint = { services.octoprint = {
port = 3000; port = 3000;
enable = true; enable = true;
plugins = ps: with ps; [ender3v2tempfix]; plugins = ps: with ps; [ ender3v2tempfix ];
extraConfig = { extraConfig = {
accessControl = { accessControl = {
addRemoteUser = true; addRemoteUser = true;

View file

@ -1,7 +1,8 @@
{config, ...}: { { config, ... }:
{
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.open-webui.port]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.open-webui.port ];
}; };
services.ollama = { services.ollama = {
host = "localhost"; host = "localhost";

View file

@ -4,10 +4,12 @@
config, config,
lib, lib,
... ...
}: let }:
let
paperlessdomain = "ppl.${config.secrets.secrets.global.domains.web}"; paperlessdomain = "ppl.${config.secrets.secrets.global.domains.web}";
paperlessBackupDir = "/var/cache/backups/paperless"; paperlessBackupDir = "/var/cache/backups/paperless";
in { in
{
systemd.tmpfiles.settings = { systemd.tmpfiles.settings = {
"10-paperless".${paperlessBackupDir}.d = { "10-paperless".${paperlessBackupDir}.d = {
inherit (config.services.paperless) user; inherit (config.services.paperless) user;
@ -36,7 +38,7 @@ in {
inherit (config.secrets.secrets.global.hetzner.users.paperless) subUid path; inherit (config.secrets.secrets.global.hetzner.users.paperless) subUid path;
sshAgeSecret = "paperlessHetznerSsh"; sshAgeSecret = "paperlessHetznerSsh";
}; };
paths = [paperlessBackupDir]; paths = [ paperlessBackupDir ];
pruneOpts = [ pruneOpts = [
"--keep-daily 10" "--keep-daily 10"
"--keep-weekly 7" "--keep-weekly 7"
@ -45,27 +47,26 @@ in {
]; ];
}; };
}; };
systemd.services.paperless-backup = let systemd.services.paperless-backup =
let
cfg = config.systemd.services.paperless-consumer; cfg = config.systemd.services.paperless-consumer;
in { in
description = "Paperless document backup";
serviceConfig =
lib.recursiveUpdate
cfg.serviceConfig
{ {
description = "Paperless document backup";
serviceConfig = lib.recursiveUpdate cfg.serviceConfig {
ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}"; ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}";
ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [paperlessBackupDir]; ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [ paperlessBackupDir ];
Restart = "no"; Restart = "no";
Type = "oneshot"; Type = "oneshot";
}; };
inherit (cfg) environment; inherit (cfg) environment;
requiredBy = ["restic-backups-main.service"]; requiredBy = [ "restic-backups-main.service" ];
before = ["restic-backups-main.service"]; before = [ "restic-backups-main.service" ];
}; };
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.paperless.port]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.paperless.port ];
}; };
age.secrets.paperless-admin-passwd = { age.secrets.paperless-admin-passwd = {

View file

@ -4,18 +4,20 @@
lib, lib,
pkgs, pkgs,
... ...
}: let }:
let
prestart = pkgs.writeShellScript "pr-tracker-pre" '' prestart = pkgs.writeShellScript "pr-tracker-pre" ''
if [ ! -d ./nixpkgs ]; then if [ ! -d ./nixpkgs ]; then
${lib.getExe pkgs.git} clone https://github.com/NixOS/nixpkgs.git ${lib.getExe pkgs.git} clone https://github.com/NixOS/nixpkgs.git
fi fi
''; '';
in { in
{
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
}; };
networking.firewall.allowedTCPPorts = [3000]; networking.firewall.allowedTCPPorts = [ 3000 ];
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
{ {
directory = "/var/lib/pr-tracker"; directory = "/var/lib/pr-tracker";
@ -43,15 +45,16 @@ in {
mode = "640"; mode = "640";
}; };
services.maddy.ensureCredentials = { services.maddy.ensureCredentials = {
"pr-tracker@${config.secrets.secrets.global.domains.mail_public}".passwordFile = nodes.maddy.config.age.secrets.pr-trackerPasswd.path; "pr-tracker@${config.secrets.secrets.global.domains.mail_public}".passwordFile =
nodes.maddy.config.age.secrets.pr-trackerPasswd.path;
}; };
}; };
systemd.sockets.pr-tracker = { systemd.sockets.pr-tracker = {
listenStreams = ["0.0.0.0:3000"]; listenStreams = [ "0.0.0.0:3000" ];
wantedBy = ["sockets.target"]; wantedBy = [ "sockets.target" ];
}; };
systemd.services.pr-tracker = { systemd.services.pr-tracker = {
path = [pkgs.git]; path = [ pkgs.git ];
serviceConfig = { serviceConfig = {
User = "pr-tracker"; User = "pr-tracker";
Group = "pr-tracker"; Group = "pr-tracker";
@ -104,13 +107,13 @@ in {
}; };
}; };
systemd.timers.pr-tracker-update = { systemd.timers.pr-tracker-update = {
wantedBy = ["timers.target"]; wantedBy = [ "timers.target" ];
timerConfig = { timerConfig = {
OnBootSec = "30m"; OnBootSec = "30m";
OnUnitActiveSec = "30m"; OnUnitActiveSec = "30m";
}; };
}; };
users.groups.pr-tracker = {}; users.groups.pr-tracker = { };
users.users.pr-tracker = { users.users.pr-tracker = {
isSystemUser = true; isSystemUser = true;
group = "pr-tracker"; group = "pr-tracker";

View file

@ -4,14 +4,20 @@
config, config,
pkgs, # not unused needed for the usage of attrs later to contains pkgs pkgs, # not unused needed for the usage of attrs later to contains pkgs
... ...
} @ attrs: let }@attrs:
let
hostName = "radicale.${config.secrets.secrets.global.domains.mail}"; hostName = "radicale.${config.secrets.secrets.global.domains.mail}";
in { in
imports = [./containers.nix ./ddclient.nix ./acme.nix]; {
imports = [
./containers.nix
./ddclient.nix
./acme.nix
];
services.nginx = { services.nginx = {
enable = true; enable = true;
upstreams.radicale = { upstreams.radicale = {
servers."192.168.178.34:8000" = {}; servers."192.168.178.34:8000" = { };
extraConfig = '' extraConfig = ''
zone radicale 64k ; zone radicale 64k ;
@ -32,10 +38,10 @@ in {
config = _: { config = _: {
systemd.network.networks = { systemd.network.networks = {
"lan01" = { "lan01" = {
address = ["192.168.178.34/24"]; address = [ "192.168.178.34/24" ];
gateway = ["192.168.178.1"]; gateway = [ "192.168.178.1" ];
matchConfig.Name = "lan01*"; matchConfig.Name = "lan01*";
dns = ["192.168.178.2"]; dns = [ "192.168.178.2" ];
networkConfig = { networkConfig = {
IPv6PrivacyExtensions = "yes"; IPv6PrivacyExtensions = "yes";
MulticastDNS = true; MulticastDNS = true;
@ -54,7 +60,10 @@ in {
enable = true; enable = true;
setting = { setting = {
server = { server = {
hosts = ["0.0.0.0:8000" "[::]:8000"]; hosts = [
"0.0.0.0:8000"
"[::]:8000"
];
auth = { auth = {
type = "htpasswd"; type = "htpasswd";
htpasswd_filename = "/etc/radicale/users"; htpasswd_filename = "/etc/radicale/users";
@ -89,7 +98,7 @@ in {
networking = { networking = {
firewall = { firewall = {
enable = true; enable = true;
allowedTCPPorts = [8000]; allowedTCPPorts = [ 8000 ];
}; };
# Use systemd-resolved inside the container # Use systemd-resolved inside the container
useHostResolvConf = lib.mkForce false; useHostResolvConf = lib.mkForce false;
@ -106,4 +115,3 @@ in {
#kanidm #kanidm
#remote backups #remote backups
#immich #immich

View file

@ -1,16 +1,13 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
services.samba-wsdd = { services.samba-wsdd = {
enable = true; # make shares visible for windows 10 clients enable = true; # make shares visible for windows 10 clients
openFirewall = true; openFirewall = true;
}; };
disabledModules = ["services/networking/netbird.nix"]; disabledModules = [ "services/networking/netbird.nix" ];
imports = [../../modules/netbird-client.nix]; imports = [ ../../modules/netbird-client.nix ];
services.netbird.tunnels = { services.netbird.tunnels = {
netbird-samba = { netbird-samba = {
environment = { environment = {
@ -43,7 +40,7 @@
inherit (config.secrets.secrets.global.hetzner.users.smb) subUid path; inherit (config.secrets.secrets.global.hetzner.users.smb) subUid path;
sshAgeSecret = "resticHetznerSsh"; sshAgeSecret = "resticHetznerSsh";
}; };
paths = ["/bunker"]; paths = [ "/bunker" ];
pruneOpts = [ pruneOpts = [
"--keep-daily 10" "--keep-daily 10"
"--keep-weekly 7" "--keep-weekly 7"
@ -55,11 +52,17 @@
wireguard.samba-patrick.server = { wireguard.samba-patrick.server = {
host = config.secrets.secrets.global.domains.web; host = config.secrets.secrets.global.domains.web;
port = 51830; port = 51830;
reservedAddresses = ["10.43.0.0/20" "fd00:1765::/112"]; reservedAddresses = [
"10.43.0.0/20"
"fd00:1765::/112"
];
openFirewall = true; openFirewall = true;
}; };
networking.nftables.firewall.zones.untrusted.interfaces = ["samba-patrick" "netbird-samba"]; networking.nftables.firewall.zones.untrusted.interfaces = [
"samba-patrick"
"netbird-samba"
];
services.samba = { services.samba = {
enable = true; enable = true;
@ -106,17 +109,20 @@
"disable spoolss = yes" "disable spoolss = yes"
"show add printer wizard = no" "show add printer wizard = no"
]; ];
shares = let shares =
mkShare = { let
mkShare =
{
name, name,
user ? "smb", user ? "smb",
group ? "smb", group ? "smb",
hasBunker ? false, hasBunker ? false,
hasPaperless ? false, hasPaperless ? false,
persistRoot ? "/panzer", persistRoot ? "/panzer",
}: cfg: let }:
config = cfg:
{ let
config = {
"#persistRoot" = persistRoot; "#persistRoot" = persistRoot;
"#user" = user; "#user" = user;
"#group" = group; "#group" = group;
@ -131,28 +137,21 @@
"force directory mode" = "0770"; "force directory mode" = "0770";
# Might be necessary for windows user to be able to open thing in smb # Might be necessary for windows user to be able to open thing in smb
"acl allow execute always" = "no"; "acl allow execute always" = "no";
} } // cfg;
// cfg;
in in
{ {
"${name}" = "${name}" = config // {
config "path" = "/media/smb/${name}";
// {"path" = "/media/smb/${name}";}; };
} }
// lib.optionalAttrs hasBunker // lib.optionalAttrs hasBunker {
{ "${name}-important" = config // {
"${name}-important" =
config
// {
"path" = "/media/smb/${name}-important"; "path" = "/media/smb/${name}-important";
"#persistRoot" = "/bunker"; "#persistRoot" = "/bunker";
}; };
} }
// lib.optionalAttrs hasPaperless // lib.optionalAttrs hasPaperless {
{ "${name}-paperless" = config // {
"${name}-paperless" =
config
// {
"path" = "/media/smb/${name}-paperless"; "path" = "/media/smb/${name}-paperless";
"#paperless" = true; "#paperless" = true;
"force user" = "paperless"; "force user" = "paperless";
@ -168,38 +167,39 @@
user = "ggr"; user = "ggr";
group = "ggr"; group = "ggr";
hasBunker = true; hasBunker = true;
} {}) } { })
(mkShare { (mkShare {
name = "patri"; name = "patri";
user = "patrick"; user = "patrick";
group = "patrick"; group = "patrick";
hasBunker = true; hasBunker = true;
hasPaperless = true; hasPaperless = true;
} {}) } { })
(mkShare { (mkShare {
name = "helen-data"; name = "helen-data";
user = "helen"; user = "helen";
group = "helen"; group = "helen";
hasBunker = true; hasBunker = true;
} {}) } { })
(mkShare { (mkShare {
name = "david"; name = "david";
user = "david"; user = "david";
group = "david"; group = "david";
hasBunker = true; hasBunker = true;
hasPaperless = true; hasPaperless = true;
} {}) } { })
(mkShare { (mkShare {
name = "printer"; name = "printer";
user = "printer"; user = "printer";
group = "printer"; group = "printer";
} {}) } { })
(mkShare { (mkShare {
name = "family-data"; name = "family-data";
user = "family"; user = "family";
group = "family"; group = "family";
} {}) } { })
(mkShare { (mkShare
{
name = "media"; name = "media";
user = "family"; user = "family";
group = "family"; group = "family";
@ -208,7 +208,8 @@
{ {
"read only" = "yes"; "read only" = "yes";
"write list" = "@family"; "write list" = "@family";
}) }
)
]; ];
}; };
# to get this file start a smbd, add users using 'smbpasswd -a <user>' # to get this file start a smbd, add users using 'smbpasswd -a <user>'
@ -216,11 +217,16 @@
age.secrets.smbpassdb = { age.secrets.smbpassdb = {
rekeyFile = config.node.secretsDir + "/smbpassdb.tdb.age"; rekeyFile = config.node.secretsDir + "/smbpassdb.tdb.age";
}; };
users = let users =
let
users = lib.unique (lib.mapAttrsToList (_: val: val."force user") config.services.samba.shares); users = lib.unique (lib.mapAttrsToList (_: val: val."force user") config.services.samba.shares);
groups = lib.unique (users ++ (lib.mapAttrsToList (_: val: val."force group") config.services.samba.shares)); groups = lib.unique (
in { users ++ (lib.mapAttrsToList (_: val: val."force group") config.services.samba.shares)
users = lib.mkMerge ((lib.flip map users (user: { );
in
{
users = lib.mkMerge (
(lib.flip map users (user: {
${user} = { ${user} = {
isNormalUser = true; isNormalUser = true;
home = "/var/empty"; home = "/var/empty";
@ -230,41 +236,63 @@
group = "${user}"; group = "${user}";
}; };
})) }))
++ [ ++ [ { paperless.isNormalUser = lib.mkForce false; } ]
{paperless.isNormalUser = lib.mkForce false;} );
]); groups = lib.mkMerge (
groups = lib.mkMerge ((lib.flip map groups (group: { (lib.flip map groups (group: {
${group} = { ${group} = { };
};
})) }))
++ [ ++ [
{ {
family.members = ["patrick" "david" "helen" "ggr"]; family.members = [
printer.members = ["patrick" "david" "helen" "ggr"]; "patrick"
"david"
"helen"
"ggr"
];
printer.members = [
"patrick"
"david"
"helen"
"ggr"
];
} }
]); ]
);
}; };
fileSystems = lib.mkMerge (lib.flip lib.mapAttrsToList config.services.samba.shares (_: v: fileSystems = lib.mkMerge (
lib.flip lib.mapAttrsToList config.services.samba.shares (
_: v:
lib.optionalAttrs ((v ? "#paperless") && v."#paperless") { lib.optionalAttrs ((v ? "#paperless") && v."#paperless") {
"${v.path}/consume" = { "${v.path}/consume" = {
fsType = "none"; fsType = "none";
options = ["bind"]; options = [ "bind" ];
device = "/paperless/consume/${v."#user"}"; device = "/paperless/consume/${v."#user"}";
}; };
"${v.path}/media/archive" = { "${v.path}/media/archive" = {
fsType = "none "; fsType = "none ";
options = ["bind" "ro"]; options = [
"bind"
"ro"
];
device = "/paperless/media/documents/archive/${v."#user"}"; device = "/paperless/media/documents/archive/${v."#user"}";
}; };
"${v.path}/media/originals" = { "${v.path}/media/originals" = {
fsType = "none "; fsType = "none ";
options = ["bind" "ro"]; options = [
"bind"
"ro"
];
device = "/paperless/media/documents/originals/${v."#user"}"; device = "/paperless/media/documents/originals/${v."#user"}";
}; };
})); }
)
);
systemd.tmpfiles.settings = lib.mkMerge (lib.flip lib.mapAttrsToList config.services.samba.shares (_: v: systemd.tmpfiles.settings = lib.mkMerge (
lib.flip lib.mapAttrsToList config.services.samba.shares (
_: v:
lib.optionalAttrs ((v ? "#paperless") && v."#paperless") { lib.optionalAttrs ((v ? "#paperless") && v."#paperless") {
"10-smb-paperless"."/paperless/consume/".d = { "10-smb-paperless"."/paperless/consume/".d = {
user = "paperless"; user = "paperless";
@ -317,9 +345,13 @@
group = "paperless"; group = "paperless";
mode = "0660"; mode = "0660";
}; };
})); }
environment.persistence = lib.mkMerge (lib.flatten [ )
(lib.flip lib.mapAttrsToList config.services.samba.shares (_: v: );
environment.persistence = lib.mkMerge (
lib.flatten [
(lib.flip lib.mapAttrsToList config.services.samba.shares (
_: v:
lib.optionalAttrs ((v ? "#persistRoot") && (v."#persistRoot" != "")) { lib.optionalAttrs ((v ? "#persistRoot") && (v."#persistRoot" != "")) {
${v."#persistRoot"}.directories = [ ${v."#persistRoot"}.directories = [
{ {
@ -329,7 +361,8 @@
mode = "0770"; mode = "0770";
} }
]; ];
})) }
))
(lib.flip lib.mapAttrsToList config.services.netbird.tunnels ( (lib.flip lib.mapAttrsToList config.services.netbird.tunnels (
_: v: { _: v: {
"/state".directories = [ "/state".directories = [
@ -340,5 +373,6 @@
]; ];
} }
)) ))
]); ]
);
} }

View file

@ -1,7 +1,8 @@
{config, ...}: { { config, ... }:
{
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [80]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ];
}; };
services.freshrss = { services.freshrss = {
enable = true; enable = true;

View file

@ -3,9 +3,11 @@
lib, lib,
nodes, nodes,
... ...
}: let }:
let
vaultwardenDomain = "pw.${config.secrets.secrets.global.domains.web}"; vaultwardenDomain = "pw.${config.secrets.secrets.global.domains.web}";
in { in
{
age.secrets.vaultwarden-env = { age.secrets.vaultwarden-env = {
rekeyFile = config.node.secretsDir + "/vaultwarden-env.age"; rekeyFile = config.node.secretsDir + "/vaultwarden-env.age";
mode = "440"; mode = "440";
@ -43,7 +45,7 @@ in {
inherit (config.secrets.secrets.global.hetzner.users.vaultwarden) subUid path; inherit (config.secrets.secrets.global.hetzner.users.vaultwarden) subUid path;
sshAgeSecret = "vaultwardenHetznerSsh"; sshAgeSecret = "vaultwardenHetznerSsh";
}; };
paths = [config.services.vaultwarden.backupDir]; paths = [ config.services.vaultwarden.backupDir ];
pruneOpts = [ pruneOpts = [
"--keep-daily 10" "--keep-daily 10"
"--keep-weekly 7" "--keep-weekly 7"
@ -64,17 +66,18 @@ in {
mode = "640"; mode = "640";
}; };
services.maddy.ensureCredentials = { services.maddy.ensureCredentials = {
"vaultwarden@${config.secrets.secrets.global.domains.mail_public}".passwordFile = nodes.maddy.config.age.secrets.vaultwardenPasswd.path; "vaultwarden@${config.secrets.secrets.global.domains.mail_public}".passwordFile =
nodes.maddy.config.age.secrets.vaultwardenPasswd.path;
}; };
}; };
system.activationScripts.systemd_env_smtp_passwd = { system.activationScripts.systemd_env_smtp_passwd = {
text = '' text = ''
echo "SMTP_PASSWORD=$(< ${lib.escapeShellArg config.age.secrets.maddyPasswd.path})" > /run/vaultwarden_smtp_passwd echo "SMTP_PASSWORD=$(< ${lib.escapeShellArg config.age.secrets.maddyPasswd.path})" > /run/vaultwarden_smtp_passwd
''; '';
deps = ["agenix"]; deps = [ "agenix" ];
}; };
systemd.services.vaultwarden.serviceConfig.EnvironmentFile = ["/run/vaultwarden_smtp_passwd"]; systemd.services.vaultwarden.serviceConfig.EnvironmentFile = [ "/run/vaultwarden_smtp_passwd" ];
services.vaultwarden = { services.vaultwarden = {
enable = true; enable = true;
@ -107,7 +110,7 @@ in {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [config.services.vaultwarden.config.rocketPort]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.vaultwarden.config.rocketPort ];
}; };
# Replace uses of old name # Replace uses of old name

View file

@ -1,11 +1,11 @@
{ config, pkgs, ... }:
{ {
config,
pkgs,
...
}: {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [3000 80]; firewallRuleForNode.elisabeth.allowedTCPPorts = [
3000
80
];
}; };
age.secrets.spotifySecret = { age.secrets.spotifySecret = {
owner = "root"; owner = "root";

View file

@ -101,7 +101,8 @@
}; };
}; };
outputs = { outputs =
{
self, self,
nixpkgs, nixpkgs,
flake-utils, flake-utils,
@ -113,15 +114,16 @@
nixos-extra-modules, nixos-extra-modules,
nix-topology, nix-topology,
... ...
} @ inputs: let }@inputs:
let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
stateVersion = "23.05"; stateVersion = "23.05";
in in
{ {
secretsConfig = { secretsConfig = {
# This should be a link to one of the age public keys is './keys' # This should be a link to one of the age public keys is './keys'
masterIdentities = [./keys/PatC.pub]; masterIdentities = [ ./keys/PatC.pub ];
extraEncryptionPubkeys = [./secrets/recipients.txt]; extraEncryptionPubkeys = [ ./secrets/recipients.txt ];
}; };
agenix-rekey = agenix-rekey.configure { agenix-rekey = agenix-rekey.configure {
userFlake = self; userFlake = self;
@ -129,8 +131,7 @@
}; };
inherit stateVersion; inherit stateVersion;
inherit inherit (import ./nix/hosts.nix inputs)
(import ./nix/hosts.nix inputs)
hosts hosts
nixosConfigurations nixosConfigurations
minimalConfigurations minimalConfigurations
@ -139,15 +140,16 @@
nodes = self.nixosConfigurations // self.guestConfigurations; nodes = self.nixosConfigurations // self.guestConfigurations;
inherit inherit
(lib.foldl' lib.recursiveUpdate {} (lib.foldl' lib.recursiveUpdate { } (
(lib.mapAttrsToList lib.mapAttrsToList (import ./nix/generate-installer-package.nix inputs) self.minimalConfigurations
(import ./nix/generate-installer-package.nix inputs) ))
self.minimalConfigurations))
packages packages
; ;
} }
// flake-utils.lib.eachDefaultSystem (system: rec { // flake-utils.lib.eachDefaultSystem (system: rec {
apps.setupHetznerStorageBoxes = import (nixos-extra-modules + "/apps/setup-hetzner-storage-boxes.nix") { apps.setupHetznerStorageBoxes =
import (nixos-extra-modules + "/apps/setup-hetzner-storage-boxes.nix")
{
inherit pkgs; inherit pkgs;
nixosConfigurations = self.nodes; nixosConfigurations = self.nodes;
decryptIdentity = builtins.head self.secretsConfig.masterIdentities; decryptIdentity = builtins.head self.secretsConfig.masterIdentities;
@ -172,7 +174,7 @@
topology = import nix-topology { topology = import nix-topology {
inherit pkgs; inherit pkgs;
modules = [ modules = [
{inherit (self) nixosConfigurations;} { inherit (self) nixosConfigurations; }
./nix/topology.nix ./nix/topology.nix
]; ];
}; };
@ -191,9 +193,7 @@
.${system}; .${system};
}; };
checks.pre-commit-check = checks.pre-commit-check = pre-commit-hooks.lib.${system}.run {
pre-commit-hooks.lib.${system}.run
{
src = lib.cleanSource ./.; src = lib.cleanSource ./.;
hooks = { hooks = {
nixfmt = { nixfmt = {

View file

@ -1,8 +1,5 @@
{ inputs, lib, ... }:
{ {
inputs,
lib,
...
}: {
imports = [ imports = [
inputs.nixos-hardware.nixosModules.common-gpu-nvidia-nonprime inputs.nixos-hardware.nixosModules.common-gpu-nvidia-nonprime
inputs.nixos-hardware.nixosModules.common-cpu-intel-cpu-only inputs.nixos-hardware.nixosModules.common-cpu-intel-cpu-only
@ -51,9 +48,23 @@
device = "/dev/input/event15"; device = "/dev/input/event15";
}; };
boot.binfmt.emulatedSystems = ["aarch64-linux" "riscv64-linux"]; boot.binfmt.emulatedSystems = [
nix.settings.system-features = ["kvm" "nixos-test"]; "aarch64-linux"
boot.kernelParams = lib.mkForce ["rd.luks.options=timeout=0" "rootflags=x-systemd.device-timeout=0" "nohibernate" "root=fstab" "loglevel=4" "nvidia-drm.modeset=1" "nvidia.NVreg_PreserveVideoMemoryAllocations=1"]; "riscv64-linux"
];
nix.settings.system-features = [
"kvm"
"nixos-test"
];
boot.kernelParams = lib.mkForce [
"rd.luks.options=timeout=0"
"rootflags=x-systemd.device-timeout=0"
"nohibernate"
"root=fstab"
"loglevel=4"
"nvidia-drm.modeset=1"
"nvidia.NVreg_PreserveVideoMemoryAllocations=1"
];
services.netbird.enable = true; services.netbird.enable = true;
# Do not cleanup nix store to prevent having to rebuild packages onca a month # Do not cleanup nix store to prevent having to rebuild packages onca a month

View file

@ -3,7 +3,8 @@
nodes, nodes,
lib, lib,
... ...
}: { }:
{
disko.devices = { disko.devices = {
disk = { disk = {
m2-ssd = rec { m2-ssd = rec {
@ -12,9 +13,15 @@
content = with lib.disko.gpt; { content = with lib.disko.gpt; {
type = "gpt"; type = "gpt";
partitions = { partitions = {
boot = (partEfi "2GiB") // {device = "${device}-part1";}; boot = (partEfi "2GiB") // {
swap = (partSwap "16G") // {device = "${device}-part2";}; device = "${device}-part1";
rpool = (partLuksZfs "m2-ssd" "rpool" "100%") // {device = "${device}-part3";}; };
swap = (partSwap "16G") // {
device = "${device}-part2";
};
rpool = (partLuksZfs "m2-ssd" "rpool" "100%") // {
device = "${device}-part3";
};
}; };
}; };
}; };
@ -24,13 +31,15 @@
content = with lib.disko.gpt; { content = with lib.disko.gpt; {
type = "gpt"; type = "gpt";
partitions = { partitions = {
panzer = (partLuksZfs "sata-hdd" "panzer" "100%") // {device = "${device}-part1";}; panzer = (partLuksZfs "sata-hdd" "panzer" "100%") // {
device = "${device}-part1";
};
}; };
}; };
}; };
}; };
zpool = with lib.disko.zfs; { zpool = with lib.disko.zfs; {
rpool = mkZpool {datasets = impermanenceZfsDatasets;}; rpool = mkZpool { datasets = impermanenceZfsDatasets; };
panzer = mkZpool { panzer = mkZpool {
datasets = { datasets = {
"local" = unmountable; "local" = unmountable;
@ -42,8 +51,8 @@
fileSystems."/state".neededForBoot = true; fileSystems."/state".neededForBoot = true;
fileSystems."/persist".neededForBoot = true; fileSystems."/persist".neededForBoot = true;
fileSystems."/panzer/state".neededForBoot = true; fileSystems."/panzer/state".neededForBoot = true;
boot.initrd.systemd.services."zfs-import-panzer".after = ["cryptsetup.target"]; boot.initrd.systemd.services."zfs-import-panzer".after = [ "cryptsetup.target" ];
boot.initrd.systemd.services."zfs-import-rpool".after = ["cryptsetup.target"]; boot.initrd.systemd.services."zfs-import-rpool".after = [ "cryptsetup.target" ];
wireguard.scrtiny-patrick.client.via = "elisabeth"; wireguard.scrtiny-patrick.client.via = "elisabeth";

View file

@ -1,4 +1,5 @@
{config, ...}: { { config, ... }:
{
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
}; };
@ -20,6 +21,6 @@
}; };
}; };
}; };
networking.nftables.firewall.zones.untrusted.interfaces = ["lan01"]; networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ];
wireguard.samba-patrick.client.via = "elisabeth-samba"; wireguard.samba-patrick.client.via = "elisabeth-samba";
} }

View file

@ -3,9 +3,9 @@
minimal, minimal,
lib, lib,
... ...
}: { }:
imports = {
[ imports = [
inputs.nixos-hardware.nixosModules.common-pc inputs.nixos-hardware.nixosModules.common-pc
inputs.nixos-hardware.nixosModules.common-pc-ssd inputs.nixos-hardware.nixosModules.common-pc-ssd
inputs.nixos-hardware.nixosModules.common-cpu-amd inputs.nixos-hardware.nixosModules.common-cpu-amd
@ -22,10 +22,7 @@
./blog.nix ./blog.nix
./net.nix ./net.nix
./fs.nix ./fs.nix
] ] ++ lib.lists.optionals (!minimal) [ ./guests.nix ];
++ lib.lists.optionals (!minimal) [
./guests.nix
];
services.xserver = { services.xserver = {
xkb = { xkb = {
layout = "de"; layout = "de";

View file

@ -1,8 +1,5 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
disko.devices = { disko.devices = {
disk = { disk = {
internal-ssd = rec { internal-ssd = rec {
@ -11,8 +8,12 @@
content = with lib.disko.gpt; { content = with lib.disko.gpt; {
type = "gpt"; type = "gpt";
partitions = { partitions = {
boot = (partEfi "1GiB") // {device = "${device}-part1";}; boot = (partEfi "1GiB") // {
rpool = (partLuksZfs "ssd" "rpool" "100%") // {device = "${device}-part2";}; device = "${device}-part1";
};
rpool = (partLuksZfs "ssd" "rpool" "100%") // {
device = "${device}-part2";
};
}; };
}; };
}; };
@ -49,7 +50,7 @@
}; };
zpool = with lib.disko.zfs; { zpool = with lib.disko.zfs; {
rpool = mkZpool {datasets = impermanenceZfsDatasets;}; rpool = mkZpool { datasets = impermanenceZfsDatasets; };
panzer = mkZpool { panzer = mkZpool {
datasets = { datasets = {
"safe/guests" = unmountable; "safe/guests" = unmountable;
@ -128,10 +129,13 @@
wireguard.scrtiny-patrick.server = { wireguard.scrtiny-patrick.server = {
host = config.secrets.secrets.global.domains.web; host = config.secrets.secrets.global.domains.web;
port = 51831; port = 51831;
reservedAddresses = ["10.44.0.0/16" "fd00:1766::/112"]; reservedAddresses = [
"10.44.0.0/16"
"fd00:1766::/112"
];
openFirewall = true; openFirewall = true;
}; };
networking.nftables.firewall.zones.untrusted.interfaces = ["scrtiny-patrick"]; networking.nftables.firewall.zones.untrusted.interfaces = [ "scrtiny-patrick" ];
services.scrutiny = { services.scrutiny = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
@ -156,6 +160,6 @@
fileSystems."/state".neededForBoot = true; fileSystems."/state".neededForBoot = true;
fileSystems."/persist".neededForBoot = true; fileSystems."/persist".neededForBoot = true;
boot.initrd.systemd.services."zfs-import-panzer".after = ["cryptsetup.target"]; boot.initrd.systemd.services."zfs-import-panzer".after = [ "cryptsetup.target" ];
boot.initrd.systemd.services."zfs-import-renaultft".after = ["cryptsetup.target"]; boot.initrd.systemd.services."zfs-import-renaultft".after = [ "cryptsetup.target" ];
} }

View file

@ -6,8 +6,11 @@
minimal, minimal,
nodes, nodes,
... ...
}: let }:
domainOf = hostName: let let
domainOf =
hostName:
let
domains = { domains = {
adguardhome = "adguardhome"; adguardhome = "adguardhome";
forgejo = "forge"; forgejo = "forge";
@ -28,23 +31,31 @@
octoprint = "print"; octoprint = "print";
pr-tracker = "tracker"; pr-tracker = "tracker";
}; };
in "${domains.${hostName}}.${config.secrets.secrets.global.domains.web}"; in
"${domains.${hostName}}.${config.secrets.secrets.global.domains.web}";
# TODO hard coded elisabeth nicht so schön # TODO hard coded elisabeth nicht so schön
ipOf = hostName: ipOf =
if nodes ? ${hostName} hostName:
then nodes.${hostName}.config.wireguard.elisabeth.ipv4 if nodes ? ${hostName} then
else nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4; nodes.${hostName}.config.wireguard.elisabeth.ipv4
in { else
services.nginx = let nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
blockOf = hostName: { in
{
services.nginx =
let
blockOf =
hostName:
{
virtualHostExtraConfig ? "", virtualHostExtraConfig ? "",
maxBodySize ? "500M", maxBodySize ? "500M",
port ? 3000, port ? 3000,
upstream ? hostName, upstream ? hostName,
protocol ? "http", protocol ? "http",
}: { }:
{
upstreams.${hostName} = { upstreams.${hostName} = {
servers."${ipOf upstream}:${toString port}" = {}; servers."${ipOf upstream}:${toString port}" = { };
extraConfig = '' extraConfig = ''
zone ${hostName} 64k ; zone ${hostName} 64k ;
keepalive 5 ; keepalive 5 ;
@ -65,7 +76,8 @@ in {
+ virtualHostExtraConfig; + virtualHostExtraConfig;
}; };
}; };
proxyProtect = hostName: cfg: allowedGroup: proxyProtect =
hostName: cfg: allowedGroup:
lib.mkMerge [ lib.mkMerge [
(blockOf hostName cfg) (blockOf hostName cfg)
{ {
@ -95,7 +107,9 @@ in {
}; };
locations."= /oauth2/auth" = { locations."= /oauth2/auth" = {
proxyPass = "http://oauth2-proxy/oauth2/auth" + lib.optionalString allowedGroup "?allowed_groups=${hostName}_access"; proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ lib.optionalString allowedGroup "?allowed_groups=${hostName}_access";
extraConfig = '' extraConfig = ''
internal; internal;
@ -114,14 +128,14 @@ in {
enable = true; enable = true;
recommendedSetup = true; recommendedSetup = true;
upstreams.netbird = { upstreams.netbird = {
servers."${ipOf "netbird"}:80" = {}; servers."${ipOf "netbird"}:80" = { };
extraConfig = '' extraConfig = ''
zone netbird 64k ; zone netbird 64k ;
keepalive 5 ; keepalive 5 ;
''; '';
}; };
upstreams.netbird-mgmt = { upstreams.netbird-mgmt = {
servers."${ipOf "netbird"}:3000" = {}; servers."${ipOf "netbird"}:3000" = { };
extraConfig = '' extraConfig = ''
zone netbird 64k ; zone netbird 64k ;
keepalive 5 ; keepalive 5 ;
@ -159,16 +173,16 @@ in {
''; '';
}; };
} }
(blockOf "vaultwarden" {maxBodySize = "1G";}) (blockOf "vaultwarden" { maxBodySize = "1G"; })
(blockOf "forgejo" {maxBodySize = "1G";}) (blockOf "forgejo" { maxBodySize = "1G"; })
(blockOf "immich" {maxBodySize = "5G";}) (blockOf "immich" { maxBodySize = "5G"; })
(proxyProtect "adguardhome" {} true) (proxyProtect "adguardhome" { } true)
(proxyProtect "oauth2-proxy" {} false) (proxyProtect "oauth2-proxy" { } false)
(blockOf "paperless" {maxBodySize = "5G";}) (blockOf "paperless" { maxBodySize = "5G"; })
(proxyProtect "ttrss" {port = 80;} true) (proxyProtect "ttrss" { port = 80; } true)
(blockOf "yourspotify" {port = 80;}) (blockOf "yourspotify" { port = 80; })
#(blockOf "homebox" {}) #(blockOf "homebox" {})
(blockOf "pr-tracker" {}) (blockOf "pr-tracker" { })
{ {
virtualHosts.${domainOf "pr-tracker"} = { virtualHosts.${domainOf "pr-tracker"} = {
locations."/update" = { locations."/update" = {
@ -176,9 +190,9 @@ in {
}; };
}; };
} }
(proxyProtect "ollama" {} true) (proxyProtect "ollama" { } true)
(proxyProtect "octoprint" {} true) (proxyProtect "octoprint" { } true)
(proxyProtect "firefly" {port = 80;} true) (proxyProtect "firefly" { port = 80; } true)
(blockOf "apispotify" { (blockOf "apispotify" {
port = 3000; port = 3000;
upstream = "yourspotify"; upstream = "yourspotify";
@ -187,8 +201,7 @@ in {
maxBodySize = "5G"; maxBodySize = "5G";
port = 80; port = 80;
}) })
(blockOf "kanidm" (blockOf "kanidm" {
{
protocol = "https"; protocol = "https";
virtualHostExtraConfig = '' virtualHostExtraConfig = ''
proxy_ssl_verify off ; proxy_ssl_verify off ;
@ -196,14 +209,18 @@ in {
}) })
]; ];
guests = let guests =
mkGuest = guestName: { let
mkGuest =
guestName:
{
enablePanzer ? false, enablePanzer ? false,
enableRenaultFT ? false, enableRenaultFT ? false,
enableBunker ? false, enableBunker ? false,
enableSharedPaperless ? false, enableSharedPaperless ? false,
... ...
}: { }:
{
autostart = true; autostart = true;
zfs."/state" = { zfs."/state" = {
pool = "rpool"; pool = "rpool";
@ -235,23 +252,25 @@ in {
../../config/services/${guestName}.nix ../../config/services/${guestName}.nix
{ {
node.secretsDir = config.node.secretsDir + "/${guestName}"; node.secretsDir = config.node.secretsDir + "/${guestName}";
networking.nftables.firewall.zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; networking.nftables.firewall.zones.untrusted.interfaces = [
config.guests.${guestName}.networking.mainLinkName
];
systemd.network.networks."10-${config.guests.${guestName}.networking.mainLinkName}" = { systemd.network.networks."10-${config.guests.${guestName}.networking.mainLinkName}" = {
DHCP = lib.mkForce "no"; DHCP = lib.mkForce "no";
address = [ address = [
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName}" config.secrets.secrets.global.net.privateSubnetv4) (lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName}" config.secrets.secrets.global.net.privateSubnetv6) }" config.secrets.secrets.global.net.privateSubnetv4)
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips."${config.guests.${guestName}.nodeName
}" config.secrets.secrets.global.net.privateSubnetv6)
]; ];
gateway = [(lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4)]; gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
}; };
} }
]; ];
}; };
mkMicrovm = guestName: cfg: { mkMicrovm = guestName: cfg: {
${guestName} = ${guestName} = mkGuest guestName cfg // {
mkGuest guestName cfg
// {
backend = "microvm"; backend = "microvm";
microvm = { microvm = {
system = "x86_64-linux"; system = "x86_64-linux";
@ -267,43 +286,39 @@ in {
}; };
mkContainer = guestName: cfg: { mkContainer = guestName: cfg: {
${guestName} = ${guestName} = mkGuest guestName cfg // {
mkGuest guestName cfg
// {
backend = "container"; backend = "container";
container.macvlan = "lan"; container.macvlan = "lan";
extraSpecialArgs = { extraSpecialArgs = {
inherit lib nodes inputs minimal stateVersion; inherit
lib
nodes
inputs
minimal
stateVersion
;
}; };
}; };
}; };
in in
{} { }
// mkContainer "adguardhome" {} // mkContainer "adguardhome" { }
// mkContainer "oauth2-proxy" {} // mkContainer "oauth2-proxy" { }
// mkContainer "vaultwarden" {} // mkContainer "vaultwarden" { }
// mkContainer "ddclient" {} // mkContainer "ddclient" { }
// mkContainer "ollama" {} // mkContainer "ollama" { }
// mkContainer "murmur" {} // mkContainer "murmur" { }
#// mkContainer "homebox" {} #// mkContainer "homebox" {}
// mkContainer "pr-tracker" {} // mkContainer "pr-tracker" { }
// mkContainer "ttrss" {} // mkContainer "ttrss" { }
// mkContainer "firefly" {} // mkContainer "firefly" { }
// mkContainer "yourspotify" {} // mkContainer "yourspotify" { }
// mkContainer "netbird" {} // mkContainer "netbird" { }
// mkContainer "kanidm" {} // mkContainer "kanidm" { }
// mkContainer "nextcloud" { // mkContainer "nextcloud" { enablePanzer = true; }
enablePanzer = true; // mkContainer "paperless" { enableSharedPaperless = true; }
} // mkContainer "forgejo" { enablePanzer = true; }
// mkContainer "paperless" { // mkMicrovm "immich" { enablePanzer = true; }
enableSharedPaperless = true;
}
// mkContainer "forgejo" {
enablePanzer = true;
}
// mkMicrovm "immich" {
enablePanzer = true;
}
// mkContainer "samba" { // mkContainer "samba" {
enablePanzer = true; enablePanzer = true;
enableRenaultFT = true; enableRenaultFT = true;

View file

@ -1,15 +1,16 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
}; };
systemd.network.networks = { systemd.network.networks = {
"10-lan01" = { "10-lan01" = {
address = [(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name} config.secrets.secrets.global.net.privateSubnetv4)]; address = [
gateway = [(lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4)]; (lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name}
config.secrets.secrets.global.net.privateSubnetv4
)
];
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
#matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac; #matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
matchConfig.Name = "lan"; matchConfig.Name = "lan";
dhcpV6Config.UseDNS = false; dhcpV6Config.UseDNS = false;
@ -33,8 +34,12 @@
networks = { networks = {
# redo the network cause the livesystem has macvlans # redo the network cause the livesystem has macvlans
"10-lan01" = { "10-lan01" = {
address = [(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name} config.secrets.secrets.global.net.privateSubnetv4)]; address = [
gateway = [(lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4)]; (lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name}
config.secrets.secrets.global.net.privateSubnetv4
)
];
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ];
matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac; matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
dhcpV6Config.UseDNS = false; dhcpV6Config.UseDNS = false;
dhcpV4Config.UseDNS = false; dhcpV4Config.UseDNS = false;
@ -46,11 +51,16 @@
}; };
}; };
}; };
networking.nftables.firewall.zones.untrusted.interfaces = ["lan"]; networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" ];
wireguard.elisabeth.server = { wireguard.elisabeth.server = {
host = lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name} config.secrets.secrets.global.net.privateSubnetv4; host =
reservedAddresses = ["10.42.0.0/20" "fd00:1764::/112"]; lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name}
config.secrets.secrets.global.net.privateSubnetv4;
reservedAddresses = [
"10.42.0.0/20"
"fd00:1764::/112"
];
openFirewall = true; openFirewall = true;
}; };
# To be able to ping containers from the host, it is necessary # To be able to ping containers from the host, it is necessary
@ -71,7 +81,7 @@
email = config.secrets.secrets.global.devEmail; email = config.secrets.secrets.global.devEmail;
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
dnsPropagationCheck = true; dnsPropagationCheck = true;
reloadServices = ["nginx"]; reloadServices = [ "nginx" ];
credentialFiles = { credentialFiles = {
"CF_DNS_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path; "CF_DNS_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
"CF_ZONE_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path; "CF_ZONE_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
@ -80,9 +90,9 @@
}; };
security.acme.certs.web = { security.acme.certs.web = {
domain = config.secrets.secrets.global.domains.web; domain = config.secrets.secrets.global.domains.web;
extraDomainNames = ["*.${config.secrets.secrets.global.domains.web}"]; extraDomainNames = [ "*.${config.secrets.secrets.global.domains.web}" ];
}; };
users.groups.acme.members = ["nginx"]; users.groups.acme.members = [ "nginx" ];
environment.persistence."/state".directories = [ environment.persistence."/state".directories = [
{ {
directory = "/var/lib/acme"; directory = "/var/lib/acme";

View file

@ -9,6 +9,11 @@
./fs.nix ./fs.nix
]; ];
boot.mode = "bios"; boot.mode = "bios";
boot.initrd.availableKernelModules = ["virtio_pci" "virtio_net" "virtio_scsi" "virtio_blk"]; boot.initrd.availableKernelModules = [
"virtio_pci"
"virtio_net"
"virtio_scsi"
"virtio_blk"
];
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
} }

View file

@ -1,8 +1,5 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
disko.devices = { disko.devices = {
disk = { disk = {
drive = rec { drive = rec {
@ -11,9 +8,15 @@
content = with lib.disko.gpt; { content = with lib.disko.gpt; {
type = "gpt"; type = "gpt";
partitions = { partitions = {
grub = partGrub // {device = "${device}-part1";}; grub = partGrub // {
bios = (partEfi "512MiB") // {device = "${device}-part2";}; device = "${device}-part1";
rpool = (partLuksZfs "rpool" "rpool" "100%") // {device = "${device}-part3";}; };
bios = (partEfi "512MiB") // {
device = "${device}-part2";
};
rpool = (partLuksZfs "rpool" "rpool" "100%") // {
device = "${device}-part3";
};
#(lib.attrsets.recursiveUpdate (partLuksZfs "rpool" "rpool" "17GiB" "100%") {content.extraFormatArgs = ["--pbkdf pbkdf2"];}) #(lib.attrsets.recursiveUpdate (partLuksZfs "rpool" "rpool" "17GiB" "100%") {content.extraFormatArgs = ["--pbkdf pbkdf2"];})
}; };
}; };
@ -21,13 +24,11 @@
}; };
zpool = with lib.disko.zfs; { zpool = with lib.disko.zfs; {
rpool = mkZpool {datasets = impermanenceZfsDatasets;}; rpool = mkZpool { datasets = impermanenceZfsDatasets; };
}; };
}; };
fileSystems."/state".neededForBoot = true; fileSystems."/state".neededForBoot = true;
fileSystems."/persist".neededForBoot = true; fileSystems."/persist".neededForBoot = true;
boot.loader.grub.devices = [ boot.loader.grub.devices = [ "/dev/disk/by-id/${config.secrets.secrets.local.disko.drive}" ];
"/dev/disk/by-id/${config.secrets.secrets.local.disko.drive}"
];
} }

View file

@ -1,27 +1,28 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
networking.hostId = config.secrets.secrets.local.networking.hostId; networking.hostId = config.secrets.secrets.local.networking.hostId;
networking.domain = config.secrets.secrets.global.domains.mail_public; networking.domain = config.secrets.secrets.global.domains.mail_public;
boot.initrd.systemd.network = { boot.initrd.systemd.network = {
enable = true; enable = true;
networks = {inherit (config.systemd.network.networks) "lan01";}; networks = {
inherit (config.systemd.network.networks) "lan01";
};
}; };
systemd.network.networks = { systemd.network.networks = {
"lan01" = let "lan01" =
let
icfg = config.secrets.secrets.local.networking.interfaces.lan01; icfg = config.secrets.secrets.local.networking.interfaces.lan01;
in { in
{
address = [ address = [
icfg.hostCidrv4 icfg.hostCidrv4
(lib.net.cidr.hostCidr 1 icfg.hostCidrv6) (lib.net.cidr.hostCidr 1 icfg.hostCidrv6)
]; ];
gateway = ["fe80::1"]; gateway = [ "fe80::1" ];
routes = [ routes = [
{Destination = "172.31.1.1";} { Destination = "172.31.1.1"; }
{ {
Gateway = "172.31.1.1"; Gateway = "172.31.1.1";
GatewayOnLink = true; GatewayOnLink = true;
@ -43,25 +44,25 @@
email = config.secrets.secrets.global.devEmail; email = config.secrets.secrets.global.devEmail;
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
dnsPropagationCheck = true; dnsPropagationCheck = true;
reloadServices = ["nginx"]; reloadServices = [ "nginx" ];
credentialFiles = { credentialFiles = {
"CF_DNS_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path; "CF_DNS_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
"CF_ZONE_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path; "CF_ZONE_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
}; };
}; };
}; };
networking.nftables.firewall.zones.untrusted.interfaces = ["lan01"]; networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ];
security.acme.certs = { security.acme.certs = {
mail_public = { mail_public = {
domain = config.secrets.secrets.global.domains.mail_public; domain = config.secrets.secrets.global.domains.mail_public;
extraDomainNames = ["*.${config.secrets.secrets.global.domains.mail_public}"]; extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_public}" ];
}; };
mail_private = { mail_private = {
domain = config.secrets.secrets.global.domains.mail_private; domain = config.secrets.secrets.global.domains.mail_private;
extraDomainNames = ["*.${config.secrets.secrets.global.domains.mail_private}"]; extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_private}" ];
}; };
}; };
users.groups.acme.members = ["maddy"]; users.groups.acme.members = [ "maddy" ];
environment.persistence."/state".directories = [ environment.persistence."/state".directories = [
{ {
directory = "/var/lib/acme"; directory = "/var/lib/acme";

View file

@ -1,8 +1,5 @@
{ inputs, lib, ... }:
{ {
inputs,
lib,
...
}: {
imports = [ imports = [
../../config/basic ../../config/basic
../../config/services/octoprint.nix ../../config/services/octoprint.nix

View file

@ -1,9 +1,10 @@
{lib, ...}: { { lib, ... }:
{
fileSystems = lib.mkForce { fileSystems = lib.mkForce {
"/" = { "/" = {
device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888"; device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4"; fsType = "ext4";
}; };
}; };
environment.persistence = lib.mkForce {}; environment.persistence = lib.mkForce { };
} }

View file

@ -1,4 +1,5 @@
{config, ...}: { { config, ... }:
{
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
wireless.iwd = { wireless.iwd = {

View file

@ -1,8 +1,5 @@
{ inputs, lib, ... }:
{ {
inputs,
lib,
...
}: {
imports = [ imports = [
inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-cpu-intel
# for some reasons the cpu-intel includes the gpu as well # for some reasons the cpu-intel includes the gpu as well
@ -45,11 +42,12 @@
layout = "de"; layout = "de";
}; };
libinput = { libinput = {
touchpad = lib.mkForce { touchpad = lib.mkForce { accelSpeed = "0.5"; };
accelSpeed = "0.5";
};
}; };
}; };
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
nix.settings.system-features = ["kvm" "nixos-test"]; nix.settings.system-features = [
"kvm"
"nixos-test"
];
} }

View file

@ -1,8 +1,5 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
disko.devices = { disko.devices = {
disk = { disk = {
m2-ssd = rec { m2-ssd = rec {
@ -11,15 +8,21 @@
content = with lib.disko.gpt; { content = with lib.disko.gpt; {
type = "gpt"; type = "gpt";
partitions = { partitions = {
boot = (partEfi "1GiB") // {device = "${device}-part1";}; boot = (partEfi "1GiB") // {
swap = (partSwap "16GiB") // {device = "${device}-part2";}; device = "${device}-part1";
rpool = (partLuksZfs "rpool" "rpool" "100%") // {device = "${device}-part3";}; };
swap = (partSwap "16GiB") // {
device = "${device}-part2";
};
rpool = (partLuksZfs "rpool" "rpool" "100%") // {
device = "${device}-part3";
};
}; };
}; };
}; };
}; };
zpool = with lib.disko.zfs; { zpool = with lib.disko.zfs; {
rpool = mkZpool {datasets = impermanenceZfsDatasets;}; rpool = mkZpool { datasets = impermanenceZfsDatasets; };
}; };
}; };
fileSystems."/state".neededForBoot = true; fileSystems."/state".neededForBoot = true;

View file

@ -1,4 +1,5 @@
{config, ...}: { { config, ... }:
{
age.secrets.eduroam = { age.secrets.eduroam = {
rekeyFile = ./secrets/iwd/eduroam.8021x.age; rekeyFile = ./secrets/iwd/eduroam.8021x.age;
path = "/var/lib/iwd/eduroam.8021x"; path = "/var/lib/iwd/eduroam.8021x";
@ -10,7 +11,11 @@
devoloog-sae20.rekeyFile = ./secrets/iwd/devoloog-sae20.age; devoloog-sae20.rekeyFile = ./secrets/iwd/devoloog-sae20.age;
}; };
wireguard.samba-patrick.client.via = "elisabeth-samba"; wireguard.samba-patrick.client.via = "elisabeth-samba";
networking.nftables.firewall.zones.untrusted.interfaces = ["lan01" "lan02" "wlan01"]; networking.nftables.firewall.zones.untrusted.interfaces = [
"lan01"
"lan02"
"wlan01"
];
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
wireless.iwd = { wireless.iwd = {
@ -36,7 +41,7 @@
IPv6PrivacyExtensions = "yes"; IPv6PrivacyExtensions = "yes";
MulticastDNS = true; MulticastDNS = true;
}; };
dns = ["1.1.1.1"]; dns = [ "1.1.1.1" ];
dhcpV4Config.RouteMetric = 10; dhcpV4Config.RouteMetric = 10;
dhcpV6Config.RouteMetric = 10; dhcpV6Config.RouteMetric = 10;
}; };
@ -47,7 +52,7 @@
IPv6PrivacyExtensions = "yes"; IPv6PrivacyExtensions = "yes";
MulticastDNS = true; MulticastDNS = true;
}; };
dns = ["1.1.1.1"]; dns = [ "1.1.1.1" ];
dhcpV4Config.RouteMetric = 10; dhcpV4Config.RouteMetric = 10;
dhcpV6Config.RouteMetric = 10; dhcpV6Config.RouteMetric = 10;
}; };
@ -58,7 +63,7 @@
IPv6PrivacyExtensions = "yes"; IPv6PrivacyExtensions = "yes";
MulticastDNS = true; MulticastDNS = true;
}; };
dns = ["1.1.1.1"]; dns = [ "1.1.1.1" ];
dhcpV4Config.RouteMetric = 40; dhcpV4Config.RouteMetric = 40;
dhcpV6Config.RouteMetric = 40; dhcpV6Config.RouteMetric = 40;
}; };

View file

@ -1,3 +1 @@
inputs: [ inputs: [ (import ./misc.nix inputs) ]
(import ./misc.nix inputs)
]

View file

@ -1,11 +1,9 @@
_inputs: _self: super: let _inputs: _self: super:
let
writeText = text: (super.writeText (builtins.hashString "sha256" "${text}") "${text}"); writeText = text: (super.writeText (builtins.hashString "sha256" "${text}") "${text}");
in { in
lib = {
super.lib lib = super.lib // {
// { inherit writeText;
inherit
writeText
;
}; };
} }

View file

@ -1,10 +1,6 @@
{ { lib, config, ... }:
lib, let
config, inherit (lib)
...
}: let
inherit
(lib)
mkEnableOption mkEnableOption
mkMerge mkMerge
attrNames attrNames
@ -18,7 +14,8 @@
mapAttrs' mapAttrs'
listToAttrs listToAttrs
; ;
in { in
{
home-manager.sharedModules = [ home-manager.sharedModules = [
{ {
options.images = { options.images = {
@ -39,16 +36,15 @@ in {
imports = [ imports = [
( (
{config, ...}: { { config, ... }:
{
age.secrets = mkMerge ( age.secrets = mkMerge (
flip map flip map (attrNames config.home-manager.users) (
(attrNames config.home-manager.users)
(
user: user:
mkIf config.home-manager.users.${user}.images.enable ( mkIf config.home-manager.users.${user}.images.enable (
listToAttrs (flip map (attrNames (filterAttrs (_: type: type == "regular") (builtins.readDir ../secrets/img))) listToAttrs (
( flip map (attrNames (filterAttrs (_: type: type == "regular") (builtins.readDir ../secrets/img)))
file: { (file: {
name = "images-${user}-${file}"; name = "images-${user}-${file}";
value = { value = {
name = removeSuffix ".age" file; name = removeSuffix ".age" file;
@ -56,8 +52,8 @@ in {
owner = user; owner = user;
group = user; group = user;
}; };
} })
)) )
) )
) )
); );

View file

@ -1,10 +1,6 @@
{ { config, lib, ... }:
config, let
lib, inherit (lib)
...
}: let
inherit
(lib)
flip flip
mapAttrs mapAttrs
attrNames attrNames
@ -13,7 +9,8 @@
mkMerge mkMerge
isAttrs isAttrs
; ;
in { in
{
# Expose a home manager module for each user that allows extending # Expose a home manager module for each user that allows extending
# environment.persistence.${sourceDir}.users.${userName} simply by # environment.persistence.${sourceDir}.users.${userName} simply by
# specifying home.persistence.${sourceDir} in home manager. # specifying home.persistence.${sourceDir} in home manager.
@ -21,54 +18,47 @@ in {
{ {
options.home.persistence = mkOption { options.home.persistence = mkOption {
description = "Additional persistence config for the given source path"; description = "Additional persistence config for the given source path";
default = {}; default = { };
type = types.attrsOf (types.submodule { type = types.attrsOf (
types.submodule {
options = { options = {
files = mkOption { files = mkOption {
description = "Additional files to persist via NixOS impermanence."; description = "Additional files to persist via NixOS impermanence.";
type = types.listOf (types.either types.attrs types.str); type = types.listOf (types.either types.attrs types.str);
default = []; default = [ ];
}; };
directories = mkOption { directories = mkOption {
description = "Additional directories to persist via NixOS impermanence."; description = "Additional directories to persist via NixOS impermanence.";
type = types.listOf (types.either types.attrs types.str); type = types.listOf (types.either types.attrs types.str);
default = []; default = [ ];
}; };
}; };
}); }
);
}; };
} }
]; ];
# For each user that has a home-manager config, merge the locally defined # For each user that has a home-manager config, merge the locally defined
# persistence options that we defined above. # persistence options that we defined above.
imports = let imports =
mkUserFiles = map (x: let
{parentDirectory.mode = "700";} mkUserFiles = map (
// ( x: { parentDirectory.mode = "700"; } // (if isAttrs x then x else { file = x; })
if isAttrs x );
then x mkUserDirs = map (x: { mode = "700"; } // (if isAttrs x then x else { directory = x; }));
else {file = x;} in
)); [
mkUserDirs = map (x:
{mode = "700";}
// (
if isAttrs x
then x
else {directory = x;}
));
in [
{ {
environment.persistence = mkMerge ( environment.persistence = mkMerge (
flip map flip map (attrNames config.home-manager.users) (
(attrNames config.home-manager.users) user:
( let
user: let
hmUserCfg = config.home-manager.users.${user}; hmUserCfg = config.home-manager.users.${user};
in in
flip mapAttrs hmUserCfg.home.persistence flip mapAttrs hmUserCfg.home.persistence (
(_: sourceCfg: { _: sourceCfg: {
users.${user} = { users.${user} = {
# This needs to be set for allo users with non # This needs to be set for allo users with non
# standart home (not /home/<userName> # standart home (not /home/<userName>
@ -77,16 +67,12 @@ in {
# as there will be infinite recursion # as there will be infinite recursion
# If this setting is forgotten there # If this setting is forgotten there
# are assertions in place warning you # are assertions in place warning you
home = home = { root = "/root"; }.${user} or "/home/${user}";
{
root = "/root";
}
.${user}
or "/home/${user}";
files = mkUserFiles sourceCfg.files; files = mkUserFiles sourceCfg.files;
directories = mkUserDirs sourceCfg.directories; directories = mkUserDirs sourceCfg.directories;
}; };
}) }
)
) )
); );
} }

View file

@ -1,20 +1,19 @@
{ { lib, pkgs, ... }:
lib, let
pkgs, inherit (lib)
...
}: let
inherit
(lib)
types types
mkEnableOption mkEnableOption
mkPackageOption mkPackageOption
mkOption mkOption
mkIf mkIf
; ;
settingsFormat = pkgs.formats.json {}; settingsFormat = pkgs.formats.json { };
in { in
{
home-manager.sharedModules = [ home-manager.sharedModules = [
({config, ...}: let (
{ config, ... }:
let
cfg = settingsFormat.generate "config.json" { cfg = settingsFormat.generate "config.json" {
streamdeck_ui_version = 2; streamdeck_ui_version = 2;
state = config.programs.streamdeck-ui.settings; state = config.programs.streamdeck-ui.settings;
@ -22,13 +21,14 @@ in {
preStart = pkgs.writeShellScript "streamdeck-setup-config" '' preStart = pkgs.writeShellScript "streamdeck-setup-config" ''
${pkgs.coreutils}/bin/cp "${cfg}" "$XDG_RUNTIME_DIR/streamdeck/config.json" ${pkgs.coreutils}/bin/cp "${cfg}" "$XDG_RUNTIME_DIR/streamdeck/config.json"
''; '';
in { in
{
options.programs.streamdeck-ui = { options.programs.streamdeck-ui = {
enable = mkEnableOption "streamdeck-ui"; enable = mkEnableOption "streamdeck-ui";
package = mkPackageOption pkgs "streamdeck-ui" {}; package = mkPackageOption pkgs "streamdeck-ui" { };
settings = mkOption { settings = mkOption {
default = {}; default = { };
type = types.submodule {freeformType = settingsFormat.type;}; type = types.submodule { freeformType = settingsFormat.type; };
description = "Configuration per streamdeck"; description = "Configuration per streamdeck";
}; };
}; };
@ -39,7 +39,7 @@ in {
Unit = { Unit = {
Description = "start streamdeck-ui"; Description = "start streamdeck-ui";
# For some reason this depends on X or wayland running # For some reason this depends on X or wayland running
ConditionEnvironment = ["DISPLAY"]; ConditionEnvironment = [ "DISPLAY" ];
}; };
Service = { Service = {
Type = "exec"; Type = "exec";
@ -48,11 +48,12 @@ in {
Environment = ''STREAMDECK_UI_CONFIG=%t/streamdeck/config.json''; Environment = ''STREAMDECK_UI_CONFIG=%t/streamdeck/config.json'';
RuntimeDirectory = "streamdeck"; RuntimeDirectory = "streamdeck";
}; };
Install.WantedBy = ["graphical-session.target"]; Install.WantedBy = [ "graphical-session.target" ];
}; };
}; };
}; };
}; };
}) }
)
]; ];
} }

View file

@ -3,9 +3,9 @@
pkgs, pkgs,
config, config,
... ...
}: let }:
inherit let
(lib) inherit (lib)
types types
mkEnableOption mkEnableOption
mkPackageOption mkPackageOption
@ -15,13 +15,14 @@
cfg = config.services.actual; cfg = config.services.actual;
configFile = formatType.generate "config.json" cfg.settings; configFile = formatType.generate "config.json" cfg.settings;
formatType = pkgs.formats.json {}; formatType = pkgs.formats.json { };
in { in
{
options.services.actual = { options.services.actual = {
enable = mkEnableOption "actual, a privacy focused app for managing your finances"; enable = mkEnableOption "actual, a privacy focused app for managing your finances";
package = mkPackageOption pkgs "actual" {}; package = mkPackageOption pkgs "actual" { };
settings = mkOption { settings = mkOption {
default = {}; default = { };
type = types.submodule { type = types.submodule {
freeformType = formatType.type; freeformType = formatType.type;
config = { config = {
@ -33,7 +34,7 @@ in {
}; };
}; };
config.systemd.services.actual = { config.systemd.services.actual = {
after = ["network.target"]; after = [ "network.target" ];
environment.ACTUAL_CONFIG_PATH = configFile; environment.ACTUAL_CONFIG_PATH = configFile;
serviceConfig = { serviceConfig = {
ExecStartPre = "${pkgs.coreutils}/bin/ln -sf ${cfg.package}/migrations /var/lib/actual/"; ExecStartPre = "${pkgs.coreutils}/bin/ln -sf ${cfg.package}/migrations /var/lib/actual/";
@ -78,6 +79,6 @@ in {
]; ];
UMask = "0077"; UMask = "0077";
}; };
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
}; };
} }

View file

@ -1,10 +1,6 @@
{ { lib, config, ... }:
lib, let
config, inherit (lib)
...
}: let
inherit
(lib)
concatLists concatLists
flip flip
mapAttrsToList mapAttrsToList
@ -16,16 +12,18 @@
; ;
cfg = config.users.deterministicIds; cfg = config.users.deterministicIds;
in { in
{
options = { options = {
users.deterministicIds = mkOption { users.deterministicIds = mkOption {
default = {}; default = { };
description = mdDoc '' description = mdDoc ''
Maps a user or group name to its expected uid/gid values. If a user/group is Maps a user or group name to its expected uid/gid values. If a user/group is
used on the system without specifying a uid/gid, this module will assign the used on the system without specifying a uid/gid, this module will assign the
corresponding ids defined here, or show an error if the definition is missing. corresponding ids defined here, or show an error if the definition is missing.
''; '';
type = types.attrsOf (types.submodule { type = types.attrsOf (
types.submodule {
options = { options = {
uid = mkOption { uid = mkOption {
type = types.nullOr types.int; type = types.nullOr types.int;
@ -38,31 +36,46 @@ in {
description = mdDoc "The gid to assign if it is missing in `users.groups.<name>`."; description = mdDoc "The gid to assign if it is missing in `users.groups.<name>`.";
}; };
}; };
}); }
);
}; };
users.users = mkOption { users.users = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: { type = types.attrsOf (
config.uid = let types.submodule (
{ name, ... }:
{
config.uid =
let
deterministicUid = cfg.${name}.uid or null; deterministicUid = cfg.${name}.uid or null;
in in
mkIf (deterministicUid != null) (mkDefault deterministicUid); mkIf (deterministicUid != null) (mkDefault deterministicUid);
})); }
)
);
}; };
users.groups = mkOption { users.groups = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: { type = types.attrsOf (
config.gid = let types.submodule (
{ name, ... }:
{
config.gid =
let
deterministicGid = cfg.${name}.gid or null; deterministicGid = cfg.${name}.gid or null;
in in
mkIf (deterministicGid != null) (mkDefault deterministicGid); mkIf (deterministicGid != null) (mkDefault deterministicGid);
})); }
)
);
}; };
}; };
config = { config = {
assertions = assertions =
concatLists (flip mapAttrsToList config.users.users (name: user: [ concatLists (
flip mapAttrsToList config.users.users (
name: user: [
{ {
assertion = user.uid != null; assertion = user.uid != null;
message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`"; message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`";
@ -71,10 +84,14 @@ in {
assertion = !user.autoSubUidGidRange; assertion = !user.autoSubUidGidRange;
message = "non-deterministic subUids/subGids detected for: ${name}"; message = "non-deterministic subUids/subGids detected for: ${name}";
} }
])) ]
++ flip mapAttrsToList config.users.groups (name: group: { )
)
++ flip mapAttrsToList config.users.groups (
name: group: {
assertion = group.gid != null; assertion = group.gid != null;
message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`"; message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`";
}); }
);
}; };
} }

View file

@ -3,9 +3,9 @@
lib, lib,
nodes, nodes,
... ...
}: let }:
inherit let
(lib) inherit (lib)
attrNames attrNames
concatMap concatMap
concatStringsSep concatStringsSep
@ -21,16 +21,20 @@
; ;
nodeName = config.node.name; nodeName = config.node.name;
mkForwardedOption = path: mkForwardedOption =
path:
mkOption { mkOption {
type = mkOptionType { type = mkOptionType {
name = "Same type that the receiving option `${concatStringsSep "." path}` normally accepts."; name = "Same type that the receiving option `${concatStringsSep "." path}` normally accepts.";
merge = _loc: defs: merge =
builtins.filter _loc: defs:
(x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) builtins.filter (x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) (
(map (x: x.value) defs); map (x: x.value) defs
);
};
default = {
_type = "__distributed_config_empty";
}; };
default = {_type = "__distributed_config_empty";};
description = '' description = ''
Anything specified here will be forwarded to `${concatStringsSep "." path}` Anything specified here will be forwarded to `${concatStringsSep "." path}`
on the given node. Forwarding happens as-is to the raw values, on the given node. Forwarding happens as-is to the raw values,
@ -39,23 +43,34 @@
}; };
forwardedOptions = [ forwardedOptions = [
["age" "secrets"] [
["services" "maddy" "ensureCredentials"] "age"
"secrets"
]
[
"services"
"maddy"
"ensureCredentials"
]
]; ];
attrsForEachOption = f: (foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) {} forwardedOptions); attrsForEachOption =
in { f: (foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) { } forwardedOptions);
in
{
options.nodes = mkOption { options.nodes = mkOption {
description = "Options forwareded to the given node."; description = "Options forwareded to the given node.";
default = {}; default = { };
type = types.attrsOf (types.submodule { type = types.attrsOf (types.submodule { options = attrsForEachOption mkForwardedOption; });
options = attrsForEachOption mkForwardedOption;
});
}; };
config = let config =
mergeConfigFromOthers = let let
getConfig = path: otherNode: let mergeConfigFromOthers =
let
getConfig =
path: otherNode:
let
cfg = nodes.${otherNode}.config.nodes.${nodeName} or null; cfg = nodes.${otherNode}.config.nodes.${nodeName} or null;
in in
optionals (cfg != null) (getAttrFromPath path cfg); optionals (cfg != null) (getAttrFromPath path cfg);

View file

@ -3,20 +3,21 @@
config, config,
pkgs, pkgs,
... ...
}: let }:
let
cfg = config.services.homebox; cfg = config.services.homebox;
inherit inherit (lib)
(lib)
mkEnableOption mkEnableOption
mkPackageOption mkPackageOption
mkDefault mkDefault
types types
mkIf mkIf
; ;
in { in
{
options.services.homebox = { options.services.homebox = {
enable = mkEnableOption "homebox"; enable = mkEnableOption "homebox";
package = mkPackageOption pkgs "homebox" {}; package = mkPackageOption pkgs "homebox" { };
settings = lib.mkOption { settings = lib.mkOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
defaultText = '' defaultText = ''
@ -39,7 +40,7 @@ in {
HBOX_MODE = mkDefault "production"; HBOX_MODE = mkDefault "production";
}; };
systemd.services.homebox = { systemd.services.homebox = {
after = ["network.target"]; after = [ "network.target" ];
environment = cfg.settings; environment = cfg.settings;
serviceConfig = { serviceConfig = {
User = "homebox"; User = "homebox";
@ -86,8 +87,8 @@ in {
# System Call Filtering # System Call Filtering
UMask = "0077"; UMask = "0077";
}; };
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
}; };
}; };
meta.maintainers = with lib.maintainers; [patrickdag]; meta.maintainers = with lib.maintainers; [ patrickdag ];
} }

View file

@ -3,18 +3,20 @@
pkgs, pkgs,
config, config,
... ...
}: { }:
options.networking.wireless.iwd = let {
inherit options.networking.wireless.iwd =
(lib) let
inherit (lib)
mkOption mkOption
literalExample literalExample
types types
hasAttrByPath hasAttrByPath
; ;
in { in
{
networks = mkOption { networks = mkOption {
default = {}; default = { };
example = literalExample '' example = literalExample ''
{ "karlsruhe.freifunk.net" = {}; { "karlsruhe.freifunk.net" = {};
}; };
@ -32,35 +34,57 @@
<citerefentry><refentrytitle>iwctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <citerefentry><refentrytitle>iwctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
''; '';
type = types.attrsOf (types.submodule ({config, ...}: { type = types.attrsOf (
types.submodule (
{ config, ... }:
{
config.kind = config.kind =
if (hasAttrByPath ["Security" "Passphrase"] config.settings) if
then "psk" (hasAttrByPath [
else if !(hasAttrByPath ["Security"] config.settings) "Security"
then "open" "Passphrase"
else "8021x"; ] config.settings)
then
"psk"
else if !(hasAttrByPath [ "Security" ] config.settings) then
"open"
else
"8021x";
options = { options = {
kind = mkOption { kind = mkOption {
type = types.enum ["open" "psk" "8021x"]; type = types.enum [
"open"
"psk"
"8021x"
];
description = "The type of network. This will determine the file ending. The module will try to determine this automatically so this should only be set when the heuristics fail."; description = "The type of network. This will determine the file ending. The module will try to determine this automatically so this should only be set when the heuristics fail.";
}; };
settings = mkOption { settings = mkOption {
type = with types; (attrsOf (attrsOf (oneOf [str path]))); type =
with types;
(attrsOf (
attrsOf (oneOf [
str
path
])
));
description = '' description = ''
Contents of the iwd config file for this network Contents of the iwd config file for this network
The lowest level values should be files, that will be read into the config files The lowest level values should be files, that will be read into the config files
''; '';
default = {}; default = { };
}; };
}; };
})); }
)
);
}; };
}; };
config = let config =
inherit let
(lib) inherit (lib)
mkIf mkIf
flip flip
mapAttrsToList mapAttrsToList
@ -81,24 +105,38 @@
''; '';
in in
mkIf cfg.enable { mkIf cfg.enable {
systemd.services.iwd = mkIf (cfg.networks != {}) { systemd.services.iwd = mkIf (cfg.networks != { }) {
path = [encoder]; path = [ encoder ];
preStart = let preStart =
let
dataDir = "/var/lib/iwd"; dataDir = "/var/lib/iwd";
in '' in
''
# Create config files for declaratively defined networks in the NixOS config. # Create config files for declaratively defined networks in the NixOS config.
${concatStringsSep "\n" (flip mapAttrsToList cfg.networks (network: config: '' ${concatStringsSep "\n" (
flip mapAttrsToList cfg.networks (
network: config: ''
filename=${dataDir}/"$(encoder '${network}.${config.kind}')" filename=${dataDir}/"$(encoder '${network}.${config.kind}')"
touch "$filename" touch "$filename"
cat >$filename <<EOF cat >$filename <<EOF
${concatStringsSep "\n" (flip mapAttrsToList config.settings (toplevel: config: '' ${concatStringsSep "\n" (
flip mapAttrsToList config.settings (
toplevel: config: ''
[${toplevel}] [${toplevel}]
${concatStringsSep "\n" (flip mapAttrsToList config (name: value: '' ${concatStringsSep "\n" (
flip mapAttrsToList config (
name: value: ''
${name}=$(<${value}) ${name}=$(<${value})
''))} ''
''))} )
)}
''
)
)}
EOF EOF
''))} ''
)
)}
''; '';
}; };
}; };

View file

@ -4,9 +4,9 @@
options, options,
pkgs, pkgs,
... ...
}: let }:
inherit let
(lib) inherit (lib)
any any
attrNames attrNames
attrValues attrValues
@ -40,25 +40,32 @@
; ;
cfg = config.services.kanidm; cfg = config.services.kanidm;
settingsFormat = pkgs.formats.toml {}; settingsFormat = pkgs.formats.toml { };
# Remove null values, so we can document optional values that don't end up in the generated TOML file. # Remove null values, so we can document optional values that don't end up in the generated TOML file.
filterConfig = converge (filterAttrsRecursive (_: v: v != null)); filterConfig = converge (filterAttrsRecursive (_: v: v != null));
serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings); serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings);
clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings); clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings);
unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings); unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings);
certPaths = builtins.map builtins.dirOf [cfg.serverSettings.tls_chain cfg.serverSettings.tls_key]; certPaths = builtins.map builtins.dirOf [
cfg.serverSettings.tls_chain
cfg.serverSettings.tls_key
];
# Merge bind mount paths and remove paths where a prefix is already mounted. # Merge bind mount paths and remove paths where a prefix is already mounted.
# This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount # This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount
# paths, no new bind mount is added. Adding subpaths caused problems on ofborg. # paths, no new bind mount is added. Adding subpaths caused problems on ofborg.
hasPrefixInList = list: newPath: any (path: hasPrefix (builtins.toString path) (builtins.toString newPath)) list; hasPrefixInList =
mergePaths = foldl' (merged: newPath: let list: newPath: any (path: hasPrefix (builtins.toString path) (builtins.toString newPath)) list;
mergePaths = foldl' (
merged: newPath:
let
# If the new path is a prefix to some existing path, we need to filter it out # If the new path is a prefix to some existing path, we need to filter it out
filteredPaths = filter (p: !hasPrefix (builtins.toString newPath) (builtins.toString p)) merged; filteredPaths = filter (p: !hasPrefix (builtins.toString newPath) (builtins.toString p)) merged;
# If a prefix of the new path is already in the list, do not add it # If a prefix of the new path is already in the list, do not add it
filteredNew = optional (!hasPrefixInList filteredPaths newPath) newPath; filteredNew = optional (!hasPrefixInList filteredPaths newPath) newPath;
in in
filteredPaths ++ filteredNew) []; filteredPaths ++ filteredNew
) [ ];
defaultServiceConfig = { defaultServiceConfig = {
BindReadOnlyPaths = [ BindReadOnlyPaths = [
@ -68,7 +75,7 @@
"-/etc/hosts" "-/etc/hosts"
"-/etc/localtime" "-/etc/localtime"
]; ];
CapabilityBoundingSet = []; CapabilityBoundingSet = [ ];
# ProtectClock= adds DeviceAllow=char-rtc r # ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = ""; DeviceAllow = "";
# Implies ProtectSystem=strict, which re-mounts all paths # Implies ProtectSystem=strict, which re-mounts all paths
@ -92,17 +99,21 @@
ProtectKernelModules = true; ProtectKernelModules = true;
ProtectKernelTunables = true; ProtectKernelTunables = true;
ProtectProc = "invisible"; ProtectProc = "invisible";
RestrictAddressFamilies = []; RestrictAddressFamilies = [ ];
RestrictNamespaces = true; RestrictNamespaces = true;
RestrictRealtime = true; RestrictRealtime = true;
RestrictSUIDSGID = true; RestrictSUIDSGID = true;
SystemCallArchitectures = "native"; SystemCallArchitectures = "native";
SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"]; SystemCallFilter = [
"@system-service"
"~@privileged @resources @setuid @keyring"
];
# Does not work well with the temporary root # Does not work well with the temporary root
#UMask = "0066"; #UMask = "0066";
}; };
mkPresentOption = what: mkPresentOption =
what:
mkOption { mkOption {
description = mdDoc "Whether to ensure that this ${what} is present or absent."; description = mdDoc "Whether to ensure that this ${what} is present or absent.";
type = types.bool; type = types.bool;
@ -111,20 +122,21 @@
filterPresent = filterAttrs (_: v: v.present); filterPresent = filterAttrs (_: v: v.present);
provisionStateJson = pkgs.writeText "provision-state.json" (builtins.toJSON { provisionStateJson = pkgs.writeText "provision-state.json" (
inherit (cfg.provision) groups persons systems; builtins.toJSON { inherit (cfg.provision) groups persons systems; }
}); );
serverPort = serverPort =
# ipv6: # ipv6:
if hasInfix "]:" cfg.serverSettings.bindaddress if hasInfix "]:" cfg.serverSettings.bindaddress then
then last (splitString "]:" cfg.serverSettings.bindaddress) last (splitString "]:" cfg.serverSettings.bindaddress)
else else
# ipv4: # ipv4:
if hasInfix "." cfg.serverSettings.bindaddress if hasInfix "." cfg.serverSettings.bindaddress then
then last (splitString ":" cfg.serverSettings.bindaddress) last (splitString ":" cfg.serverSettings.bindaddress)
# default is 8443 # default is 8443
else "8443"; else
"8443";
# Only recover the admin account if a password should explicitly be provisioned # Only recover the admin account if a password should explicitly be provisioned
# for the account. Otherwise it is not needed for provisioning. # for the account. Otherwise it is not needed for provisioning.
@ -141,8 +153,8 @@
# for the account we set it, otherwise we generate a new one because it is required # for the account we set it, otherwise we generate a new one because it is required
# for provisioning. # for provisioning.
recoverIdmAdmin = recoverIdmAdmin =
if cfg.provision.idmAdminPasswordFile != null if cfg.provision.idmAdminPasswordFile != null then
then '' ''
KANIDM_IDM_ADMIN_PASSWORD=$(< ${cfg.provision.idmAdminPasswordFile}) KANIDM_IDM_ADMIN_PASSWORD=$(< ${cfg.provision.idmAdminPasswordFile})
# We always reset the idm_admin account password if a desired password was specified. # We always reset the idm_admin account password if a desired password was specified.
if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_IDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin --from-environment >/dev/null; then if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_IDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin --from-environment >/dev/null; then
@ -150,7 +162,8 @@
exit 1 exit 1
fi fi
'' ''
else '' else
''
# Recover idm_admin account # Recover idm_admin account
if ! recover_out=$(${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin -o json); then if ! recover_out=$(${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} idm_admin -o json); then
echo "$recover_out" >&2 echo "$recover_out" >&2
@ -191,13 +204,14 @@
KANIDM_PROVISION_IDM_ADMIN_TOKEN=$KANIDM_IDM_ADMIN_PASSWORD \ KANIDM_PROVISION_IDM_ADMIN_TOKEN=$KANIDM_IDM_ADMIN_PASSWORD \
${getExe pkgs.kanidm-provision} --url "${cfg.provision.instanceUrl}" --state ${provisionStateJson} ${optionalString cfg.provision.acceptInvalidCerts "--accept-invalid-certs"} ${getExe pkgs.kanidm-provision} --url "${cfg.provision.instanceUrl}" --state ${provisionStateJson} ${optionalString cfg.provision.acceptInvalidCerts "--accept-invalid-certs"}
''; '';
in { in
{
options.services.kanidm = { options.services.kanidm = {
enableClient = mkEnableOption (mdDoc "the Kanidm client"); enableClient = mkEnableOption (mdDoc "the Kanidm client");
enableServer = mkEnableOption (mdDoc "the Kanidm server"); enableServer = mkEnableOption (mdDoc "the Kanidm server");
enablePam = mkEnableOption (mdDoc "the Kanidm PAM and NSS integration"); enablePam = mkEnableOption (mdDoc "the Kanidm PAM and NSS integration");
package = mkPackageOption pkgs "kanidm" {}; package = mkPackageOption pkgs "kanidm" { };
serverSettings = mkOption { serverSettings = mkOption {
type = types.submodule { type = types.submodule {
@ -253,12 +267,20 @@ in {
log_level = mkOption { log_level = mkOption {
description = mdDoc "Log level of the server."; description = mdDoc "Log level of the server.";
default = "info"; default = "info";
type = types.enum ["info" "debug" "trace"]; type = types.enum [
"info"
"debug"
"trace"
];
}; };
role = mkOption { role = mkOption {
description = mdDoc "The role of this server. This affects the replication relationship and thereby available features."; description = mdDoc "The role of this server. This affects the replication relationship and thereby available features.";
default = "WriteReplica"; default = "WriteReplica";
type = types.enum ["WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica"]; type = types.enum [
"WriteReplica"
"WriteReplicaNoUI"
"ReadOnlyReplica"
];
}; };
online_backup = { online_backup = {
path = mkOption { path = mkOption {
@ -284,7 +306,7 @@ in {
}; };
}; };
}; };
default = {}; default = { };
description = mdDoc '' description = mdDoc ''
Settings for Kanidm, see Settings for Kanidm, see
[the documentation](https://kanidm.github.io/kanidm/stable/server_configuration.html) [the documentation](https://kanidm.github.io/kanidm/stable/server_configuration.html)
@ -387,8 +409,9 @@ in {
groups = mkOption { groups = mkOption {
description = "Provisioning of kanidm groups"; description = "Provisioning of kanidm groups";
default = {}; default = { };
type = types.attrsOf (types.submodule (groupSubmod: { type = types.attrsOf (
types.submodule (groupSubmod: {
options = { options = {
present = mkPresentOption "group"; present = mkPresentOption "group";
@ -396,20 +419,26 @@ in {
description = "List of kanidm entities (persons, groups, ...) which are part of this group."; description = "List of kanidm entities (persons, groups, ...) which are part of this group.";
type = types.listOf types.str; type = types.listOf types.str;
apply = unique; apply = unique;
default = []; default = [ ];
}; };
}; };
config.members = concatLists (flip mapAttrsToList cfg.provision.persons ( config.members = concatLists (
flip mapAttrsToList cfg.provision.persons (
person: personCfg: person: personCfg:
optional (personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups) person optional (
)); personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups
})); ) person
)
);
})
);
}; };
persons = mkOption { persons = mkOption {
description = "Provisioning of kanidm persons"; description = "Provisioning of kanidm persons";
default = {}; default = { };
type = types.attrsOf (types.submodule { type = types.attrsOf (
types.submodule {
options = { options = {
present = mkPresentOption "person"; present = mkPresentOption "person";
@ -429,24 +458,26 @@ in {
mailAddresses = mkOption { mailAddresses = mkOption {
description = "Mail addresses. First given address is considered the primary address."; description = "Mail addresses. First given address is considered the primary address.";
type = types.listOf types.str; type = types.listOf types.str;
example = ["jane.doe@example.com"]; example = [ "jane.doe@example.com" ];
default = []; default = [ ];
}; };
groups = mkOption { groups = mkOption {
description = "List of groups this person should belong to."; description = "List of groups this person should belong to.";
type = types.listOf types.str; type = types.listOf types.str;
apply = unique; apply = unique;
default = []; default = [ ];
}; };
}; };
}); }
);
}; };
systems.oauth2 = mkOption { systems.oauth2 = mkOption {
description = "Provisioning of oauth2 resource servers"; description = "Provisioning of oauth2 resource servers";
default = {}; default = { };
type = types.attrsOf (types.submodule { type = types.attrsOf (
types.submodule {
options = { options = {
present = mkPresentOption "oauth2 resource server"; present = mkPresentOption "oauth2 resource server";
@ -519,7 +550,7 @@ in {
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information. See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
''; '';
type = types.attrsOf (types.listOf types.str); type = types.attrsOf (types.listOf types.str);
default = {}; default = { };
}; };
supplementaryScopeMaps = mkOption { supplementaryScopeMaps = mkOption {
@ -528,7 +559,7 @@ in {
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information. See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
''; '';
type = types.attrsOf (types.listOf types.str); type = types.attrsOf (types.listOf types.str);
default = {}; default = { };
}; };
removeOrphanedClaimMaps = mkOption { removeOrphanedClaimMaps = mkOption {
@ -542,72 +573,81 @@ in {
Adds additional claims (and values) based on which kanidm groups an authenticating party belongs to. Adds additional claims (and values) based on which kanidm groups an authenticating party belongs to.
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information. See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
''; '';
default = {}; default = { };
type = types.attrsOf (types.submodule { type = types.attrsOf (
types.submodule {
options = { options = {
joinType = mkOption { joinType = mkOption {
description = '' description = ''
Determines how multiple values are joined to create the claim value. Determines how multiple values are joined to create the claim value.
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information. See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
''; '';
type = types.enum ["array" "csv" "ssv"]; type = types.enum [
"array"
"csv"
"ssv"
];
default = "array"; default = "array";
}; };
valuesByGroup = mkOption { valuesByGroup = mkOption {
description = "Maps kanidm groups to values for the claim."; description = "Maps kanidm groups to values for the claim.";
default = {}; default = { };
type = types.attrsOf (types.listOf types.str); type = types.attrsOf (types.listOf types.str);
}; };
}; };
}); }
);
}; };
}; };
}); }
);
}; };
}; };
}; };
config = mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) { config = mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) {
assertions = let assertions =
entityList = type: attrs: flip mapAttrsToList (filterPresent attrs) (name: _: {inherit type name;}); let
entityList =
type: attrs: flip mapAttrsToList (filterPresent attrs) (name: _: { inherit type name; });
entities = entities =
entityList "group" cfg.provision.groups entityList "group" cfg.provision.groups
++ entityList "person" cfg.provision.persons ++ entityList "person" cfg.provision.persons
++ entityList "oauth2" cfg.provision.systems.oauth2; ++ entityList "oauth2" cfg.provision.systems.oauth2;
# Accumulate entities by name. Track corresponding entity types for later duplicate check. # Accumulate entities by name. Track corresponding entity types for later duplicate check.
entitiesByName = entitiesByName = foldl' (
foldl' ( acc: { type, name }: acc // { ${name} = (acc.${name} or [ ]) ++ [ type ]; }
acc: { ) { } entities;
type,
name,
}:
acc
// {
${name} = (acc.${name} or []) ++ [type];
}
) {}
entities;
assertGroupsKnown = opt: groups: let assertGroupsKnown =
opt: groups:
let
knownGroups = attrNames (filterPresent cfg.provision.groups); knownGroups = attrNames (filterPresent cfg.provision.groups);
unknownGroups = subtractLists knownGroups groups; unknownGroups = subtractLists knownGroups groups;
in { in
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownGroups == []; {
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownGroups == [ ];
message = "${opt} refers to unknown groups: ${toString unknownGroups}"; message = "${opt} refers to unknown groups: ${toString unknownGroups}";
}; };
assertEntitiesKnown = opt: entities: let assertEntitiesKnown =
opt: entities:
let
unknownEntities = subtractLists (attrNames entitiesByName) entities; unknownEntities = subtractLists (attrNames entitiesByName) entities;
in { in
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownEntities == []; {
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownEntities == [ ];
message = "${opt} refers to unknown entities: ${toString unknownEntities}"; message = "${opt} refers to unknown entities: ${toString unknownEntities}";
}; };
in in
[ [
{ {
assertion = !cfg.enableServer || ((cfg.serverSettings.tls_chain or null) == null) || (!isStorePath cfg.serverSettings.tls_chain); assertion =
!cfg.enableServer
|| ((cfg.serverSettings.tls_chain or null) == null)
|| (!isStorePath cfg.serverSettings.tls_chain);
message = '' message = ''
<option>services.kanidm.serverSettings.tls_chain</option> points to <option>services.kanidm.serverSettings.tls_chain</option> points to
a file in the Nix store. You should use a quoted absolute path to a file in the Nix store. You should use a quoted absolute path to
@ -615,7 +655,10 @@ in {
''; '';
} }
{ {
assertion = !cfg.enableServer || ((cfg.serverSettings.tls_key or null) == null) || (!isStorePath cfg.serverSettings.tls_key); assertion =
!cfg.enableServer
|| ((cfg.serverSettings.tls_key or null) == null)
|| (!isStorePath cfg.serverSettings.tls_key);
message = '' message = ''
<option>services.kanidm.serverSettings.tls_key</option> points to <option>services.kanidm.serverSettings.tls_key</option> points to
a file in the Nix store. You should use a quoted absolute path to a file in the Nix store. You should use a quoted absolute path to
@ -639,9 +682,10 @@ in {
{ {
assertion = assertion =
!cfg.enableServer !cfg.enableServer
|| (cfg.serverSettings.domain || (
== null cfg.serverSettings.domain == null
-> cfg.serverSettings.role == "WriteReplica" || cfg.serverSettings.role == "WriteReplicaNoUI"); -> cfg.serverSettings.role == "WriteReplica" || cfg.serverSettings.role == "WriteReplicaNoUI"
);
message = '' message = ''
<option>services.kanidm.serverSettings.domain</option> can only be set if this instance <option>services.kanidm.serverSettings.domain</option> can only be set if this instance
is not a ReadOnlyReplica. Otherwise the db would inherit it from is not a ReadOnlyReplica. Otherwise the db would inherit it from
@ -655,13 +699,14 @@ in {
# If any secret is provisioned, the kanidm package must have some required patches applied to it # If any secret is provisioned, the kanidm package must have some required patches applied to it
{ {
assertion = assertion =
(cfg.provision.enable (
cfg.provision.enable
&& ( && (
cfg.provision.adminPasswordFile cfg.provision.adminPasswordFile != null
!= null
|| cfg.provision.idmAdminPasswordFile != null || cfg.provision.idmAdminPasswordFile != null
|| any (x: x.basicSecretFile != null) (attrValues (filterPresent cfg.provision.systems.oauth2)) || any (x: x.basicSecretFile != null) (attrValues (filterPresent cfg.provision.systems.oauth2))
)) )
)
-> cfg.package.enableSecretProvisioning; -> cfg.package.enableSecretProvisioning;
message = '' message = ''
Specifying an admin account password or oauth2 basicSecretFile requires kanidm to be built with the secret provisioning patches. Specifying an admin account password or oauth2 basicSecretFile requires kanidm to be built with the secret provisioning patches.
@ -669,15 +714,20 @@ in {
''; '';
} }
# Entity names must be globally unique: # Entity names must be globally unique:
(let (
let
# Filter all names that occurred in more than one entity type. # Filter all names that occurred in more than one entity type.
duplicateNames = filterAttrs (_: v: builtins.length v > 1) entitiesByName; duplicateNames = filterAttrs (_: v: builtins.length v > 1) entitiesByName;
in { in
assertion = cfg.provision.enable -> duplicateNames == {}; {
assertion = cfg.provision.enable -> duplicateNames == { };
message = '' message = ''
services.kanidm.provision requires all entity names (group, person, oauth2, ...) to be unique! services.kanidm.provision requires all entity names (group, person, oauth2, ...) to be unique!
${concatLines (mapAttrsToList (name: xs: " - '${name}' used as: ${toString xs}") duplicateNames)}''; ${concatLines (
}) mapAttrsToList (name: xs: " - '${name}' used as: ${toString xs}") duplicateNames
)}'';
}
)
] ]
++ flip mapAttrsToList (filterPresent cfg.provision.persons) ( ++ flip mapAttrsToList (filterPresent cfg.provision.persons) (
person: personCfg: person: personCfg:
@ -687,38 +737,57 @@ in {
group: groupCfg: group: groupCfg:
assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members
) )
++ concatLists (flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) ( ++ concatLists (
flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) (
oauth2: oauth2Cfg: oauth2: oauth2Cfg:
[ [
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (attrNames oauth2Cfg.scopeMaps)) (assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (attrNames oauth2Cfg.supplementaryScopeMaps)) attrNames oauth2Cfg.scopeMaps
))
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (
attrNames oauth2Cfg.supplementaryScopeMaps
))
] ]
++ concatLists (flip mapAttrsToList oauth2Cfg.claimMaps (claim: claimCfg: [ ++ concatLists (
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim}.valuesByGroup" (attrNames claimCfg.valuesByGroup)) flip mapAttrsToList oauth2Cfg.claimMaps (
claim: claimCfg: [
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim}.valuesByGroup" (
attrNames claimCfg.valuesByGroup
))
# At least one group must map to a value in each claim map # At least one group must map to a value in each claim map
{ {
assertion = (cfg.provision.enable && cfg.enableServer) -> any (xs: xs != []) (attrValues claimCfg.valuesByGroup); assertion =
(cfg.provision.enable && cfg.enableServer)
-> any (xs: xs != [ ]) (attrValues claimCfg.valuesByGroup);
message = "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim} does not specify any values for any group"; message = "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim} does not specify any values for any group";
} }
# Public clients cannot define a basic secret # Public clients cannot define a basic secret
{ {
assertion = (cfg.provision.enable && cfg.enableServer && oauth2Cfg.public) -> oauth2Cfg.basicSecretFile == null; assertion =
(cfg.provision.enable && cfg.enableServer && oauth2Cfg.public) -> oauth2Cfg.basicSecretFile == null;
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot specify a basic secret"; message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot specify a basic secret";
} }
# Public clients cannot disable PKCE # Public clients cannot disable PKCE
{ {
assertion = (cfg.provision.enable && cfg.enableServer && oauth2Cfg.public) -> !oauth2Cfg.allowInsecureClientDisablePkce; assertion =
(cfg.provision.enable && cfg.enableServer && oauth2Cfg.public)
-> !oauth2Cfg.allowInsecureClientDisablePkce;
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot disable PKCE"; message = "services.kanidm.provision.systems.oauth2.${oauth2} is a public client and thus cannot disable PKCE";
} }
# Non-public clients cannot enable localhost redirects # Non-public clients cannot enable localhost redirects
{ {
assertion = (cfg.provision.enable && cfg.enableServer && !oauth2Cfg.public) -> !oauth2Cfg.enableLocalhostRedirects; assertion =
(cfg.provision.enable && cfg.enableServer && !oauth2Cfg.public)
-> !oauth2Cfg.enableLocalhostRedirects;
message = "services.kanidm.provision.systems.oauth2.${oauth2} is a non-public client and thus cannot enable localhost redirects"; message = "services.kanidm.provision.systems.oauth2.${oauth2} is a non-public client and thus cannot enable localhost redirects";
} }
])) ]
)); )
)
)
);
environment.systemPackages = mkIf cfg.enableClient [cfg.package]; environment.systemPackages = mkIf cfg.enableClient [ cfg.package ];
systemd.tmpfiles.settings."10-kanidm" = { systemd.tmpfiles.settings."10-kanidm" = {
${cfg.serverSettings.online_backup.path}.d = { ${cfg.serverSettings.online_backup.path}.d = {
@ -730,14 +799,16 @@ in {
systemd.services.kanidm = mkIf cfg.enableServer { systemd.services.kanidm = mkIf cfg.enableServer {
description = "kanidm identity management daemon"; description = "kanidm identity management daemon";
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
after = ["network.target"]; after = [ "network.target" ];
serviceConfig = mkMerge [ serviceConfig = mkMerge [
# Merge paths and ignore existing prefixes needs to sidestep mkMerge # Merge paths and ignore existing prefixes needs to sidestep mkMerge
(defaultServiceConfig (
defaultServiceConfig
// { // {
BindReadOnlyPaths = mergePaths (defaultServiceConfig.BindReadOnlyPaths ++ certPaths); BindReadOnlyPaths = mergePaths (defaultServiceConfig.BindReadOnlyPaths ++ certPaths);
}) }
)
{ {
StateDirectory = "kanidm"; StateDirectory = "kanidm";
StateDirectoryMode = "0700"; StateDirectoryMode = "0700";
@ -754,13 +825,17 @@ in {
cfg.serverSettings.online_backup.path cfg.serverSettings.online_backup.path
]; ];
AmbientCapabilities = ["CAP_NET_BIND_SERVICE"]; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"]; CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
# This would otherwise override the CAP_NET_BIND_SERVICE capability. # This would otherwise override the CAP_NET_BIND_SERVICE capability.
PrivateUsers = mkForce false; PrivateUsers = mkForce false;
# Port needs to be exposed to the host network # Port needs to be exposed to the host network
PrivateNetwork = mkForce false; PrivateNetwork = mkForce false;
RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"]; RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
TemporaryFileSystem = "/:ro"; TemporaryFileSystem = "/:ro";
} }
]; ];
@ -769,9 +844,12 @@ in {
systemd.services.kanidm-unixd = mkIf cfg.enablePam { systemd.services.kanidm-unixd = mkIf cfg.enablePam {
description = "Kanidm PAM daemon"; description = "Kanidm PAM daemon";
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
after = ["network.target"]; after = [ "network.target" ];
restartTriggers = [unixConfigFile clientConfigFile]; restartTriggers = [
unixConfigFile
clientConfigFile
];
serviceConfig = mkMerge [ serviceConfig = mkMerge [
defaultServiceConfig defaultServiceConfig
{ {
@ -796,7 +874,11 @@ in {
]; ];
# Needs to connect to kanidmd # Needs to connect to kanidmd
PrivateNetwork = mkForce false; PrivateNetwork = mkForce false;
RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"]; RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
TemporaryFileSystem = "/:ro"; TemporaryFileSystem = "/:ro";
} }
]; ];
@ -805,10 +887,16 @@ in {
systemd.services.kanidm-unixd-tasks = mkIf cfg.enablePam { systemd.services.kanidm-unixd-tasks = mkIf cfg.enablePam {
description = "Kanidm PAM home management daemon"; description = "Kanidm PAM home management daemon";
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
after = ["network.target" "kanidm-unixd.service"]; after = [
partOf = ["kanidm-unixd.service"]; "network.target"
restartTriggers = [unixConfigFile clientConfigFile]; "kanidm-unixd.service"
];
partOf = [ "kanidm-unixd.service" ];
restartTriggers = [
unixConfigFile
clientConfigFile
];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/kanidm_unixd_tasks"; ExecStart = "${cfg.package}/bin/kanidm_unixd_tasks";
@ -828,13 +916,18 @@ in {
"/run/kanidm-unixd:/var/run/kanidm-unixd" "/run/kanidm-unixd:/var/run/kanidm-unixd"
]; ];
# CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket # CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket
CapabilityBoundingSet = ["CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_DAC_READ_SEARCH"]; CapabilityBoundingSet = [
"CAP_CHOWN"
"CAP_FOWNER"
"CAP_DAC_OVERRIDE"
"CAP_DAC_READ_SEARCH"
];
IPAddressDeny = "any"; IPAddressDeny = "any";
# Need access to users # Need access to users
PrivateUsers = false; PrivateUsers = false;
# Need access to home directories # Need access to home directories
ProtectHome = false; ProtectHome = false;
RestrictAddressFamilies = ["AF_UNIX"]; RestrictAddressFamilies = [ "AF_UNIX" ];
TemporaryFileSystem = "/:ro"; TemporaryFileSystem = "/:ro";
Restart = "on-failure"; Restart = "on-failure";
}; };
@ -843,29 +936,21 @@ in {
# These paths are hardcoded # These paths are hardcoded
environment.etc = mkMerge [ environment.etc = mkMerge [
(mkIf cfg.enableServer { (mkIf cfg.enableServer { "kanidm/server.toml".source = serverConfigFile; })
"kanidm/server.toml".source = serverConfigFile;
})
(mkIf options.services.kanidm.clientSettings.isDefined { (mkIf options.services.kanidm.clientSettings.isDefined {
"kanidm/config".source = clientConfigFile; "kanidm/config".source = clientConfigFile;
}) })
(mkIf cfg.enablePam { (mkIf cfg.enablePam { "kanidm/unixd".source = unixConfigFile; })
"kanidm/unixd".source = unixConfigFile;
})
]; ];
system.nssModules = mkIf cfg.enablePam [cfg.package]; system.nssModules = mkIf cfg.enablePam [ cfg.package ];
system.nssDatabases.group = optional cfg.enablePam "kanidm"; system.nssDatabases.group = optional cfg.enablePam "kanidm";
system.nssDatabases.passwd = optional cfg.enablePam "kanidm"; system.nssDatabases.passwd = optional cfg.enablePam "kanidm";
users.groups = mkMerge [ users.groups = mkMerge [
(mkIf cfg.enableServer { (mkIf cfg.enableServer { kanidm = { }; })
kanidm = {}; (mkIf cfg.enablePam { kanidm-unixd = { }; })
})
(mkIf cfg.enablePam {
kanidm-unixd = {};
})
]; ];
users.users = mkMerge [ users.users = mkMerge [
(mkIf cfg.enableServer { (mkIf cfg.enableServer {
@ -873,7 +958,7 @@ in {
description = "Kanidm server"; description = "Kanidm server";
isSystemUser = true; isSystemUser = true;
group = "kanidm"; group = "kanidm";
packages = [cfg.package]; packages = [ cfg.package ];
}; };
}) })
(mkIf cfg.enablePam { (mkIf cfg.enablePam {
@ -886,6 +971,10 @@ in {
]; ];
}; };
meta.maintainers = with lib.maintainers; [erictapen Flakebi oddlama]; meta.maintainers = with lib.maintainers; [
erictapen
Flakebi
oddlama
];
meta.buildDocsInSandbox = false; meta.buildDocsInSandbox = false;
} }

View file

@ -1,10 +1,8 @@
{lib, ...}: let { lib, ... }:
inherit let
(lib) inherit (lib) mkOption types;
mkOption in
types {
;
in {
options.node = { options.node = {
secretsDir = mkOption { secretsDir = mkOption {
description = "Path to the secrets directory for this node."; description = "Path to the secrets directory for this node.";

View file

@ -3,9 +3,9 @@
lib, lib,
pkgs, pkgs,
... ...
}: let }:
inherit let
(lib) inherit (lib)
attrNames attrNames
getExe getExe
literalExpression literalExpression
@ -22,8 +22,7 @@
versionOlder versionOlder
; ;
inherit inherit (lib.types)
(lib.types)
attrsOf attrsOf
port port
str str
@ -36,7 +35,8 @@
kernel = config.boot.kernelPackages; kernel = config.boot.kernelPackages;
cfg = config.services.netbird; cfg = config.services.netbird;
in { in
{
meta.maintainers = with maintainers; [ meta.maintainers = with maintainers; [
misuzu misuzu
thubrecht thubrecht
@ -46,16 +46,13 @@ in {
options.services.netbird = { options.services.netbird = {
enable = mkEnableOption (lib.mdDoc "Netbird daemon"); enable = mkEnableOption (lib.mdDoc "Netbird daemon");
package = mkPackageOption pkgs "netbird" {}; package = mkPackageOption pkgs "netbird" { };
tunnels = mkOption { tunnels = mkOption {
type = attrsOf ( type = attrsOf (
submodule ( submodule (
{ name, config, ... }:
{ {
name,
config,
...
}: {
options = { options = {
port = mkOption { port = mkOption {
type = port; type = port;
@ -111,7 +108,7 @@ in {
} }
) )
); );
default = {}; default = { };
description = '' description = ''
Attribute set of Netbird tunnels, each one will spawn a daemon listening on ... Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
''; '';
@ -124,16 +121,15 @@ in {
services.netbird.tunnels.wt0.stateDir = "netbird"; services.netbird.tunnels.wt0.stateDir = "netbird";
}) })
(mkIf (cfg.tunnels != {}) { (mkIf (cfg.tunnels != { }) {
boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
environment.systemPackages = [cfg.package]; environment.systemPackages = [ cfg.package ];
networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels; networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
systemd.network.networks = mkIf config.networking.useNetworkd ( systemd.network.networks = mkIf config.networking.useNetworkd (
mapAttrs' mapAttrs' (
(
name: _: name: _:
nameValuePair "50-netbird-${name}" { nameValuePair "50-netbird-${name}" {
matchConfig = { matchConfig = {
@ -144,14 +140,12 @@ in {
ActivationPolicy = "manual"; ActivationPolicy = "manual";
}; };
} }
) ) cfg.tunnels
cfg.tunnels
); );
systemd.services = systemd.services = mapAttrs' (
mapAttrs' name:
( {
name: {
environment, environment,
stateDir, stateDir,
environmentFile, environmentFile,
@ -161,12 +155,12 @@ in {
nameValuePair "netbird-${name}" { nameValuePair "netbird-${name}" {
description = "A WireGuard-based mesh network that connects your devices into a single private network"; description = "A WireGuard-based mesh network that connects your devices into a single private network";
documentation = ["https://netbird.io/docs/"]; documentation = [ "https://netbird.io/docs/" ];
after = ["network.target"]; after = [ "network.target" ];
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
path = with pkgs; [openresolv]; path = with pkgs; [ openresolv ];
inherit environment; inherit environment;
@ -178,10 +172,7 @@ in {
StateDirectory = stateDir; StateDirectory = stateDir;
StateDirectoryMode = "0700"; StateDirectoryMode = "0700";
WorkingDirectory = "/var/lib/${stateDir}"; WorkingDirectory = "/var/lib/${stateDir}";
RuntimeDirectoryMode = RuntimeDirectoryMode = if userAccess then "0755" else "0750";
if userAccess
then "0755"
else "0750";
# hardening # hardening
LockPersonality = true; LockPersonality = true;
@ -222,8 +213,7 @@ in {
stopIfChanged = false; stopIfChanged = false;
} }
) ) cfg.tunnels;
cfg.tunnels;
}) })
]; ];
} }

View file

@ -3,9 +3,9 @@
inputs, inputs,
config, config,
... ...
}: let }:
inherit let
(lib) inherit (lib)
mapAttrs mapAttrs
assertMsg assertMsg
types types
@ -16,22 +16,25 @@
# If the given expression is a bare set, it will be wrapped in a function, # If the given expression is a bare set, it will be wrapped in a function,
# so that the imported file can always be applied to the inputs, similar to # so that the imported file can always be applied to the inputs, similar to
# how modules can be functions or sets. # how modules can be functions or sets.
constSet = x: constSet = x: if builtins.isAttrs x then (_: x) else x;
if builtins.isAttrs x
then (_: x)
else x;
rageImportEncrypted = assert assertMsg (builtins ? extraBuiltins.rageImportEncrypted) "The rageImportEncrypted extra plugin is not loaded"; rageImportEncrypted =
assert assertMsg (
builtins ? extraBuiltins.rageImportEncrypted
) "The rageImportEncrypted extra plugin is not loaded";
builtins.extraBuiltins.rageImportEncrypted; builtins.extraBuiltins.rageImportEncrypted;
# This "imports" an encrypted .nix.age file # This "imports" an encrypted .nix.age file
importEncrypted = path: importEncrypted =
path:
constSet ( constSet (
if builtins.pathExists path if builtins.pathExists path then
then rageImportEncrypted inputs.self.secretsConfig.masterIdentities path rageImportEncrypted inputs.self.secretsConfig.masterIdentities path
else {} else
{ }
); );
cfg = config.secrets; cfg = config.secrets;
in { in
{
options.secrets = { options.secrets = {
defineRageBuiltins = mkOption { defineRageBuiltins = mkOption {
default = true; default = true;
@ -43,7 +46,7 @@ in {
}; };
secretFiles = mkOption { secretFiles = mkOption {
default = {}; default = { };
type = types.attrsOf types.path; type = types.attrsOf types.path;
example = literalExpression "{ local = ./secrets.nix.age; }"; example = literalExpression "{ local = ./secrets.nix.age; }";
description = mdDoc '' description = mdDoc ''
@ -56,15 +59,16 @@ in {
secrets = mkOption { secrets = mkOption {
readOnly = true; readOnly = true;
default = default = mapAttrs (_: x: importEncrypted x inputs) cfg.secretFiles;
mapAttrs (_: x: importEncrypted x inputs) cfg.secretFiles;
description = mdDoc '' description = mdDoc ''
the secrets decrypted from the secretFiles the secrets decrypted from the secretFiles
''; '';
}; };
}; };
config.home-manager.sharedModules = [ config.home-manager.sharedModules = [
({config, ...}: { (
{ config, ... }:
{
options = { options = {
userSecretsFile = mkOption { userSecretsFile = mkOption {
default = ../users/${config._module.args.name}/secrets.nix.age; default = ../users/${config._module.args.name}/secrets.nix.age;
@ -78,6 +82,7 @@ in {
description = "User secrets"; description = "User secrets";
}; };
}; };
}) }
)
]; ];
} }

View file

@ -3,9 +3,9 @@
config, config,
lib, lib,
... ...
}: let }:
inherit let
(lib) inherit (lib)
mkOption mkOption
types types
flip flip
@ -19,14 +19,18 @@
"x-systemd.device-timeout=5s" "x-systemd.device-timeout=5s"
"x-systemd.mount-timeout=5s" "x-systemd.mount-timeout=5s"
]; ];
in { in
{
# Give users the ability to add their own smb shares # Give users the ability to add their own smb shares
home-manager.sharedModules = [ home-manager.sharedModules = [
{ {
options.home.smb = mkOption { options.home.smb = mkOption {
description = "Samba shares to be mountable under $HOME/smb"; description = "Samba shares to be mountable under $HOME/smb";
default = []; default = [ ];
type = types.listOf (types.submodule ({config, ...}: { type = types.listOf (
types.submodule (
{ config, ... }:
{
options = { options = {
localPath = mkOption { localPath = mkOption {
description = "The path under which the share will be mounted. Defaults to the remotePath"; description = "The path under which the share will be mounted. Defaults to the remotePath";
@ -53,30 +57,28 @@ in {
type = types.bool; type = types.bool;
}; };
}; };
})); }
)
);
}; };
} }
]; ];
imports = [ imports = [
{ {
environment.systemPackages = [pkgs.cifs-utils]; environment.systemPackages = [ pkgs.cifs-utils ];
fileSystems = fileSystems = mkMerge (
mkMerge flip concatMap (attrNames config.home-manager.users) (
( user:
flip let
concatMap
(attrNames config.home-manager.users)
(
user: let
parentPath = "/home/${user}/smb"; parentPath = "/home/${user}/smb";
cfg = config.home-manager.users.${user}.home.smb; cfg = config.home-manager.users.${user}.home.smb;
inherit (config.users.users.${user}) uid; inherit (config.users.users.${user}) uid;
inherit (config.users.groups.${user}) gid; inherit (config.users.groups.${user}) gid;
in in
flip map cfg ( flip map cfg (cfg: {
cfg: { "${parentPath}/${cfg.localPath}" =
"${parentPath}/${cfg.localPath}" = let let
options = options =
baseOptions baseOptions
++ [ ++ [
@ -87,13 +89,13 @@ in {
"credentials=${cfg.credentials}" "credentials=${cfg.credentials}"
] ]
++ (optional (!cfg.automatic) "noauto"); ++ (optional (!cfg.automatic) "noauto");
in { in
{
inherit options; inherit options;
device = "//${cfg.address}/${cfg.remotePath}"; device = "//${cfg.address}/${cfg.remotePath}";
fsType = "cifs"; fsType = "cifs";
}; };
} })
)
) )
); );
} }

View file

@ -1,10 +1,9 @@
{ { self, ... }:
self, system:
... let
}: system: let
pkgs = self.pkgs.${system}; pkgs = self.pkgs.${system};
in in
pkgs.devshell.mkShell { pkgs.devshell.mkShell {
name = "nix-config"; name = "nix-config";
packages = with pkgs; [ packages = with pkgs; [
# Nix # Nix
@ -67,4 +66,4 @@ in
]; ];
devshell.startup.pre-commit.text = self.checks.${system}.pre-commit-check.shellHook; devshell.startup.pre-commit.text = self.checks.${system}.pre-commit-check.shellHook;
} }

View file

@ -14,21 +14,40 @@
# ''; # '';
# } # }
# ``` # ```
{exec, ...}: let { exec, ... }:
let
assertMsg = pred: msg: pred || builtins.throw msg; assertMsg = pred: msg: pred || builtins.throw msg;
hasSuffix = suffix: content: let hasSuffix =
suffix: content:
let
lenContent = builtins.stringLength content; lenContent = builtins.stringLength content;
lenSuffix = builtins.stringLength suffix; lenSuffix = builtins.stringLength suffix;
in in
lenContent >= lenSuffix && builtins.substring (lenContent - lenSuffix) lenContent content == suffix; lenContent >= lenSuffix && builtins.substring (lenContent - lenSuffix) lenContent content == suffix;
in { in
{
# Instead of calling rage directly here, we call a wrapper script that will cache the output # Instead of calling rage directly here, we call a wrapper script that will cache the output
# in a predictable path in /tmp, which allows us to only require the password for each encrypted # in a predictable path in /tmp, which allows us to only require the password for each encrypted
# file once. # file once.
rageImportEncrypted = identities: nixFile: rageImportEncrypted =
assert assertMsg (builtins.isPath nixFile) "The file to decrypt must be given as a path to prevent impurity."; identities: nixFile:
assert assertMsg (hasSuffix ".nix.age" nixFile) "The content of the decrypted file must be a nix expression and should therefore end in .nix.age"; assert assertMsg (builtins.isPath nixFile)
exec ([./rage-decrypt-and-cache.sh nixFile] ++ identities); "The file to decrypt must be given as a path to prevent impurity.";
assert assertMsg (hasSuffix ".nix.age" nixFile)
"The content of the decrypted file must be a nix expression and should therefore end in .nix.age";
exec (
[
./rage-decrypt-and-cache.sh
nixFile
]
++ identities
);
# currentSystem # currentSystem
unsafeCurrentSystem = exec ["nix" "eval" "--impure" "--expr" "builtins.currentSystem"]; unsafeCurrentSystem = exec [
"nix"
"eval"
"--impure"
"--expr"
"builtins.currentSystem"
];
} }

View file

@ -1,11 +1,15 @@
{self, ...}: nodeName: nodeAttrs: let { self, ... }:
nodeName: nodeAttrs:
let
#FIXME inherit nodeAttrs. system; #FIXME inherit nodeAttrs. system;
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = self.pkgs.${system}; pkgs = self.pkgs.${system};
disko-script = pkgs.writeShellScriptBin "disko-script" "${nodeAttrs.config.system.build.diskoScript}"; disko-script = pkgs.writeShellScriptBin "disko-script" "${nodeAttrs.config.system.build.diskoScript
}";
disko-mount = pkgs.writeShellScriptBin "disko-mount" "${nodeAttrs.config.system.build.mountScript}"; disko-mount = pkgs.writeShellScriptBin "disko-mount" "${nodeAttrs.config.system.build.mountScript}";
disko-format = pkgs.writeShellScriptBin "disko-format" "${nodeAttrs.config.system.build.formatScript}"; disko-format = pkgs.writeShellScriptBin "disko-format" "${nodeAttrs.config.system.build.formatScript
}";
install-system = pkgs.writeShellScriptBin "install-system" '' install-system = pkgs.writeShellScriptBin "install-system" ''
set -euo pipefail set -euo pipefail
@ -28,7 +32,8 @@
install-system install-system
]; ];
}; };
in { in
{
# Everything required for the installer as a single package, # Everything required for the installer as a single package,
# so it can be used from an existing live system by copying the derivation. # so it can be used from an existing live system by copying the derivation.
packages.${system}.installer-package.${nodeName} = installer-package; packages.${system}.installer-package.${nodeName} = installer-package;

View file

@ -1,7 +1,7 @@
inputs: let inputs:
let
inherit (inputs) self; inherit (inputs) self;
inherit inherit (inputs.nixpkgs.lib)
(inputs.nixpkgs.lib)
concatMapAttrs concatMapAttrs
filterAttrs filterAttrs
flip flip
@ -12,7 +12,10 @@ inputs: let
; ;
# Creates a new nixosSystem with the correct specialArgs, pkgs and name definition # Creates a new nixosSystem with the correct specialArgs, pkgs and name definition
mkHost = {minimal}: name: let mkHost =
{ minimal }:
name:
let
pkgs = self.pkgs.x86_64-linux; pkgs = self.pkgs.x86_64-linux;
in in
nixosSystem { nixosSystem {
@ -42,22 +45,30 @@ inputs: let
# to instanciate hosts correctly. # to instanciate hosts correctly.
hosts = builtins.attrNames (filterAttrs (_: type: type == "directory") (builtins.readDir ../hosts)); hosts = builtins.attrNames (filterAttrs (_: type: type == "directory") (builtins.readDir ../hosts));
# Process each nixosHosts declaration and generatea nixosSystem definitions # Process each nixosHosts declaration and generatea nixosSystem definitions
nixosConfigurations = genAttrs hosts (mkHost {minimal = false;}); nixosConfigurations = genAttrs hosts (mkHost {
minimalConfigurations = genAttrs hosts (mkHost {minimal = true;}); minimal = false;
});
minimalConfigurations = genAttrs hosts (mkHost {
minimal = true;
});
# True NixOS nodes can define additional guest nodes that are built # True NixOS nodes can define additional guest nodes that are built
# together with it. We collect all defined guests from each node here # together with it. We collect all defined guests from each node here
# to allow accessing any node via the unified attribute `nodes`. # to allow accessing any node via the unified attribute `nodes`.
guestConfigurations = flip concatMapAttrs self.nixosConfigurations (_: node: guestConfigurations = flip concatMapAttrs self.nixosConfigurations (
flip mapAttrs' (node.config.guests or {}) ( _: node:
flip mapAttrs' (node.config.guests or { }) (
guestName: guestDef: guestName: guestDef:
nameValuePair guestDef.nodeName ( nameValuePair guestDef.nodeName (
if guestDef.backend == "microvm" if guestDef.backend == "microvm" then
then node.config.microvm.vms.${guestName}.config node.config.microvm.vms.${guestName}.config
else node.config.containers.${guestName}.nixosConfiguration else
node.config.containers.${guestName}.nixosConfiguration
) )
)); )
in { );
in
{
inherit inherit
hosts hosts
nixosConfigurations nixosConfigurations

View file

@ -1,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
nix.extraOptions = '' nix.extraOptions = ''
experimental-features = nix-command flakes recursive-nix experimental-features = nix-command flakes recursive-nix
''; '';

View file

@ -1 +1 @@
{} { }

View file

@ -71,7 +71,7 @@ stdenv.mkDerivation rec {
homepage = "https://actualbudget.com/"; homepage = "https://actualbudget.com/";
license = licenses.mit; license = licenses.mit;
mainProgram = "actual-server"; mainProgram = "actual-server";
maintainers = with maintainers; [patrickdag]; maintainers = with maintainers; [ patrickdag ];
platforms = ["x86_64-linux"]; platforms = [ "x86_64-linux" ];
}; };
} }

View file

@ -1,7 +1,5 @@
{ { pkgs, fetchurl }:
pkgs, let
fetchurl,
}: let
name = "awakened-poe-trade"; name = "awakened-poe-trade";
version = "3.22.10003"; version = "3.22.10003";
description = "Path of Exile trading app for price checking"; description = "Path of Exile trading app for price checking";
@ -23,7 +21,7 @@
sha256 = "sha256-fZ3PU+yE1n/RytkPFAXQhU85KNQStYcSrdgw+OYfJRg="; sha256 = "sha256-fZ3PU+yE1n/RytkPFAXQhU85KNQStYcSrdgw+OYfJRg=";
}; };
in in
pkgs.appimageTools.wrapType2 { pkgs.appimageTools.wrapType2 {
name = "awakened-poe-trade"; name = "awakened-poe-trade";
src = fetchurl { src = fetchurl {
url = "https://github.com/SnosMe/awakened-poe-trade/releases/download/v${version}/${file}"; url = "https://github.com/SnosMe/awakened-poe-trade/releases/download/v${version}/${file}";
@ -36,4 +34,4 @@ in
cp ${desktopEntry} $out/share/applications/${name}.desktop cp ${desktopEntry} $out/share/applications/${name}.desktop
substituteInPlace $out/share/applications/awakened-poe-trade.desktop --replace /share/ $out/share/ substituteInPlace $out/share/applications/awakened-poe-trade.desktop --replace /share/ $out/share/
''; '';
} }

View file

@ -1,39 +1,43 @@
[ [
(import ./scripts) (import ./scripts)
(_self: super: { (_self: super: {
zsh-histdb-skim = super.callPackage ./zsh-histdb-skim.nix {}; zsh-histdb-skim = super.callPackage ./zsh-histdb-skim.nix { };
zsh-histdb = super.callPackage ./zsh-histdb.nix {}; zsh-histdb = super.callPackage ./zsh-histdb.nix { };
actual = super.callPackage ./actual.nix {}; actual = super.callPackage ./actual.nix { };
pr-tracker = super.callPackage ./pr-tracker.nix {}; pr-tracker = super.callPackage ./pr-tracker.nix { };
homebox = super.callPackage ./homebox.nix {}; homebox = super.callPackage ./homebox.nix { };
deploy = super.callPackage ./deploy.nix {}; deploy = super.callPackage ./deploy.nix { };
mongodb-bin = super.callPackage ./mongodb-bin.nix {}; mongodb-bin = super.callPackage ./mongodb-bin.nix { };
awakened-poe-trade = super.callPackage ./awakened-poe-trade.nix {}; awakened-poe-trade = super.callPackage ./awakened-poe-trade.nix { };
neovim-clean = super.neovim-unwrapped.overrideAttrs (_neovimFinal: neovimPrev: { neovim-clean = super.neovim-unwrapped.overrideAttrs (
nativeBuildInputs = (neovimPrev.nativeBuildInputs or []) ++ [super.makeWrapper]; _neovimFinal: neovimPrev: {
nativeBuildInputs = (neovimPrev.nativeBuildInputs or [ ]) ++ [ super.makeWrapper ];
postInstall = postInstall =
(neovimPrev.postInstall or "") (neovimPrev.postInstall or "")
+ '' + ''
wrapProgram $out/bin/nvim --add-flags "--clean" wrapProgram $out/bin/nvim --add-flags "--clean"
''; '';
}); }
kanidm = super.kanidm.overrideAttrs (old: let );
kanidm = super.kanidm.overrideAttrs (
old:
let
provisionSrc = super.fetchFromGitHub { provisionSrc = super.fetchFromGitHub {
owner = "oddlama"; owner = "oddlama";
repo = "kanidm-provision"; repo = "kanidm-provision";
rev = "v1.1.0"; rev = "v1.1.0";
hash = "sha256-pFOFFKh3la/sZGXj+pAM8x4SMeffvvbOvTjPeHS1XPU="; hash = "sha256-pFOFFKh3la/sZGXj+pAM8x4SMeffvvbOvTjPeHS1XPU=";
}; };
in { in
patches = {
old.patches patches = old.patches ++ [
++ [
"${provisionSrc}/patches/1.2.0-oauth2-basic-secret-modify.patch" "${provisionSrc}/patches/1.2.0-oauth2-basic-secret-modify.patch"
"${provisionSrc}/patches/1.2.0-recover-account.patch" "${provisionSrc}/patches/1.2.0-recover-account.patch"
]; ];
passthru.enableSecretProvisioning = true; passthru.enableSecretProvisioning = true;
doCheck = false; doCheck = false;
}); }
kanidm-provision = super.callPackage ./kanidm-provision.nix {}; );
kanidm-provision = super.callPackage ./kanidm-provision.nix { };
}) })
] ]

View file

@ -3,7 +3,8 @@
writeShellApplication, writeShellApplication,
nvd, nvd,
nix-output-monitor, nix-output-monitor,
}: let }:
let
deploy = writeShellApplication { deploy = writeShellApplication {
name = "deploy"; name = "deploy";
text = '' text = ''
@ -166,7 +167,10 @@
''; '';
}; };
in in
symlinkJoin { symlinkJoin {
name = "deploy and build"; name = "deploy and build";
paths = [deploy build]; paths = [
} deploy
build
];
}

View file

@ -7,7 +7,8 @@
lib, lib,
buildGoModule, buildGoModule,
fetchFromGitHub, fetchFromGitHub,
}: let }:
let
pname = "homebox"; pname = "homebox";
version = "0.10.3"; version = "0.10.3";
src = "${fetchFromGitHub { src = "${fetchFromGitHub {
@ -101,7 +102,7 @@
outputHash = "sha256-BVZSdc8e6v+paMzMYazEdnKSNw+OnCpjSzGSEKxVl24="; outputHash = "sha256-BVZSdc8e6v+paMzMYazEdnKSNw+OnCpjSzGSEKxVl24=";
}; };
in in
buildGoModule { buildGoModule {
inherit pname version; inherit pname version;
src = "${src}/backend"; src = "${src}/backend";
@ -130,9 +131,9 @@ in
meta = with lib; { meta = with lib; {
mainProgram = "api"; mainProgram = "api";
homepage = "https://hay-kot.github.io/homebox/"; homepage = "https://hay-kot.github.io/homebox/";
maintainers = with maintainers; [patrickdag]; maintainers = with maintainers; [ patrickdag ];
license = licenses.agpl3Only; license = licenses.agpl3Only;
description = "A inventory and organization system built for the Home User"; description = "A inventory and organization system built for the Home User";
platforms = platforms.all; platforms = platforms.all;
}; };
} }

View file

@ -19,8 +19,11 @@ rustPlatform.buildRustPackage rec {
meta = with lib; { meta = with lib; {
description = "A small utility to help with kanidm provisioning"; description = "A small utility to help with kanidm provisioning";
homepage = "https://github.com/oddlama/kanidm-provision"; homepage = "https://github.com/oddlama/kanidm-provision";
license = with licenses; [asl20 mit]; license = with licenses; [
maintainers = with maintainers; [oddlama]; asl20
mit
];
maintainers = with maintainers; [ oddlama ];
mainProgram = "kanidm-provision"; mainProgram = "kanidm-provision";
}; };
} }

View file

@ -11,27 +11,26 @@ stdenv.mkDerivation {
pname = "mongodb-bin"; pname = "mongodb-bin";
version = "1.0.0"; version = "1.0.0";
srcs = [ srcs = [
( (fetchurl {
fetchurl {
url = "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-6.0.14.tgz"; url = "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-6.0.14.tgz";
hash = "sha256-1MW3pVIffdxq63gY64ozM1erWM2ou2L8T+MTfG+ZPLg="; hash = "sha256-1MW3pVIffdxq63gY64ozM1erWM2ou2L8T+MTfG+ZPLg=";
} })
) (fetchurl {
(
fetchurl {
url = "https://downloads.mongodb.com/compass/mongosh-2.1.5-linux-x64.tgz"; url = "https://downloads.mongodb.com/compass/mongosh-2.1.5-linux-x64.tgz";
hash = "sha256-R1GGB0ZGqmpJtMUNF2+EJK6iNiChHuoHyOf2vKDcOKA="; hash = "sha256-R1GGB0ZGqmpJtMUNF2+EJK6iNiChHuoHyOf2vKDcOKA=";
} })
)
]; ];
sourceRoot = "."; sourceRoot = ".";
nativeBuildInputs = [ nativeBuildInputs = [ autoPatchelfHook ];
autoPatchelfHook
];
buildPhase = '' buildPhase = ''
mkdir -p $out/bin mkdir -p $out/bin
cp mongosh-2.1.5-linux-x64/bin/mongosh $out/bin/mongo cp mongosh-2.1.5-linux-x64/bin/mongosh $out/bin/mongo
cp mongodb-linux-x86_64-ubuntu2204-6.0.14/bin/mongod $out/bin/mongod cp mongodb-linux-x86_64-ubuntu2204-6.0.14/bin/mongod $out/bin/mongod
''; '';
buildInputs = [openssl curl xz libgcc]; buildInputs = [
openssl
curl
xz
libgcc
];
} }

View file

@ -56,7 +56,7 @@ buildNpmPackage rec {
homepage = "https://github.com/ollama-webui/ollama-webui"; homepage = "https://github.com/ollama-webui/ollama-webui";
license = licenses.mit; license = licenses.mit;
mainProgram = pname; mainProgram = pname;
maintainers = with maintainers; [malteneuss]; maintainers = with maintainers; [ malteneuss ];
platforms = platforms.all; platforms = platforms.all;
}; };
} }

View file

@ -18,8 +18,11 @@ rustPlatform.buildRustPackage {
cargoHash = "sha256-9bhKtg2g5H4zGn7yVCjTazeXfeoKjtAKAlzkLkCraiw="; cargoHash = "sha256-9bhKtg2g5H4zGn7yVCjTazeXfeoKjtAKAlzkLkCraiw=";
nativeBuildInputs = [pkg-config]; nativeBuildInputs = [ pkg-config ];
buildInputs = [openssl systemd]; buildInputs = [
openssl
systemd
];
meta = with lib; { meta = with lib; {
description = "Nixpkgs pull request channel tracker"; description = "Nixpkgs pull request channel tracker";
@ -29,7 +32,7 @@ rustPlatform.buildRustPackage {
''; '';
platforms = platforms.linux; platforms = platforms.linux;
license = licenses.agpl3Plus; license = licenses.agpl3Plus;
maintainers = with maintainers; [patrickdag]; maintainers = with maintainers; [ patrickdag ];
mainProgram = "pr-tracker"; mainProgram = "pr-tracker";
}; };
} }

View file

@ -7,7 +7,12 @@
}: }:
writeShellApplication { writeShellApplication {
name = "clone-term"; name = "clone-term";
runtimeInputs = [ps procps xdotool jq]; runtimeInputs = [
ps
procps
xdotool
jq
];
text = '' text = ''
if [[ ''${XDG_CURRENT_DESKTOP-} == sway ]]; then if [[ ''${XDG_CURRENT_DESKTOP-} == sway ]]; then

View file

@ -1,7 +1,7 @@
_final: prev: { _final: prev: {
scripts = { scripts = {
usbguardw = prev.callPackage ./usbguardw.nix {}; usbguardw = prev.callPackage ./usbguardw.nix { };
clone-term = prev.callPackage ./clone-term.nix {}; clone-term = prev.callPackage ./clone-term.nix { };
impermanence-o = prev.callPackage ./impermanence-orphan.nix {}; impermanence-o = prev.callPackage ./impermanence-orphan.nix { };
}; };
} }

View file

@ -1,5 +1,5 @@
{writers}: { writers }:
writers.writePython3Bin "find-orphaned" {} '' writers.writePython3Bin "find-orphaned" { } ''
import sys import sys
import os import os
if len(sys.argv) != 2: if len(sys.argv) != 2:

View file

@ -1,4 +1,4 @@
{writeShellApplication}: { writeShellApplication }:
writeShellApplication { writeShellApplication {
name = "usguardw"; name = "usguardw";
text = '' text = ''

View file

@ -6,7 +6,7 @@
rustPlatform.buildRustPackage rec { rustPlatform.buildRustPackage rec {
pname = "zsh-histd-skim"; pname = "zsh-histd-skim";
version = "0.8.6"; version = "0.8.6";
buildInputs = [sqlite]; buildInputs = [ sqlite ];
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "m42e"; owner = "m42e";
repo = "zsh-histdb-skim"; repo = "zsh-histdb-skim";

View file

@ -1,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
home.packages = [ home.packages = [
pkgs.xclip pkgs.xclip
pkgs.xdragon pkgs.xdragon

View file

@ -79,13 +79,15 @@ MOD: TAGS: pkgs:
"${MOD}-m " = "spawn ${pkgs.thunderbird}/bin/thunderbird"; "${MOD}-m " = "spawn ${pkgs.thunderbird}/bin/thunderbird";
"Menu" = "spawn rofi -show drun"; "Menu" = "spawn rofi -show drun";
} }
// builtins.listToAttrs (map (x: { // builtins.listToAttrs (
map (x: {
name = "${MOD}-${x}"; name = "${MOD}-${x}";
value = "use_index ${x}"; value = "use_index ${x}";
}) }) TAGS
TAGS) )
// builtins.listToAttrs (map (x: { // builtins.listToAttrs (
map (x: {
name = "${MOD}-Shift-${x}"; name = "${MOD}-Shift-${x}";
value = "move_index ${x}"; value = "move_index ${x}";
}) }) TAGS
TAGS) )

Some files were not shown because too many files have changed in this diff Show more