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;
@ -14,7 +15,18 @@
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 = [
"xhci_pci"
"nvme"
"r8169"
"usb_storage"
"usbhid"
"sd_mod"
"rtsx_pci_sdmmc"
"ahci"
"uas"
"tpm_crb"
];
supportedFilesystems = [ "ntfs" ]; supportedFilesystems = [ "ntfs" ];
kernelModules = [ "kvm-intel" ]; kernelModules = [ "kvm-intel" ];
kernelParams = [ kernelParams = [

View file

@ -1,8 +1,5 @@
{ inputs, lib, ... }:
{ {
inputs,
lib,
...
}: {
imports = [ imports = [
./boot.nix ./boot.nix
./home-manager.nix ./home-manager.nix

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

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 =
folder:
pkgs.writers.writePython3Bin "impermanence-prune" { } '' 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"
@ -99,9 +81,7 @@ in {
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" ]; wantedBy = [ "initrd.target" ];
after = [ "zfs-import-rpool.service" ]; after = [ "zfs-import-rpool.service" ];
before = [ "sysroot.mount" ]; before = [ "sysroot.mount" ];

View file

@ -1,8 +1,5 @@
{ lib, config, ... }:
{ {
lib,
config,
...
}: {
networking = { networking = {
useNetworkd = true; useNetworkd = true;
dhcpcd.enable = false; dhcpcd.enable = false;
@ -10,8 +7,7 @@
# 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 = {

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,8 +33,23 @@
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"
];
}; };
}; };
@ -45,11 +57,7 @@
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"

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\"";
}; };
@ -71,7 +71,8 @@
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
{ {

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,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
environment.systemPackages = with pkgs; [ bluetuith ]; environment.systemPackages = with pkgs; [ bluetuith ];
hardware.bluetooth = { hardware.bluetooth = {
@ -24,7 +25,5 @@
''; '';
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

@ -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;

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,9 +32,7 @@ in {
"gtk" "gtk"
"hyprland" "hyprland"
]; ];
sway.default = [ sway.default = [ "wlr" ];
"wlr"
];
}; };
}; };
# needed for gnome pinentry # needed for gnome pinentry
@ -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;
@ -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 = {

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

@ -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,7 +3,8 @@
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;

View file

@ -8,9 +8,5 @@
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,8 +1,5 @@
{ 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 ];
@ -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 = [

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";

View file

@ -1,8 +1,5 @@
{ config, nodes, ... }:
{ {
config,
nodes,
...
}: {
i18n.supportedLocales = [ "all" ]; i18n.supportedLocales = [ "all" ];
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";

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";
}; };
@ -52,7 +54,9 @@ 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 ];
@ -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

@ -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";
}; };
@ -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"

View file

@ -1,6 +1,8 @@
{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";
@ -84,7 +86,11 @@ in {
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;
}; };
@ -100,7 +106,11 @@ in {
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;
}; };
@ -117,7 +127,11 @@ 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;
}; };
@ -131,11 +145,31 @@ in {
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";
@ -157,7 +191,11 @@ in {
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 = {
@ -166,8 +204,7 @@ in {
}; };
}; };
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,14 +57,13 @@ 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";
@ -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,4 +1,5 @@
{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,13 +20,19 @@
}; };
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 = [
80
3000
3001
];
networking.firewall.allowedUDPPorts = [ 3478 ]; networking.firewall.allowedUDPPorts = [ 3478 ];
services.netbird = { services.netbird = {
server = { server = {

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;
@ -93,14 +104,16 @@ 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}"
]; ];

View file

@ -1,8 +1,5 @@
{ config, nodes, ... }:
{ {
config,
nodes,
...
}: {
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
@ -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,4 +1,5 @@
{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 ];

View file

@ -1,4 +1,5 @@
{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 ];

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;
@ -45,14 +47,13 @@ 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";

View file

@ -4,13 +4,15 @@
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 ];
@ -43,7 +45,8 @@ 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 = {

View file

@ -4,10 +4,16 @@
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 = {
@ -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";
@ -106,4 +115,3 @@ in {
#kanidm #kanidm
#remote backups #remote backups
#immich #immich

View file

@ -1,8 +1,5 @@
{ 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;
@ -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";
@ -199,7 +198,8 @@
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,22 +236,34 @@
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";
@ -254,17 +272,27 @@
}; };
"${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,4 +1,5 @@
{config, ...}: { { config, ... }:
{
wireguard.elisabeth = { wireguard.elisabeth = {
client.via = "elisabeth"; client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ]; firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ];

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";
@ -64,7 +66,8 @@ 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 = {

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,7 +114,8 @@
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
@ -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;
@ -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,7 +31,9 @@
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";
};
}; };
}; };
}; };

View file

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

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";
};
}; };
}; };
}; };
@ -128,7 +129,10 @@
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" ];

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,21 +31,29 @@
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 = ''
@ -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;
@ -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,12 +252,16 @@ 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) ];
}; };
@ -249,9 +270,7 @@ in {
}; };
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,13 +286,17 @@ 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
;
}; };
}; };
}; };
@ -292,18 +315,10 @@ in {
// 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,14 +1,15 @@
{ 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 = [
(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) ]; 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";
@ -33,7 +34,11 @@
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 = [
(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) ]; 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;
@ -49,8 +54,13 @@
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

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"];})
}; };
}; };
@ -27,7 +30,5 @@
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,20 +1,21 @@
{ 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)

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,4 +1,5 @@
{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";

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,9 +8,15 @@
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";
};
}; };
}; };
}; };

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 = {

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.
@ -22,7 +19,8 @@ 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.";
@ -36,39 +34,31 @@ in {
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,10 +1,6 @@
{ { lib, pkgs, ... }:
lib, let
pkgs, inherit (lib)
...
}: let
inherit
(lib)
types types
mkEnableOption mkEnableOption
mkPackageOption mkPackageOption
@ -12,9 +8,12 @@
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,7 +21,8 @@ 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" { };
@ -53,6 +53,7 @@ in {
}; };
}; };
}; };
}) }
)
]; ];
} }

View file

@ -3,9 +3,9 @@
pkgs, pkgs,
config, config,
... ...
}: let }:
inherit let
(lib) inherit (lib)
types types
mkEnableOption mkEnableOption
mkPackageOption mkPackageOption
@ -16,7 +16,8 @@
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" { };

View file

@ -1,10 +1,6 @@
{ { lib, config, ... }:
lib, let
config, inherit (lib)
...
}: let
inherit
(lib)
concatLists concatLists
flip flip
mapAttrsToList mapAttrsToList
@ -16,7 +12,8 @@
; ;
cfg = config.users.deterministicIds; cfg = config.users.deterministicIds;
in { in
{
options = { options = {
users.deterministicIds = mkOption { users.deterministicIds = mkOption {
default = { }; default = { };
@ -25,7 +22,8 @@ in {
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,17 +3,18 @@
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" { };

View file

@ -3,16 +3,18 @@
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 ''
@ -32,21 +34,41 @@
<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
@ -54,13 +76,15 @@
default = { }; default = { };
}; };
}; };
})); }
)
);
}; };
}; };
config = let config =
inherit let
(lib) inherit (lib)
mkIf mkIf
flip flip
mapAttrsToList mapAttrsToList
@ -83,22 +107,36 @@
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
@ -46,19 +46,26 @@
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 = [
@ -97,12 +104,16 @@
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,7 +204,8 @@
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");
@ -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 {
@ -388,7 +410,8 @@ 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";
@ -399,17 +422,23 @@ in {
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";
@ -440,13 +469,15 @@ in {
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";
@ -543,14 +574,19 @@ in {
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";
}; };
@ -560,54 +596,58 @@ in {
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,36 +737,55 @@ 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 ];
@ -734,10 +803,12 @@ in {
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";
@ -760,7 +831,11 @@ in {
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";
} }
]; ];
@ -771,7 +846,10 @@ in {
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";
} }
]; ];
@ -806,9 +888,15 @@ 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 = [
"network.target"
"kanidm-unixd.service"
];
partOf = [ "kanidm-unixd.service" ]; partOf = [ "kanidm-unixd.service" ];
restartTriggers = [unixConfigFile clientConfigFile]; restartTriggers = [
unixConfigFile
clientConfigFile
];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/kanidm_unixd_tasks"; ExecStart = "${cfg.package}/bin/kanidm_unixd_tasks";
@ -828,7 +916,12 @@ 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;
@ -843,15 +936,11 @@ 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 ];
@ -860,12 +949,8 @@ in {
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 {
@ -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
@ -51,11 +51,8 @@ in {
tunnels = mkOption { tunnels = mkOption {
type = attrsOf ( type = attrsOf (
submodule ( submodule (
{ name, config, ... }:
{ {
name,
config,
...
}: {
options = { options = {
port = mkOption { port = mkOption {
type = port; type = port;
@ -132,8 +129,7 @@ in {
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,
@ -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;
@ -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,7 +57,9 @@ in {
type = types.bool; type = types.bool;
}; };
}; };
})); }
)
);
}; };
} }
]; ];
@ -61,22 +67,18 @@ in {
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,7 +1,6 @@
{ { self, ... }:
self, system:
... let
}: system: let
pkgs = self.pkgs.${system}; pkgs = self.pkgs.${system};
in in
pkgs.devshell.mkShell { pkgs.devshell.mkShell {

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 (
_: node:
flip mapAttrs' (node.config.guests or { }) ( 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,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";

View file

@ -9,31 +9,35 @@
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 (
_neovimFinal: neovimPrev: {
nativeBuildInputs = (neovimPrev.nativeBuildInputs or [ ]) ++ [ super.makeWrapper ]; 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 = ''
@ -168,5 +169,8 @@
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 {

View file

@ -19,7 +19,10 @@ 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; [
asl20
mit
];
maintainers = with maintainers; [ oddlama ]; 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

@ -19,7 +19,10 @@ 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";

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,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) )

View file

@ -1,18 +1,14 @@
{ { pkgs, lib, ... }:
pkgs, let
lib,
...
}: let
# set the modifier key to WIN # set the modifier key to WIN
MOD = "Super"; MOD = "Super";
#set the default resize step for herbstluft #set the default resize step for herbstluft
TAGS = map toString (lib.lists.range 1 9); TAGS = map toString (lib.lists.range 1 9);
in { in
{
xsession.windowManager.herbstluftwm = { xsession.windowManager.herbstluftwm = {
enable = true; enable = true;
package = pkgs.herbstluftwm.overrideAttrs (_finalAttrs: _previousAttrs: { package = pkgs.herbstluftwm.overrideAttrs (_finalAttrs: _previousAttrs: { doCheck = false; });
doCheck = false;
});
extraConfig = '' extraConfig = ''
herbstclient set auto_detect_monitors 1 herbstclient set auto_detect_monitors 1
killall polybar killall polybar

View file

@ -3,7 +3,8 @@
pkgs, pkgs,
lib, lib,
... ...
}: { }:
{
# import shared sway config # import shared sway config
imports = [ ../sway3.nix ]; imports = [ ../sway3.nix ];
systemd.user.services = { systemd.user.services = {
@ -16,13 +17,17 @@
enableSystemdTarget = true; enableSystemdTarget = true;
config = { config = {
startup = [ startup = [
{command = "${pkgs.xorg.xrandr}/bin/xrandr --output DVI-D-0 --mode 1920x1080 --pos 0x0 --rate 60.00 --output DP-4 --mode 2560x1440 --pos 1920x720 --primary --rate 144 --output HDMI-0 --pos 0x1080 --rate 60.00";} {
command = "${pkgs.xorg.xrandr}/bin/xrandr --output DVI-D-0 --mode 1920x1080 --pos 0x0 --rate 60.00 --output DP-4 --mode 2560x1440 --pos 1920x720 --primary --rate 144 --output HDMI-0 --pos 0x1080 --rate 60.00";
}
]; ];
menu = "rofi -show drun"; menu = "rofi -show drun";
keybindings = let keybindings =
let
cfg = config.xsession.windowManager.i3.config; cfg = config.xsession.windowManager.i3.config;
maim = "${pkgs.maim}/bin/maim -qs -b 1 --hidecursor"; maim = "${pkgs.maim}/bin/maim -qs -b 1 --hidecursor";
in { in
{
"Menu" = "exec ${cfg.menu}"; "Menu" = "exec ${cfg.menu}";
"Ctrl+F9" = "exec ${config.xsession.wallpapers.script}"; "Ctrl+F9" = "exec ${config.xsession.wallpapers.script}";
"${cfg.modifier}+F12" = "${cfg.modifier}+F12" =
@ -40,7 +45,8 @@
${maim} | ${pkgs.xclip}/bin/xclip -selection clipboard -t image/png ${maim} | ${pkgs.xclip}/bin/xclip -selection clipboard -t image/png
'' ''
); );
"${cfg.modifier}+F10" = let "${cfg.modifier}+F10" =
let
nsend = '' nsend = ''
${pkgs.libnotify}/bin/notify-send \ ${pkgs.libnotify}/bin/notify-send \
-h string:category:Screenshot\ -h string:category:Screenshot\

View file

@ -13,7 +13,8 @@
pkgs, pkgs,
nixosConfig, nixosConfig,
... ...
}: let }:
let
color = { color = {
shade1 = "#311B92"; shade1 = "#311B92";
shade2 = "#4527A0"; shade2 = "#4527A0";
@ -58,7 +59,8 @@
}; };
fontsize = "9"; fontsize = "9";
in { in
{
services.polybar = { services.polybar = {
enable = true; enable = true;
@ -89,9 +91,24 @@ in {
}; };
modules = with lib; { modules = with lib; {
left = concatStringsSep " " ["left1" "title" "left2"]; left = concatStringsSep " " [
"left1"
"title"
"left2"
];
center = concatStringsSep " " [ "workspaces" ]; center = concatStringsSep " " [ "workspaces" ];
right = concatStringsSep " " ["right5" "alsa" "right4" "battery" "right3" "network" "right2" "date" "right1" "keyboardswitcher"]; right = concatStringsSep " " [
"right5"
"alsa"
"right4"
"battery"
"right3"
"network"
"right2"
"date"
"right1"
"keyboardswitcher"
];
}; };
tray = { tray = {
@ -108,9 +125,22 @@ in {
dpi = 96; dpi = 96;
height = 22; height = 22;
modules = with lib; { modules = with lib; {
left = concatStringsSep " " ["left1" "title" "left2"]; left = concatStringsSep " " [
"left1"
"title"
"left2"
];
center = concatStringsSep " " [ "workspaces" ]; center = concatStringsSep " " [ "workspaces" ];
right = concatStringsSep " " ["right5" "alsa" "right3" "network" "right2" "date" "right1" "keyboardswitcher"]; right = concatStringsSep " " [
"right5"
"alsa"
"right3"
"network"
"right2"
"date"
"right1"
"keyboardswitcher"
];
}; };
}; };
patricknix = { patricknix = {
@ -120,14 +150,28 @@ in {
dpi = 144; dpi = 144;
height = 33; height = 33;
modules = with lib; { modules = with lib; {
left = concatStringsSep " " ["left1" "title" "left2"]; left = concatStringsSep " " [
"left1"
"title"
"left2"
];
center = concatStringsSep " " [ "workspaces" ]; center = concatStringsSep " " [ "workspaces" ];
right = concatStringsSep " " ["right5" "alsa" "right4" "battery" "right3" "network" "right2" "date" "right1" "keyboardswitcher"]; right = concatStringsSep " " [
"right5"
"alsa"
"right4"
"battery"
"right3"
"network"
"right2"
"date"
"right1"
"keyboardswitcher"
];
}; };
}; };
} }
.${nixosConfig.node.name} .${nixosConfig.node.name} or { };
or {};
# Functional MODULES # Functional MODULES
@ -296,8 +340,7 @@ in {
interface = "wlan0"; interface = "wlan0";
}; };
} }
.${nixosConfig.node.name} .${nixosConfig.node.name} or { };
or {};
"module/keyboardswitcher" = { "module/keyboardswitcher" = {
type = "custom/menu"; type = "custom/menu";

View file

@ -1,8 +1,5 @@
{ pkgs, config, ... }:
{ {
pkgs,
config,
...
}: {
home = { home = {
packages = with pkgs; [ packages = with pkgs; [
zathura zathura

View file

@ -32,63 +32,108 @@ let
trayOutput = "primary"; trayOutput = "primary";
} }
]; ];
floating.criteria = [ floating.criteria = [ { class = "Pavucontrol"; } ];
{class = "Pavucontrol";}
];
assigns = { assigns = {
"2:d" = [ "2:d" = [ { class = "^firefox$"; } ];
{class = "^firefox$";} "2:2" = [ { class = "^spotify$"; } ];
]; "3:u" = [ { class = "^thunderbird$"; } ];
"2:2" = [
{class = "^spotify$";}
];
"3:u" = [
{class = "^thunderbird$";}
];
"4:a" = [ "4:a" = [
{ class = "^bottles$"; } { class = "^bottles$"; }
{ class = "^steam$"; } { class = "^steam$"; }
{ class = "^prismlauncher$"; } { class = "^prismlauncher$"; }
]; ];
"1:F1" = [ "1:F1" = [ { class = "^discord$"; } ];
{class = "^discord$";}
];
"2:F2" = [ "2:F2" = [
{ class = "^Signal$"; } { class = "^Signal$"; }
{ class = "^TelegramDesktop$"; } { class = "^TelegramDesktop$"; }
]; ];
}; };
workspaceOutputAssign = let workspaceOutputAssign =
output = out: let
lib.lists.imap1 (i: x: { output =
out:
lib.lists.imap1 (
i: x: {
workspace = "${toString i}:${x}"; workspace = "${toString i}:${x}";
output = out; output = out;
}); }
);
in in
{ {
"desktopnix" = "desktopnix" =
output "HDMI-0" ["1" "2" "3" "4" "5" "6" "7" "8" "9"] output "HDMI-0" [
++ output "DP-4" ["j" "d" "u" "a" "x" "p"] "1"
++ output "DVI-D-0" ["F1" "F2" "F3" "F4"]; "2"
"3"
"4"
"5"
"6"
"7"
"8"
"9"
]
++ output "DP-4" [
"j"
"d"
"u"
"a"
"x"
"p"
]
++ output "DVI-D-0" [
"F1"
"F2"
"F3"
"F4"
];
"patricknix" = "patricknix" =
output "eDP-1" ["1" "2" "3" "4" "5" "6" "7" "8" "9"] output "eDP-1" [
++ output "DP-1" ["j" "d" "u" "a" "x" "p"]; "1"
"gojo" = "2"
output "eDP-1" ["1" "2" "3" "4" "5" "6"]; "3"
"4"
"5"
"6"
"7"
"8"
"9"
]
++ output "DP-1" [
"j"
"d"
"u"
"a"
"x"
"p"
];
"gojo" = output "eDP-1" [
"1"
"2"
"3"
"4"
"5"
"6"
];
} }
.${nixosConfig.node.name} .${nixosConfig.node.name} or [ ];
or [];
keybindings = keybindings =
(lib.attrsets.mergeAttrsList (map (x: (let (lib.attrsets.mergeAttrsList (
map (
x:
(
let
key = lib.elemAt (lib.strings.splitString ":" x.workspace) 1; key = lib.elemAt (lib.strings.splitString ":" x.workspace) 1;
in { in
{
"${modifier}+${key}" = "workspace ${x.workspace}"; "${modifier}+${key}" = "workspace ${x.workspace}";
"${modifier}+Shift+${key}" = "move container to workspace ${x.workspace}"; "${modifier}+Shift+${key}" = "move container to workspace ${x.workspace}";
})) }
cfg.workspaceOutputAssign)) )
) cfg.workspaceOutputAssign
))
// { // {
"${modifier}+t" = "exec ${terminal}"; "${modifier}+t" = "exec ${terminal}";
"${modifier}+b" = "exec firefox"; "${modifier}+b" = "exec firefox";
@ -137,7 +182,8 @@ let
"${modifier}+period" = "workspace next_on_output"; "${modifier}+period" = "workspace next_on_output";
}; };
}; };
in { in
{
wayland.windowManager.sway.config = cfg; wayland.windowManager.sway.config = cfg;
xsession.windowManager.i3.config = cfg; xsession.windowManager.i3.config = cfg;
@ -146,9 +192,7 @@ in {
bars.main = { bars.main = {
blocks = blocks =
[ [
{ { block = "net"; }
block = "net";
}
{ {
block = "cpu"; block = "cpu";
format = " $icon $utilization "; format = " $icon $utilization ";
@ -158,7 +202,10 @@ in {
format = " $icon $utilization $memory $temperature "; format = " $icon $utilization $memory $temperature ";
} }
] ]
++ {"patricknix" = [{block = "battery";}];}.${nixosConfig.node.name} or [] ++ {
"patricknix" = [ { block = "battery"; } ];
}
.${nixosConfig.node.name} or [ ]
++ [ ++ [
{ {
block = "sound"; block = "sound";

View file

@ -1,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
imports = [ imports = [
../. ../.
./fuzzel.nix ./fuzzel.nix

View file

@ -1,4 +1,5 @@
{pkgs, ...}: { { pkgs, ... }:
{
stylix.targets.fuzzel.enable = true; stylix.targets.fuzzel.enable = true;
home.packages = with pkgs; [ home.packages = with pkgs; [
(writeShellScriptBin "fuzzel" '' (writeShellScriptBin "fuzzel" ''

View file

@ -3,9 +3,9 @@
lib, lib,
nixosConfig, nixosConfig,
... ...
}: let }:
inherit let
(lib) inherit (lib)
mkMerge mkMerge
optionals optionals
elem elem
@ -46,7 +46,8 @@
# Listen to the Hyprland socket for events and process each line with the handle function # Listen to the Hyprland socket for events and process each line with the handle function
${pkgs.socat}/bin/socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done ${pkgs.socat}/bin/socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done
''; '';
in { in
{
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
enable = true; enable = true;
settings = mkMerge [ settings = mkMerge [
@ -82,7 +83,8 @@ in {
focus_preferred_method = 1; focus_preferred_method = 1;
workspace_center_on = 1; workspace_center_on = 1;
}; };
bind = let bind =
let
monitor_binds = { monitor_binds = {
"1" = "j"; "1" = "j";
"2" = "d"; "2" = "d";
@ -146,12 +148,10 @@ in {
"SUPER + SHIFT,q,exit" "SUPER + SHIFT,q,exit"
] ]
++ flip concatMap (map toString (lib.lists.range 1 9)) ( ++ flip concatMap (map toString (lib.lists.range 1 9)) (x: [
x: [
"SUPER,${monitor_binds."${x}"},workspace,${x}" "SUPER,${monitor_binds."${x}"},workspace,${x}"
"SUPER + SHIFT,${monitor_binds."${x}"},movetoworkspacesilent,${x}" "SUPER + SHIFT,${monitor_binds."${x}"},movetoworkspacesilent,${x}"
] ]);
);
cursor.no_warps = true; cursor.no_warps = true;
debug.disable_logs = false; debug.disable_logs = false;

View file

@ -1,8 +1,5 @@
{ config, nixosConfig, ... }:
{ {
config,
nixosConfig,
...
}: {
# import shared i3 config # import shared i3 config
imports = [ ../sway3.nix ]; imports = [ ../sway3.nix ];
stylix.targets.sway.enable = true; stylix.targets.sway.enable = true;
@ -42,9 +39,11 @@
map_to_output = "eDP-1"; map_to_output = "eDP-1";
}; };
}; };
keybindings = let keybindings =
let
cfg = config.wayland.windowManager.sway.config; cfg = config.wayland.windowManager.sway.config;
in { in
{
"Menu" = "exec ${cfg.menu}"; "Menu" = "exec ${cfg.menu}";
}; };
} }
@ -80,11 +79,12 @@
}; };
}; };
} }
.${nixosConfig.node.name} .${nixosConfig.node.name} or { };
or {}; extraConfig =
extraConfig = let let
cfg = config.wayland.windowManager.sway.config; cfg = config.wayland.windowManager.sway.config;
in '' in
''
bindgesture swipe:3:left workpace next bindgesture swipe:3:left workpace next
bindgesture swipe:3:right workpace prev bindgesture swipe:3:right workpace prev
bindgesture pinch:4:outward exec ${cfg.menu} bindgesture pinch:4:outward exec ${cfg.menu}

View file

@ -1,8 +1,5 @@
{ config, lib, ... }:
{ {
config,
lib,
...
}: {
services.swaync = { services.swaync = {
enable = true; enable = true;
settings = { settings = {

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