From 48c60629ab0d5182b906bf02357a14b77a64b88b Mon Sep 17 00:00:00 2001 From: Gabe Venberg Date: Sun, 13 Apr 2025 15:27:25 +0200 Subject: [PATCH] Restic: made modular backup declerations. Required tree-wide re-wiring of the host option. Now, rather than each host having a monolithic restic.nix file, the hosts restic.nix file just specifies the password and url of the restic repository. Eatch module then definies specific paths to backup and any pre and post commands that need to be performed. Each backed up service gets an independent systemd backup service and timer. --- configs/nixos/common.nix | 4 +- configs/nixos/docker.nix | 2 +- configs/nixos/factorio-docker.nix | 5 + configs/nixos/forgejo.nix | 10 + configs/nixos/i3/default.nix | 4 +- configs/nixos/i3/i3status-rust.nix | 6 +- configs/nixos/i3/lockscreen.nix | 2 +- configs/nixos/interactive-networking.nix | 2 +- configs/nixos/minecraft-docker.nix | 27 +- configs/nixos/radicale.nix | 6 + configs/nixos/sound.nix | 2 +- configs/nixos/sshd.nix | 6 +- configs/nixos/syncthing.nix | 7 +- configs/nixos/tailscale.nix | 1 - flake.nix | 24 +- hosts/archlaptop-vm/default.nix | 11 +- hosts/cirrostratus/default.nix | 13 +- hosts/cirrostratus/restic.nix | 77 +- hosts/cirrus/default.nix | 13 +- hosts/cirrus/restic.nix | 35 +- hosts/home-laptop.nix | 7 +- hosts/home-personal.nix | 5 +- hosts/rockhole64/adguard.nix | 1 - hosts/rockhole64/default.nix | 13 +- hosts/work-laptop.nix | 7 +- lib/default.nix | 13 +- lib/net.nix | 1269 ---------------------- modules/nixos/default.nix | 1 + modules/nixos/hostopts.nix | 2 +- modules/nixos/restic.nix | 148 +++ packages/aarch64-iso.nix | 13 +- packages/iso.nix | 13 +- packages/proxmox.nix | 17 +- packages/rpi3-sd-image.nix | 13 +- roles/nixos/graphical-vm.nix | 2 +- roles/nixos/vm.nix | 2 +- 36 files changed, 307 insertions(+), 1476 deletions(-) delete mode 100644 lib/net.nix create mode 100644 modules/nixos/restic.nix diff --git a/configs/nixos/common.nix b/configs/nixos/common.nix index 10eeb50..4d6b4de 100644 --- a/configs/nixos/common.nix +++ b/configs/nixos/common.nix @@ -48,13 +48,13 @@ in { environment.shells = lib.mkDefault [pkgs.zsh]; # if we arent setting our password from nix secrets, we need to allow changing it. users.mutableUsers = !inputs ? nix-secrets; - users.users.${config.host.user} = { + users.users.${config.host.details.user} = { isNormalUser = true; hashedPassword = if inputs ? nix-secrets then (lib.removeSuffix "\n" (builtins.readFile "${inputs.nix-secrets}/password-hash")) else defaultPasswordHash; - description = config.host.fullName; + description = config.host.details.fullName; shell = pkgs.zsh; extraGroups = ["wheel"]; }; diff --git a/configs/nixos/docker.nix b/configs/nixos/docker.nix index 4137898..c84eb29 100644 --- a/configs/nixos/docker.nix +++ b/configs/nixos/docker.nix @@ -9,5 +9,5 @@ enable = true; autoPrune.enable = true; }; - users.users.${config.host.user}.extraGroups = ["docker"]; + users.users.${config.host.details.user}.extraGroups = ["docker"]; } diff --git a/configs/nixos/factorio-docker.nix b/configs/nixos/factorio-docker.nix index 922d317..85e7f02 100644 --- a/configs/nixos/factorio-docker.nix +++ b/configs/nixos/factorio-docker.nix @@ -17,6 +17,11 @@ environment = {UPDATE_MODS_ON_START = "true";}; }; }; + + host.restic.backups.factorio = { + paths = ["/storage/factorio"]; + }; + imports = [ ./docker.nix ]; diff --git a/configs/nixos/forgejo.nix b/configs/nixos/forgejo.nix index 7efa1f2..d0bf6ba 100644 --- a/configs/nixos/forgejo.nix +++ b/configs/nixos/forgejo.nix @@ -39,5 +39,15 @@ in { }; }; + host.restic.backups.forgejo = { + paths = [ + "/var/lib/forgejo/custom" + "/var/lib/forgejo/data" + "/var/lib/forgejo/repositories" + ]; + preBackupCommands = "systemctl stop forgejo.service"; + postBackupCommands = "systemctl start forgejo.service"; + }; + imports = [./nginx.nix]; } diff --git a/configs/nixos/i3/default.nix b/configs/nixos/i3/default.nix index 24a459e..dca57c9 100644 --- a/configs/nixos/i3/default.nix +++ b/configs/nixos/i3/default.nix @@ -8,7 +8,7 @@ services.displayManager = { defaultSession = "none+i3"; autoLogin = { - user = config.host.user; + user = config.host.details.user; enable = true; }; }; @@ -22,7 +22,7 @@ }; xkb.options = "ctrl:nocaps,compose:rctrl"; }; - home-manager.users.${config.host.user} = {config, ...}: { + home-manager.users.${config.host.details.user} = {config, ...}: { home.packages = with pkgs; [ maim brightnessctl diff --git a/configs/nixos/i3/i3status-rust.nix b/configs/nixos/i3/i3status-rust.nix index bc5c819..3024afc 100644 --- a/configs/nixos/i3/i3status-rust.nix +++ b/configs/nixos/i3/i3status-rust.nix @@ -4,7 +4,7 @@ lib, ... }: { - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { config, osConfig, lib, @@ -46,14 +46,14 @@ } ( lib.mkIf - (!osConfig.host.isVm) + (!osConfig.host.details.isVm) { block = "backlight"; missing_format = ""; } ) ( - lib.mkIf (osConfig.host.isLaptop) + lib.mkIf (osConfig.host.details.isLaptop) { block = "battery"; driver = "upower"; diff --git a/configs/nixos/i3/lockscreen.nix b/configs/nixos/i3/lockscreen.nix index 5af25ac..0e7b7ca 100644 --- a/configs/nixos/i3/lockscreen.nix +++ b/configs/nixos/i3/lockscreen.nix @@ -7,7 +7,7 @@ environment.systemPackages = with pkgs; [ betterlockscreen ]; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { config, osConfig, lib, diff --git a/configs/nixos/interactive-networking.nix b/configs/nixos/interactive-networking.nix index 1410f8a..f765e32 100644 --- a/configs/nixos/interactive-networking.nix +++ b/configs/nixos/interactive-networking.nix @@ -5,5 +5,5 @@ }: { # Enable networking networking.networkmanager.enable = true; - users.users.${config.host.user}.extraGroups = ["networkmanager"]; + users.users.${config.host.details.user}.extraGroups = ["networkmanager"]; } diff --git a/configs/nixos/minecraft-docker.nix b/configs/nixos/minecraft-docker.nix index ba76e2c..87a374d 100644 --- a/configs/nixos/minecraft-docker.nix +++ b/configs/nixos/minecraft-docker.nix @@ -4,7 +4,23 @@ pkgs, lib, ... -}: { +}: let + preBackup = pkgs.writeShellScriptBin "mc-docker-pre-backup" '' + set -euxo pipefail + + docker exec minecraft rcon-cli "say server backing up, expect minor lag" + sleep 10 + docker exec minecraft rcon-cli "save-all flush" + docker exec minecraft rcon-cli "save-off" + sleep 10 + ''; + postBackup = pkgs.writeShellScriptBin "mc-docker-post-backup" '' + set -euxo pipefail + + docker exec minecraft rcon-cli "save-on" + docker exec minecraft rcon-cli "say server backup succsessful!" + ''; +in { virtualisation.oci-containers = { backend = "docker"; containers.minecraft = { @@ -31,7 +47,7 @@ # SETUP_ONLY = "true"; MOTD = "Welcome!"; DIFFICULTY = "normal"; - OPS ="TheToric"; + OPS = "TheToric"; ENFORCE_WHITELIST = "true"; ENABLE_WHITELIST = "true"; ANNOUNCE_PLAYER_ACHIEVEMENTS = "true"; @@ -42,6 +58,13 @@ extraOptions = ["--stop-timeout=60"]; }; }; + + host.restic.backups.minecraft = { + preBackupCommands = "${preBackup}/bin/mc-docker-pre-backup"; + postBackupCommands = "${postBackup}/bin/mc-docker-post-backup"; + paths = ["/storage/minecraft"]; + }; + imports = [ ./docker.nix ]; diff --git a/configs/nixos/radicale.nix b/configs/nixos/radicale.nix index 78db55d..54fa3e8 100644 --- a/configs/nixos/radicale.nix +++ b/configs/nixos/radicale.nix @@ -25,5 +25,11 @@ }; }; + host.restic.backups.radicale = { + paths = [ + "/var/lib/radicale" + ]; + }; + imports = [./nginx.nix]; } diff --git a/configs/nixos/sound.nix b/configs/nixos/sound.nix index 9284da2..45b03d8 100644 --- a/configs/nixos/sound.nix +++ b/configs/nixos/sound.nix @@ -13,7 +13,7 @@ pulse.enable = true; }; - home-manager.users.${config.host.user} = {config, ...}: { + home-manager.users.${config.host.details.user} = {config, ...}: { home.packages = with pkgs; [ pwvucontrol helvum diff --git a/configs/nixos/sshd.nix b/configs/nixos/sshd.nix index 341a261..e91be82 100644 --- a/configs/nixos/sshd.nix +++ b/configs/nixos/sshd.nix @@ -2,8 +2,8 @@ config, pkgs, inputs, - configLib, lib, + myLib, ... }: { services.openssh = { @@ -18,10 +18,10 @@ users.users.root.openssh.authorizedKeys.keys = lib.mkDefault ( if inputs ? nix-secrets - then (configLib.dirToStrings "${inputs.nix-secrets}/public-keys") + then (myLib.dirToStrings "${inputs.nix-secrets}/public-keys") else [] ); # if it can log into root, it should also be able to log in to the main user. - users.users.${config.host.user}.openssh.authorizedKeys.keys = + users.users.${config.host.details.user}.openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; } diff --git a/configs/nixos/syncthing.nix b/configs/nixos/syncthing.nix index 542aaa3..609a5d3 100644 --- a/configs/nixos/syncthing.nix +++ b/configs/nixos/syncthing.nix @@ -2,19 +2,18 @@ config, pkgs, inputs, - configLib, lib, ... }: { services.syncthing = { enable = true; - user = config.host.user; + user = config.host.details.user; group = "users"; overrideDevices = false; overrideFolders = false; openDefaultPorts = true; systemService = true; - dataDir = "/home/${config.host.user}/Sync"; - configDir = "/home/${config.host.user}/.local/state/syncthing"; + dataDir = "/home/${config.host.details.user}/Sync"; + configDir = "/home/${config.host.details.user}/.local/state/syncthing"; }; } diff --git a/configs/nixos/tailscale.nix b/configs/nixos/tailscale.nix index c770245..1c246ed 100644 --- a/configs/nixos/tailscale.nix +++ b/configs/nixos/tailscale.nix @@ -2,7 +2,6 @@ config, pkgs, inputs, - configLib, lib, ... }: { diff --git a/flake.nix b/flake.nix index 062cd1b..6208349 100644 --- a/flake.nix +++ b/flake.nix @@ -63,7 +63,7 @@ "aarch64-linux" ]; inherit (nixpkgs) lib; - configLib = import ./lib {inherit lib;}; + myLib = import ./lib {inherit lib;}; in { formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra); @@ -80,24 +80,22 @@ } ); - lib = configLib; - homeManagerModules = import ./modules/home-manager; # NixOS configuration entrypoint # Available through 'nixos-rebuild --flake .#your-hostname' nixosConfigurations = { - rockhole = import ./hosts/rockhole64 {inherit inputs configLib;}; - cirrus = import ./hosts/cirrus {inherit inputs configLib;}; - cirrostratus = import ./hosts/cirrostratus {inherit inputs configLib;}; + rockhole = import ./hosts/rockhole64 {inherit inputs myLib;}; + cirrus = import ./hosts/cirrus {inherit inputs myLib;}; + cirrostratus = import ./hosts/cirrostratus {inherit inputs myLib;}; }; # Standalone home-manager configuration entrypoint # Available through 'home-manager --flake .#your-username@your-hostname' homeConfigurations = { - "gabe@archlaptop" = import ./hosts/home-laptop.nix {inherit inputs configLib;}; - "gabe@linuxgamingrig" = import ./hosts/home-personal.nix {inherit inputs configLib;}; - "gabe@gvworklaptop" = import ./hosts/work-laptop.nix {inherit inputs configLib;}; + "gabe@archlaptop" = import ./hosts/home-laptop.nix {inherit inputs myLib;}; + "gabe@linuxgamingrig" = import ./hosts/home-personal.nix {inherit inputs myLib;}; + "gabe@gvworklaptop" = import ./hosts/work-laptop.nix {inherit inputs myLib;}; }; deploy = { @@ -121,10 +119,10 @@ }; packages.x86_64-linux = { - proxmox = import ./packages/proxmox.nix {inherit inputs configLib;}; - iso = import ./packages/iso.nix {inherit inputs configLib;}; - aarch-64-iso = import ./packages/aarch64-iso.nix {inherit inputs configLib;}; - rpi3-sd-image = import ./packages/rpi3-sd-image.nix {inherit inputs configLib;}; + proxmox = import ./packages/proxmox.nix {inherit inputs myLib;}; + iso = import ./packages/iso.nix {inherit inputs myLib;}; + aarch-64-iso = import ./packages/aarch64-iso.nix {inherit inputs myLib;}; + rpi3-sd-image = import ./packages/rpi3-sd-image.nix {inherit inputs myLib;}; }; templates = import ./templates; diff --git a/hosts/archlaptop-vm/default.nix b/hosts/archlaptop-vm/default.nix index 82562de..28df26a 100644 --- a/hosts/archlaptop-vm/default.nix +++ b/hosts/archlaptop-vm/default.nix @@ -1,11 +1,11 @@ { inputs, - configLib, + myLib, ... }: inputs.nixpkgs.lib.nixosSystem { system = "x86_64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; # > Our main nixos configuration file < modules = [ inputs.home-manager.nixosModules.home-manager @@ -21,7 +21,6 @@ inputs.nixpkgs.lib.nixosSystem { ({ config, pkgs, - configLib, ... }: { host = { @@ -33,14 +32,14 @@ inputs.nixpkgs.lib.nixosSystem { networking.hostName = "archlaptop-vm"; # Define your hostname. # Define a user account. Don't forget to set a password with ‘passwd’. - users.users.${config.host.user} = { + users.users.${config.host.details.user} = { packages = with pkgs; [firefox]; }; home-manager.sharedModules = [ inputs.sops-nix.homeManagerModules.sops ]; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, lib, @@ -50,7 +49,7 @@ inputs.nixpkgs.lib.nixosSystem { user = { git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/hosts/cirrostratus/default.nix b/hosts/cirrostratus/default.nix index 64b5b3a..0e6f73a 100644 --- a/hosts/cirrostratus/default.nix +++ b/hosts/cirrostratus/default.nix @@ -1,12 +1,12 @@ { inputs, - configLib, + myLib, ... }: # Kapr site server. inputs.nixpkgs.lib.nixosSystem { system = "x86_64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; # > Our main nixos configuration file < modules = [ inputs.home-manager.nixosModules.home-manager @@ -31,11 +31,10 @@ inputs.nixpkgs.lib.nixosSystem { ({ config, pkgs, - configLib, lib, ... }: { - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; gui.enable = false; @@ -72,18 +71,18 @@ inputs.nixpkgs.lib.nixosSystem { # virtualisation.docker.daemon.settings.data-root="/storage/docker"; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, lib, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { nvim.enable-lsp = false; git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/hosts/cirrostratus/restic.nix b/hosts/cirrostratus/restic.nix index 68e9cc6..3b00074 100644 --- a/hosts/cirrostratus/restic.nix +++ b/hosts/cirrostratus/restic.nix @@ -6,82 +6,37 @@ ... }: let port = "8090"; - # TODO: I should really make restic a custom module at this point, with an enable option, a option for being the host, - # and the ability to add paths and pre/post commands from multiple places. - preBackup = pkgs.writeShellScriptBin "mc-docker-pre-backup" '' - set -euxo pipefail - - docker exec minecraft rcon-cli "say server backing up, expect minor lag" - sleep 10 - docker exec minecraft rcon-cli "save-all flush" - docker exec minecraft rcon-cli "save-off" - sleep 10 - ''; - postBackup = pkgs.writeShellScriptBin "mc-docker-post-backup" '' - set -euxo pipefail - - docker exec minecraft rcon-cli "save-on" - docker exec minecraft rcon-cli "say server backup succsessful!" - ''; in { - services.restic.server = lib.mkIf (inputs ? nix-secrets) { - enable = true; - appendOnly = true; - dataDir = "/backup/restic"; - listenAddress = "127.0.0.1:${port}"; - }; - services.nginx.virtualHosts."restic.venberg.xyz" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://localhost:${port}"; - }; - }; sops = lib.mkIf (inputs ? nix-secrets) { secrets.restic-server-credentials = { sopsFile = "${inputs.nix-secrets}/restic-server"; format = "binary"; - path = "/backup/restic/.htpasswd"; owner = "restic"; }; secrets.restic-url = { sopsFile = "${inputs.nix-secrets}/restic-client.yaml"; - owner = config.host.user; + owner = config.host.details.user; }; secrets.restic-password = { sopsFile = "${inputs.nix-secrets}/restic-client.yaml"; - owner = config.host.user; + owner = config.host.details.user; }; }; - environment.systemPackages = with pkgs; [ - restic - ]; - - services.restic.backups = lib.mkIf (inputs ? nix-secrets) { - local = { - repositoryFile = "/backup/restic/"; - passwordFile = config.sops.secrets.restic-password.path; - initialize = true; - backupPrepareCommand = "${preBackup}/bin/mc-docker-pre-backup"; - backupCleanupCommand = "${postBackup}/bin/mc-docker-post-backup"; - paths = [ - "/storage/syncthing" - "/storage/factorio" - "/storage/minecraft" - ]; - pruneOpts = [ - "--keep-within 14d" - "--keep-daily 14" - "--keep-weekly 8" - "--keep-monthly 12" - "--keep-yearly 10" - ]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; - RandomizedDelaySec = "4h"; - }; + host.restic = { + enable = true; + repository = "/backup/restic/"; + passwordFile = config.sops.secrets.restic-password.path; + server = { + enable = true; + htpasswdPath = config.sops.secrets.restic-server-credentials.path; + domain = "restic.venberg.xyz"; + port = port; + repositoryPath = "/backup/restic"; }; }; + + host.restic.backups.syncthing = { + paths = ["/storage/syncthing"]; + }; } diff --git a/hosts/cirrus/default.nix b/hosts/cirrus/default.nix index 2e2fe41..f17d09e 100644 --- a/hosts/cirrus/default.nix +++ b/hosts/cirrus/default.nix @@ -1,12 +1,12 @@ { inputs, - configLib, + myLib, ... }: # Hetzner cloud multipurpouse server inputs.nixpkgs.lib.nixosSystem { system = "x86_64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; # > Our main nixos configuration file < modules = [ inputs.home-manager.nixosModules.home-manager @@ -25,11 +25,10 @@ inputs.nixpkgs.lib.nixosSystem { ({ config, pkgs, - configLib, lib, ... }: { - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; gui.enable = false; @@ -61,17 +60,17 @@ inputs.nixpkgs.lib.nixosSystem { }; }; }; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, lib, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/hosts/cirrus/restic.nix b/hosts/cirrus/restic.nix index 78e57ac..c6a3fa2 100644 --- a/hosts/cirrus/restic.nix +++ b/hosts/cirrus/restic.nix @@ -5,43 +5,20 @@ lib, ... }: { - environment.systemPackages = with pkgs; [ - restic - ]; - sops = lib.mkIf (inputs ? nix-secrets) { secrets.restic-url = { sopsFile = "${inputs.nix-secrets}/restic-client.yaml"; - owner = config.host.user; + owner = config.host.details.user; }; secrets.restic-password = { sopsFile = "${inputs.nix-secrets}/restic-client.yaml"; - owner = config.host.user; + owner = config.host.details.user; }; }; - services.restic.backups = lib.mkIf (inputs ? nix-secrets) { - remote = { - repositoryFile = config.sops.secrets.restic-url.path; - passwordFile = config.sops.secrets.restic-password.path; - initialize = true; - backupPrepareCommand = '' - systemctl stop forgejo.service - ''; - backupCleanupCommand = '' - systemctl start forgejo.service - ''; - paths = [ - "/var/lib/radicale" - "/var/lib/forgejo/custom" - "/var/lib/forgejo/data" - "/var/lib/forgejo/repositories" - ]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; - RandomizedDelaySec = "4h"; - }; - }; + host.restic = { + enable = true; + passwordFile = config.sops.secrets.restic-password.path; + repositoryFile = config.sops.secrets.restic-url.path; }; } diff --git a/hosts/home-laptop.nix b/hosts/home-laptop.nix index 5a6035a..4a66d2c 100644 --- a/hosts/home-laptop.nix +++ b/hosts/home-laptop.nix @@ -1,17 +1,16 @@ { inputs, - configLib, + myLib, ... }: inputs.home-manager.lib.homeManagerConfiguration { pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance - extraSpecialArgs = {inherit inputs configLib;}; + extraSpecialArgs = {inherit inputs myLib;}; modules = [ ({ config, pkgs, lib, - configLib, ... }: { # machine specific options @@ -25,7 +24,7 @@ inputs.home-manager.lib.homeManagerConfiguration { workProfile.enable = false; }; }; - host.isLaptop = true; + host.details.isLaptop = true; targets.genericLinux.enable = true; home.username = "gabe"; diff --git a/hosts/home-personal.nix b/hosts/home-personal.nix index ee67261..d37e69b 100644 --- a/hosts/home-personal.nix +++ b/hosts/home-personal.nix @@ -1,17 +1,16 @@ { inputs, - configLib, + myLib, ... }: inputs.home-manager.lib.homeManagerConfiguration { pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance - extraSpecialArgs = {inherit inputs configLib;}; + extraSpecialArgs = {inherit inputs myLib;}; modules = [ ({ config, pkgs, lib, - configLib, ... }: { # machine specific options diff --git a/hosts/rockhole64/adguard.nix b/hosts/rockhole64/adguard.nix index 868ba4e..9813901 100644 --- a/hosts/rockhole64/adguard.nix +++ b/hosts/rockhole64/adguard.nix @@ -2,7 +2,6 @@ config, pkgs, inputs, - configLib, lib, ... }: { diff --git a/hosts/rockhole64/default.nix b/hosts/rockhole64/default.nix index 0cc7c02..ed94293 100644 --- a/hosts/rockhole64/default.nix +++ b/hosts/rockhole64/default.nix @@ -1,11 +1,11 @@ { inputs, - configLib, + myLib, ... }: inputs.nixpkgs.lib.nixosSystem { system = "aarch64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; modules = [ inputs.home-manager.nixosModules.home-manager inputs.disko.nixosModules.disko @@ -19,7 +19,6 @@ inputs.nixpkgs.lib.nixosSystem { ({ config, pkgs, - configLib, ... }: { boot.initrd.kernelModules = [ @@ -33,7 +32,7 @@ inputs.nixpkgs.lib.nixosSystem { "phy_rockchip_pcie" ]; hardware.enableRedistributableFirmware = true; - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; }; @@ -53,17 +52,17 @@ inputs.nixpkgs.lib.nixosSystem { # home-manager.sharedModules = [ # inputs.sops-nix.homeManagerModules.sops # ]; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, lib, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/hosts/work-laptop.nix b/hosts/work-laptop.nix index c37779d..41aa48b 100644 --- a/hosts/work-laptop.nix +++ b/hosts/work-laptop.nix @@ -1,17 +1,16 @@ { inputs, - configLib, + myLib, ... }: inputs.home-manager.lib.homeManagerConfiguration { pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance - extraSpecialArgs = {inherit inputs configLib;}; + extraSpecialArgs = {inherit inputs myLib;}; modules = [ ({ config, pkgs, lib, - configLib, ... }: { # machine specific options @@ -28,7 +27,7 @@ inputs.home-manager.lib.homeManagerConfiguration { }; }; }; - host.isLaptop = true; + host.details.isLaptop = true; targets.genericLinux.enable = true; home.username = "gabe"; diff --git a/lib/default.nix b/lib/default.nix index 425fa9d..5675bad 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,6 +1,4 @@ -{lib}: let - net = import ./net.nix {inherit lib;}; -in { +{lib}: { dirToStrings = dir: (map (v: builtins.readFile "${dir}/${v}") (builtins.filter (v: (builtins.readFileType "${dir}/${v}") == "regular") ( @@ -11,13 +9,4 @@ in { ) else [] ))); - - calcSystemdDhcpPoolOffset = { - base, - start, - end, - }: { - offset = net.lib.net.ip.diff start base; - size = net.lib.net.ip.diff end start; - }; } diff --git a/lib/net.nix b/lib/net.nix deleted file mode 100644 index 702db28..0000000 --- a/lib/net.nix +++ /dev/null @@ -1,1269 +0,0 @@ -{lib ? null, ...}: let - net = - { - ip = { - # add :: (ip | mac | integer) -> ip -> ip - # - # Examples: - # - # Adding integer to IPv4: - # > net.ip.add 100 "10.0.0.1" - # "10.0.0.101" - # - # Adding IPv4 to IPv4: - # > net.ip.add "127.0.0.1" "10.0.0.1" - # "137.0.0.2" - # - # Adding IPv6 to IPv4: - # > net.ip.add "::cafe:beef" "10.0.0.1" - # "212.254.186.191" - # - # Adding MAC to IPv4 (overflows): - # > net.ip.add "fe:ed:fa:ce:f0:0d" "10.0.0.1" - # "4.206.240.14" - # - # Adding integer to IPv6: - # > net.ip.add 100 "dead:cafe:beef::" - # "dead:cafe:beef::64" - # - # Adding IPv4 to to IPv6: - # > net.ip.add "127.0.0.1" "dead:cafe:beef::" - # "dead:cafe:beef::7f00:1" - # - # Adding MAC to IPv6: - # > net.ip.add "fe:ed:fa:ce:f0:0d" "dead:cafe:beef::" - # "dead:cafe:beef::feed:face:f00d" - add = delta: ip: let - function = "net.ip.add"; - delta' = typechecks.numeric function "delta" delta; - ip' = typechecks.ip function "ip" ip; - in - builders.ip (implementations.ip.add delta' ip'); - - # diff :: ip -> ip -> (integer | ipv6) - # - # net.ip.diff is the reverse of net.ip.add: - # - # net.ip.diff (net.ip.add a b) a = b - # net.ip.diff (net.ip.add a b) b = a - # - # The difference between net.ip.diff and net.ip.subtract is that - # net.ip.diff will try its best to return an integer (falling back - # to an IPv6 if the result is too big to fit in an integer). This is - # useful if you have two hosts that you know are on the same network - # and you just want to calculate the offset between them — a result - # like "0.0.0.10" is not very useful (which is what you would get - # from net.ip.subtract). - diff = minuend: subtrahend: let - function = "net.ip.diff"; - minuend' = typechecks.ip function "minuend" minuend; - subtrahend' = typechecks.ip function "subtrahend" subtrahend; - result = implementations.ip.diff minuend' subtrahend'; - in - if result ? ipv6 - then builders.ipv6 result - else result; - - # subtract :: (ip | mac | integer) -> ip -> ip - # - # net.ip.subtract is also the reverse of net.ip.add: - # - # net.ip.subtract a (net.ip.add a b) = b - # net.ip.subtract b (net.ip.add a b) = a - # - # The difference between net.ip.subtract and net.ip.diff is that - # net.ip.subtract will always return the same type as its "ip" - # parameter. Its implementation takes the "delta" parameter, - # coerces it to be the same type as the "ip" paramter, negates it - # (using two's complement), and then adds it to "ip". - subtract = delta: ip: let - function = "net.ip.subtract"; - delta' = typechecks.numeric function "delta" delta; - ip' = typechecks.ip function "ip" ip; - in - builders.ip (implementations.ip.subtract delta' ip'); - }; - - mac = { - # add :: (ip | mac | integer) -> mac -> mac - # - # Examples: - # - # Adding integer to MAC: - # > net.mac.add 100 "fe:ed:fa:ce:f0:0d" - # "fe:ed:fa:ce:f0:71" - # - # Adding IPv4 to MAC: - # > net.mac.add "127.0.0.1" "fe:ed:fa:ce:f0:0d" - # "fe:ee:79:ce:f0:0e" - # - # Adding IPv6 to MAC: - # > net.mac.add "::cafe:beef" "fe:ed:fa:ce:f0:0d" - # "fe:ee:c5:cd:aa:cb - # - # Adding MAC to MAC: - # > net.mac.add "fe:ed:fa:00:00:00" "00:00:00:ce:f0:0d" - # "fe:ed:fa:ce:f0:0d" - add = delta: mac: let - function = "net.mac.add"; - delta' = typechecks.numeric function "delta" delta; - mac' = typechecks.mac function "mac" mac; - in - builders.mac (implementations.mac.add delta' mac'); - - # diff :: mac -> mac -> integer - # - # net.mac.diff is the reverse of net.mac.add: - # - # net.mac.diff (net.mac.add a b) a = b - # net.mac.diff (net.mac.add a b) b = a - # - # The difference between net.mac.diff and net.mac.subtract is that - # net.mac.diff will always return an integer. - diff = minuend: subtrahend: let - function = "net.mac.diff"; - minuend' = typechecks.mac function "minuend" minuend; - subtrahend' = typechecks.mac function "subtrahend" subtrahend; - in - implementations.mac.diff minuend' subtrahend'; - - # subtract :: (ip | mac | integer) -> mac -> mac - # - # net.mac.subtract is also the reverse of net.ip.add: - # - # net.mac.subtract a (net.mac.add a b) = b - # net.mac.subtract b (net.mac.add a b) = a - # - # The difference between net.mac.subtract and net.mac.diff is that - # net.mac.subtract will always return a MAC address. - subtract = delta: mac: let - function = "net.mac.subtract"; - delta' = typechecks.numeric function "delta" delta; - mac' = typechecks.mac function "mac" mac; - in - builders.mac (implementations.mac.subtract delta' mac'); - }; - - cidr = { - # add :: (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.add 2 "127.0.0.0/8" - # "129.0.0.0/8" - # - # > net.cidr.add (-2) "127.0.0.0/8" - # "125.0.0.0/8" - add = delta: cidr: let - function = "net.cidr.add"; - delta' = typechecks.numeric function "delta" delta; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.cidr (implementations.cidr.add delta' cidr'); - - # child :: cidr -> cidr -> bool - # - # > net.cidr.child "10.10.10.0/24" "10.0.0.0/8" - # true - # - # > net.cidr.child "127.0.0.0/8" "10.0.0.0/8" - # false - child = subcidr: cidr: let - function = "net.cidr.child"; - subcidr' = typechecks.cidr function "subcidr" subcidr; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.child subcidr' cidr'; - - # contains :: ip -> cidr -> bool - # - # > net.cidr.contains "127.0.0.1" "127.0.0.0/8" - # true - # - # > net.cidr.contains "127.0.0.1" "192.168.0.0/16" - # false - contains = ip: cidr: let - function = "net.cidr.contains"; - ip' = typechecks.ip function "ip" ip; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.contains ip' cidr'; - - # capacity :: cidr -> integer - # - # > net.cidr.capacity "172.16.0.0/12" - # 1048576 - # - # > net.cidr.capacity "dead:cafe:beef::/96" - # 4294967296 - # - # > net.cidr.capacity "dead:cafe:beef::/48" (saturates to maxBound) - # 9223372036854775807 - capacity = cidr: let - function = "net.cidr.capacity"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.capacity cidr'; - - # host :: (ip | mac | integer) -> cidr -> ip - # - # > net.cidr.host 10000 "10.0.0.0/8" - # 10.0.39.16 - # - # > net.cidr.host 10000 "dead:cafe:beef::/64" - # "dead:cafe:beef::2710" - # - # net.cidr.host "127.0.0.1" "dead:cafe:beef::/48" - # > "dead:cafe:beef::7f00:1" - # - # Inpsired by: - # https://www.terraform.io/docs/configuration/functions/cidrhost.html - host = hostnum: cidr: let - function = "net.cidr.host"; - hostnum' = typechecks.numeric function "hostnum" hostnum; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.ip (implementations.cidr.host hostnum' cidr'); - - # length :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 8 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 48 - length = cidr: let - function = "net.cidr.length"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.length cidr'; - - # make :: integer -> ip -> cidr - # - # > net.cidr.make 24 "192.168.0.150" - # "192.168.0.0/24" - # - # > net.cidr.make 40 "dead:cafe:beef::feed:face:f00d" - # "dead:cafe:be00::/40" - make = length: base: let - function = "net.cidr.make"; - length' = typechecks.int function "length" length; - base' = typechecks.ip function "base" base; - in - builders.cidr (implementations.cidr.make length' base'); - - # netmask :: cidr -> ip - # - # > net.cidr.netmask "192.168.0.0/24" - # "255.255.255.0" - # - # > net.cidr.netmask "dead:cafe:beef::/64" - # "ffff:ffff:ffff:ffff::" - netmask = cidr: let - function = "net.cidr.netmask"; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.ip (implementations.cidr.netmask cidr'); - - # size :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 24 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 80 - size = cidr: let - function = "net.cidr.size"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.size cidr'; - - # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.subnet 4 2 "172.16.0.0/12" - # "172.18.0.0/16" - # - # > net.cidr.subnet 4 15 "10.1.2.0/24" - # "10.1.2.240/28" - # - # > net.cidr.subnet 16 162 "fd00:fd12:3456:7890::/56" - # "fd00:fd12:3456:7800:a200::/72" - # - # Inspired by: - # https://www.terraform.io/docs/configuration/functions/cidrsubnet.html - subnet = length: netnum: cidr: let - function = "net.cidr.subnet"; - length' = typechecks.int function "length" length; - netnum' = typechecks.numeric function "netnum" netnum; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.cidr (implementations.cidr.subnet length' netnum' cidr'); - }; - } - // ( - if builtins.isNull lib - then {} - else { - types = let - mkParsedOptionType = { - name, - description, - parser, - builder, - }: let - normalize = def: - def - // { - value = builder (parser def.value); - }; - in - lib.mkOptionType { - inherit name description; - check = x: builtins.isString x && parser x != null; - merge = loc: defs: lib.mergeEqualOption loc (map normalize defs); - }; - - dependent-ip = type: cidr: let - cidrs = - if builtins.isList cidr - then cidr - else [cidr]; - in - lib.types.addCheck type (i: lib.any (net.cidr.contains i) cidrs) - // { - description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; - }; - - dependent-cidr = type: cidr: let - cidrs = - if builtins.isList cidr - then cidr - else [cidr]; - in - lib.types.addCheck type (i: lib.any (net.cidr.child i) cidrs) - // { - description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; - }; - in rec { - ip = mkParsedOptionType { - name = "ip"; - description = "IPv4 or IPv6 address"; - parser = parsers.ip; - builder = builders.ip; - }; - - ip-in = dependent-ip ip; - - ipv4 = mkParsedOptionType { - name = "ipv4"; - description = "IPv4 address"; - parser = parsers.ipv4; - builder = builders.ipv4; - }; - - ipv4-in = dependent-ip ipv4; - - ipv6 = mkParsedOptionType { - name = "ipv6"; - description = "IPv6 address"; - parser = parsers.ipv6; - builder = builders.ipv6; - }; - - ipv6-in = dependent-ip ipv6; - - cidr = mkParsedOptionType { - name = "cidr"; - description = "IPv4 or IPv6 address range in CIDR notation"; - parser = parsers.cidr; - builder = builders.cidr; - }; - - cidr-in = dependent-cidr cidr; - - cidrv4 = mkParsedOptionType { - name = "cidrv4"; - description = "IPv4 address range in CIDR notation"; - parser = parsers.cidrv4; - builder = builders.cidrv4; - }; - - cidrv4-in = dependent-cidr cidrv4; - - cidrv6 = mkParsedOptionType { - name = "cidrv6"; - description = "IPv6 address range in CIDR notation"; - parser = parsers.cidrv6; - builder = builders.cidrv6; - }; - - cidrv6-in = dependent-cidr cidrv6; - - mac = mkParsedOptionType { - name = "mac"; - description = "MAC address"; - parser = parsers.mac; - builder = builders.mac; - }; - }; - } - ); - - list = { - cons = a: b: [a] ++ b; - }; - - bit = let - shift = n: x: - if n < 0 - then x * math.pow 2 (-n) - else let - safeDiv = n: d: - if d == 0 - then 0 - else n / d; - d = math.pow 2 n; - in - if x < 0 - then not (safeDiv (not x) d) - else safeDiv x d; - - left = n: shift (-n); - - right = shift; - - and = builtins.bitAnd; - - or = builtins.bitOr; - - xor = builtins.bitXor; - - not = xor (-1); - - mask = n: and (left n 1 - 1); - in { - inherit left right and or xor not mask; - }; - - math = rec { - max = a: b: - if a > b - then a - else b; - - min = a: b: - if a < b - then a - else b; - - clamp = a: b: c: max a (min b c); - - pow = x: n: - if n == 0 - then 1 - else if bit.and n 1 != 0 - then x * pow (x * x) ((n - 1) / 2) - else pow (x * x) (n / 2); - }; - - parsers = let - # fmap :: (a -> b) -> parser a -> parser b - fmap = f: ma: bind ma (a: pure (f a)); - - # pure :: a -> parser a - pure = a: string: { - leftovers = string; - result = a; - }; - - # liftA2 :: (a -> b -> c) -> parser a -> parser b -> parser c - liftA2 = f: ma: mb: bind ma (a: bind mb (b: pure (f a b))); - liftA3 = f: a: b: ap (liftA2 f a b); - liftA4 = f: a: b: c: ap (liftA3 f a b c); - liftA5 = f: a: b: c: d: ap (liftA4 f a b c d); - liftA6 = f: a: b: c: d: e: ap (liftA5 f a b c d e); - - # ap :: parser (a -> b) -> parser a -> parser b - ap = liftA2 (a: a); - - # then_ :: parser a -> parser b -> parser b - then_ = liftA2 (a: b: b); - - # empty :: parser a - empty = string: null; - - # alt :: parser a -> parser a -> parser a - alt = left: right: string: let - result = left string; - in - if builtins.isNull result - then right string - else result; - - # guard :: bool -> parser {} - guard = condition: - if condition - then pure {} - else empty; - - # mfilter :: (a -> bool) -> parser a -> parser a - mfilter = f: parser: bind parser (a: then_ (guard (f a)) (pure a)); - - # some :: parser a -> parser [a] - some = v: liftA2 list.cons v (many v); - - # many :: parser a -> parser [a] - many = v: alt (some v) (pure []); - - # bind :: parser a -> (a -> parser b) -> parser b - bind = parser: f: string: let - a = parser string; - in - if builtins.isNull a - then null - else f a.result a.leftovers; - - # run :: parser a -> string -> maybe a - run = parser: string: let - result = parser string; - in - if builtins.isNull result || result.leftovers != "" - then null - else result.result; - - next = string: - if string == "" - then null - else { - leftovers = builtins.substring 1 (-1) string; - result = builtins.substring 0 1 string; - }; - - # Count how many characters were consumed by a parser - count = parser: string: let - result = parser string; - in - if builtins.isNull result - then null - else - result - // { - result = { - inherit (result) result; - count = with result; - builtins.stringLength string - builtins.stringLength leftovers; - }; - }; - - # Limit the parser to n characters at most - limit = n: parser: - fmap (a: a.result) (mfilter (a: a.count <= n) (count parser)); - - # Ensure the parser consumes exactly n characters - exactly = n: parser: - fmap (a: a.result) (mfilter (a: a.count == n) (count parser)); - - char = c: bind next (c': guard (c == c')); - - string = css: - if css == "" - then pure {} - else let - c = builtins.substring 0 1 css; - cs = builtins.substring 1 (-1) css; - in - then_ (char c) (string cs); - - digit = set: - bind next ( - c: - then_ - (guard (builtins.hasAttr c set)) - (pure (builtins.getAttr c set)) - ); - - decimalDigits = { - "0" = 0; - "1" = 1; - "2" = 2; - "3" = 3; - "4" = 4; - "5" = 5; - "6" = 6; - "7" = 7; - "8" = 8; - "9" = 9; - }; - - hexadecimalDigits = - decimalDigits - // { - "a" = 10; - "b" = 11; - "c" = 12; - "d" = 13; - "e" = 14; - "f" = 15; - "A" = 10; - "B" = 11; - "C" = 12; - "D" = 13; - "E" = 14; - "F" = 15; - }; - - fromDecimalDigits = builtins.foldl' (a: c: a * 10 + c) 0; - fromHexadecimalDigits = builtins.foldl' (a: bit.or (bit.left 4 a)) 0; - - # disallow leading zeros - decimal = bind (digit decimalDigits) ( - n: - if n == 0 - then pure 0 - else - fmap - (ns: fromDecimalDigits (list.cons n ns)) - (many (digit decimalDigits)) - ); - - hexadecimal = fmap fromHexadecimalDigits (some (digit hexadecimalDigits)); - - ipv4 = let - dot = char "."; - - octet = mfilter (n: n < 256) decimal; - - octet' = then_ dot octet; - - fromOctets = a: b: c: d: { - ipv4 = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d; - }; - in - liftA4 fromOctets octet octet' octet' octet'; - - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#parser - ipv6 = let - colon = char ":"; - - hextet = limit 4 hexadecimal; - - hextet' = then_ colon hextet; - - fromHextets = hextets: - if builtins.length hextets != 8 - then empty - else let - a = builtins.elemAt hextets 0; - b = builtins.elemAt hextets 1; - c = builtins.elemAt hextets 2; - d = builtins.elemAt hextets 3; - e = builtins.elemAt hextets 4; - f = builtins.elemAt hextets 5; - g = builtins.elemAt hextets 6; - h = builtins.elemAt hextets 7; - in - pure { - ipv6 = { - a = bit.or (bit.left 16 a) b; - b = bit.or (bit.left 16 c) d; - c = bit.or (bit.left 16 e) f; - d = bit.or (bit.left 16 g) h; - }; - }; - - ipv4' = - fmap - ( - address: let - upper = bit.right 16 address.ipv4; - lower = bit.mask 16 address.ipv4; - in [upper lower] - ) - ipv4; - - part = n: let - n' = n + 1; - hex = - liftA2 list.cons hextet - ( - then_ colon - ( - alt - (then_ colon (doubleColon n')) - (part n') - ) - ); - in - if n == 7 - then fmap (a: [a]) hextet - else if n == 6 - then alt ipv4' hex - else hex; - - doubleColon = n: - bind (alt afterDoubleColon (pure [])) ( - rest: let - missing = 8 - n - builtins.length rest; - in - if missing < 0 - then empty - else pure (builtins.genList (_: 0) missing ++ rest) - ); - - afterDoubleColon = - alt ipv4' - ( - liftA2 list.cons hextet - ( - alt - (then_ colon afterDoubleColon) - (pure []) - ) - ); - in - bind - ( - alt - ( - then_ - (string "::") - (doubleColon 0) - ) - (part 0) - ) - fromHextets; - - cidrv4 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv4 - (then_ (char "/") (mfilter (n: n <= 32) decimal)); - - cidrv6 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv6 - (then_ (char "/") (mfilter (n: n <= 128) decimal)); - - mac = let - colon = char ":"; - - octet = exactly 2 hexadecimal; - - octet' = then_ colon octet; - - fromOctets = a: b: c: d: e: f: { - mac = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d)) e)) f; - }; - in - liftA6 fromOctets octet octet' octet' octet' octet' octet'; - in { - ipv4 = run ipv4; - ipv6 = run ipv6; - ip = run (alt ipv4 ipv6); - cidrv4 = run cidrv4; - cidrv6 = run cidrv6; - cidr = run (alt cidrv4 cidrv6); - mac = run mac; - numeric = run (alt (alt ipv4 ipv6) mac); - }; - - builders = let - ipv4 = address: let - abcd = address.ipv4; - abc = bit.right 8 abcd; - ab = bit.right 8 abc; - a = bit.right 8 ab; - b = bit.mask 8 ab; - c = bit.mask 8 abc; - d = bit.mask 8 abcd; - in - builtins.concatStringsSep "." (map toString [a b c d]); - - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#encode - ipv6 = address: let - digits = "0123456789abcdef"; - - toHexString = n: let - rest = bit.right 4 n; - current = bit.mask 4 n; - prefix = - if rest == 0 - then "" - else toHexString rest; - in "${prefix}${builtins.substring current 1 digits}"; - in - if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) - then "::${ipv4 {ipv4 = address.ipv6.d;}}" - else if (with address.ipv6; a == 0 && b == 0 && c == 65535) - then "::ffff:${ipv4 {ipv4 = address.ipv6.d;}}" - else let - a = bit.right 16 address.ipv6.a; - b = bit.mask 16 address.ipv6.a; - c = bit.right 16 address.ipv6.b; - d = bit.mask 16 address.ipv6.b; - e = bit.right 16 address.ipv6.c; - f = bit.mask 16 address.ipv6.c; - g = bit.right 16 address.ipv6.d; - h = bit.mask 16 address.ipv6.d; - - hextets = [a b c d e f g h]; - - # calculate the position and size of the longest sequence of - # zeroes within the list of hextets - longest = let - go = i: current: best: - if i < builtins.length hextets - then let - n = builtins.elemAt hextets i; - - current' = - if n == 0 - then - if builtins.isNull current - then { - size = 1; - position = i; - } - else - current - // { - size = current.size + 1; - } - else null; - - best' = - if n == 0 - then - if builtins.isNull best - then current' - else if current'.size > best.size - then current' - else best - else best; - in - go (i + 1) current' best' - else best; - in - go 0 null null; - - format = hextets: - builtins.concatStringsSep ":" (map toHexString hextets); - in - if builtins.isNull longest - then format hextets - else let - sublist = i: length: xs: - map - (builtins.elemAt xs) - (builtins.genList (x: x + i) length); - - end = longest.position + longest.size; - - before = sublist 0 longest.position hextets; - - after = sublist end (builtins.length hextets - end) hextets; - in "${format before}::${format after}"; - - ip = address: - if address ? ipv4 - then ipv4 address - else ipv6 address; - - cidrv4 = cidr: "${ipv4 cidr.base}/${toString cidr.length}"; - - cidrv6 = cidr: "${ipv6 cidr.base}/${toString cidr.length}"; - - cidr = cidr: "${ip cidr.base}/${toString cidr.length}"; - - mac = address: let - digits = "0123456789abcdef"; - octet = n: let - upper = bit.right 4 n; - lower = bit.mask 4 n; - in "${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}"; - in let - a = bit.mask 8 (bit.right 40 address.mac); - b = bit.mask 8 (bit.right 32 address.mac); - c = bit.mask 8 (bit.right 24 address.mac); - d = bit.mask 8 (bit.right 16 address.mac); - e = bit.mask 8 (bit.right 8 address.mac); - f = bit.mask 8 (bit.right 0 address.mac); - in "${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}"; - in { - inherit ipv4 ipv6 ip cidrv4 cidrv6 cidr mac; - }; - - arithmetic = rec { - # or :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - or = a_: b: let - a = coerce b a_; - in - if a ? ipv6 - then { - ipv6 = { - a = bit.or a.ipv6.a b.ipv6.a; - b = bit.or a.ipv6.b b.ipv6.b; - c = bit.or a.ipv6.c b.ipv6.c; - d = bit.or a.ipv6.d b.ipv6.d; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.or a.ipv4 b.ipv4; - } - else if a ? mac - then { - mac = bit.or a.mac b.mac; - } - else bit.or a b; - - # and :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - and = a_: b: let - a = coerce b a_; - in - if a ? ipv6 - then { - ipv6 = { - a = bit.and a.ipv6.a b.ipv6.a; - b = bit.and a.ipv6.b b.ipv6.b; - c = bit.and a.ipv6.c b.ipv6.c; - d = bit.and a.ipv6.d b.ipv6.d; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.and a.ipv4 b.ipv4; - } - else if a ? mac - then { - mac = bit.and a.mac b.mac; - } - else bit.and a b; - - # not :: (ip | mac | integer) -> (ip | mac | integer) - not = a: - if a ? ipv6 - then { - ipv6 = { - a = bit.mask 32 (bit.not a.ipv6.a); - b = bit.mask 32 (bit.not a.ipv6.b); - c = bit.mask 32 (bit.not a.ipv6.c); - d = bit.mask 32 (bit.not a.ipv6.d); - }; - } - else if a ? ipv4 - then { - ipv4 = bit.mask 32 (bit.not a.ipv4); - } - else if a ? mac - then { - mac = bit.mask 48 (bit.not a.mac); - } - else bit.not a; - - # add :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - add = let - split = a: { - fst = bit.mask 32 (bit.right 32 a); - snd = bit.mask 32 a; - }; - in - a_: b: let - a = coerce b a_; - in - if a ? ipv6 - then let - a' = split (a.ipv6.a + b.ipv6.a + b'.fst); - b' = split (a.ipv6.b + b.ipv6.b + c'.fst); - c' = split (a.ipv6.c + b.ipv6.c + d'.fst); - d' = split (a.ipv6.d + b.ipv6.d); - in { - ipv6 = { - a = a'.snd; - b = b'.snd; - c = c'.snd; - d = d'.snd; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.mask 32 (a.ipv4 + b.ipv4); - } - else if a ? mac - then { - mac = bit.mask 48 (a.mac + b.mac); - } - else a + b; - - # subtract :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - subtract = a: b: add (add 1 (not (coerce b a))) b; - - # diff :: (ip | mac | integer) -> (ip | mac | integer) -> (ipv6 | integer) - diff = a: b: let - toIPv6 = coerce {ipv6.a = 0;}; - result = (subtract b (toIPv6 a)).ipv6; - max32 = bit.left 32 1 - 1; - in - if result.a == 0 && result.b == 0 && bit.right 31 result.c == 0 || result.a == max32 && result.b == max32 && bit.right 31 result.c == 1 - then bit.or (bit.left 32 result.c) result.d - else { - ipv6 = result; - }; - - # left :: integer -> (ip | mac | integer) -> (ip | mac | integer) - left = i: right (-i); - - # right :: integer -> (ip | mac | integer) -> (ip | mac | integer) - right = let - step = i: x: { - _1 = bit.mask 32 (bit.right (i + 96) x); - _2 = bit.mask 32 (bit.right (i + 64) x); - _3 = bit.mask 32 (bit.right (i + 32) x); - _4 = bit.mask 32 (bit.right i x); - _5 = bit.mask 32 (bit.right (i - 32) x); - _6 = bit.mask 32 (bit.right (i - 64) x); - _7 = bit.mask 32 (bit.right (i - 96) x); - }; - ors = builtins.foldl' bit.or 0; - in - i: x: - if x ? ipv6 - then let - a' = step i x.ipv6.a; - b' = step i x.ipv6.b; - c' = step i x.ipv6.c; - d' = step i x.ipv6.d; - in { - ipv6 = { - a = ors [a'._4 b'._3 c'._2 d'._1]; - b = ors [a'._5 b'._4 c'._3 d'._2]; - c = ors [a'._6 b'._5 c'._4 d'._3]; - d = ors [a'._7 b'._6 c'._5 d'._4]; - }; - } - else if x ? ipv4 - then { - ipv4 = bit.mask 32 (bit.right i x.ipv4); - } - else if x ? mac - then { - mac = bit.mask 48 (bit.right i x.mac); - } - else bit.right i x; - - # shadow :: integer -> (ip | mac | integer) -> (ip | mac | integer) - shadow = n: a: and (right n (left n (coerce a (-1)))) a; - - # coshadow :: integer -> (ip | mac | integer) -> (ip | mac | integer) - coshadow = n: a: and (not (right n (left n (coerce a (-1))))) a; - - # coerce :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - coerce = target: value: - if target ? ipv6 - then - if value ? ipv6 - then value - else if value ? ipv4 - then { - ipv6 = { - a = 0; - b = 0; - c = 0; - d = value.ipv4; - }; - } - else if value ? mac - then { - ipv6 = { - a = 0; - b = 0; - c = bit.right 32 value.mac; - d = bit.mask 32 value.mac; - }; - } - else { - ipv6 = { - a = bit.mask 32 (bit.right 96 value); - b = bit.mask 32 (bit.right 64 value); - c = bit.mask 32 (bit.right 32 value); - d = bit.mask 32 value; - }; - } - else if target ? ipv4 - then - if value ? ipv6 - then { - ipv4 = value.ipv6.d; - } - else if value ? ipv4 - then value - else if value ? mac - then { - ipv4 = bit.mask 32 value.mac; - } - else { - ipv4 = bit.mask 32 value; - } - else if target ? mac - then - if value ? ipv6 - then { - mac = bit.or (bit.left 32 (bit.mask 16 value.ipv6.c)) value.ipv6.d; - } - else if value ? ipv4 - then { - mac = value.ipv4; - } - else if value ? mac - then value - else { - mac = bit.mask 48 value; - } - else if value ? ipv6 - then - builtins.foldl' bit.or 0 - [ - (bit.left 96 value.ipv6.a) - (bit.left 64 value.ipv6.b) - (bit.left 32 value.ipv6.c) - value.ipv6.d - ] - else if value ? ipv4 - then value.ipv4 - else if value ? mac - then value.mac - else value; - }; - - implementations = { - ip = { - # add :: (ip | mac | integer) -> ip -> ip - add = arithmetic.add; - - # diff :: ip -> ip -> (ipv6 | integer) - diff = arithmetic.diff; - - # subtract :: (ip | mac | integer) -> ip -> ip - subtract = arithmetic.subtract; - }; - - mac = { - # add :: (ip | mac | integer) -> mac -> mac - add = arithmetic.add; - - # diff :: mac -> mac -> (ipv6 | integer) - diff = arithmetic.diff; - - # subtract :: (ip | mac | integer) -> mac -> mac - subtract = arithmetic.subtract; - }; - - cidr = rec { - # add :: (ip | mac | integer) -> cidr -> cidr - add = delta: cidr: let - size' = size cidr; - in { - base = arithmetic.left size' (arithmetic.add delta (arithmetic.right size' cidr.base)); - inherit (cidr) length; - }; - - # capacity :: cidr -> integer - capacity = cidr: let - size' = size cidr; - in - if size' > 62 - then 9223372036854775807 # maxBound to prevent overflow - else bit.left size' 1; - - # child :: cidr -> cidr -> bool - child = subcidr: cidr: - length subcidr > length cidr && contains (host 0 subcidr) cidr; - - # contains :: ip -> cidr -> bool - contains = ip: cidr: host 0 (make cidr.length ip) == host 0 cidr; - - # host :: (ip | mac | integer) -> cidr -> ip - host = index: cidr: let - index' = arithmetic.coerce cidr.base index; - in - arithmetic.or (arithmetic.shadow cidr.length index') cidr.base; - - # length :: cidr -> integer - length = cidr: cidr.length; - - # netmask :: cidr -> ip - netmask = cidr: arithmetic.coshadow cidr.length (arithmetic.coerce cidr.base (-1)); - - # size :: cidr -> integer - size = cidr: - ( - if cidr.base ? ipv6 - then 128 - else 32 - ) - - cidr.length; - - # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - subnet = length: index: cidr: let - length' = cidr.length + length; - index' = arithmetic.coerce cidr.base index; - size = - ( - if cidr.base ? ipv6 - then 128 - else 32 - ) - - length'; - in - make length' (host (arithmetic.left size index') cidr); - - # make :: integer -> ip -> cidr - make = length: base: let - length' = - math.clamp 0 ( - if base ? ipv6 - then 128 - else 32 - ) - length; - in { - base = arithmetic.coshadow length' base; - length = length'; - }; - }; - }; - - typechecks = let - fail = description: function: argument: - builtins.throw "${function}: ${argument} parameter must be ${description}"; - - meta = parser: description: function: argument: input: let - error = fail description function argument; - in - if !builtins.isString input - then error - else let - result = parser input; - in - if builtins.isNull result - then error - else result; - in { - int = function: argument: input: - if builtins.isInt input - then input - else fail "an integer" function argument; - ip = meta parsers.ip "an IPv4 or IPv6 address"; - cidr = meta parsers.cidr "an IPv4 or IPv6 address range in CIDR notation"; - mac = meta parsers.mac "a MAC address"; - numeric = function: argument: input: - if builtins.isInt input - then input - else meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input; - }; -in { - lib = { - inherit net; - }; -} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 7f5db63..453807f 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -1,5 +1,6 @@ { imports = [ ./hostopts.nix + ./restic.nix ]; } diff --git a/modules/nixos/hostopts.nix b/modules/nixos/hostopts.nix index a6aa2e3..5dd038e 100644 --- a/modules/nixos/hostopts.nix +++ b/modules/nixos/hostopts.nix @@ -5,7 +5,7 @@ ... }: { options = { - host = { + host.details = { user = lib.mkOption { type = lib.types.str; description = "Primary human user"; diff --git a/modules/nixos/restic.nix b/modules/nixos/restic.nix new file mode 100644 index 0000000..10ca9cd --- /dev/null +++ b/modules/nixos/restic.nix @@ -0,0 +1,148 @@ +{ + config, + pkgs, + lib, + ... +}: { + options = { + host.restic = { + enable = lib.mkEnableOption "enable restic"; + passwordFile = lib.mkOption { + type = lib.types.path; + description = "path to the file containing the restic repository password."; + }; + repositoryFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + description = "path to the file containing the restic repository url/path"; + default = null; + }; + repository = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "restic repository url/path"; + }; + server = { + enable = lib.mkEnableOption "enable restic server (must have nginx enabled and setup, and host.restic.passwordFile populated.)"; + repositoryPath = lib.mkOption { + type = lib.types.path; + description = "path of repository"; + }; + htpasswdPath = lib.mkOption { + type = lib.types.path; + description = "path to the repositories .htpasswd file"; + }; + domain = lib.mkOption { + type = lib.types.str; + description = "domain name to serve the restic server under. (for nginx virtualHosts)"; + }; + port = lib.mkOption { + type = lib.types.str; + description = "(internal) port to use between nginx and restic-server"; + }; + }; + backups = lib.mkOption { + description = "backups to create"; + default = {}; + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + paths = lib.mkOption { + type = lib.types.listOf lib.types.path; + description = "paths to back up."; + }; + preBackupCommands = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + description = "commands to run before the start of the backup."; + default = null; + }; + postBackupCommands = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + description = "commands to run after the backup is finished."; + default = null; + }; + }; + })); + }; + }; + }; + config = let + cfg = config.host.restic; + timer = { + OnCalendar = "daily"; + Persistent = true; + RandomizedDelaySec = "4h"; + }; + pruneOpts = [ + "--keep-within 14d" + "--keep-daily 14" + "--keep-weekly 8" + "--keep-monthly 12" + "--keep-yearly 10" + ]; + in { + environment.systemPackages = + lib.mkIf + (cfg.server.enable || cfg.enable) + (with pkgs; [ + restic + ]); + + services.restic.server = lib.mkIf cfg.server.enable { + enable = true; + appendOnly = true; + dataDir = cfg.server.repositoryPath; + listenAddress = "127.0.0.1:${cfg.server.port}"; + extraFlags = ["--htpasswd-file '${cfg.server.htpasswdPath}'"]; + }; + + services.nginx.virtualHosts = + lib.mkIf ( + cfg.server.enable + && (lib.asserts.assertMsg + (config.services.nginx.enable == true) + "NGINX must be enabled") + ) + { + "${cfg.server.domain}" = { + enableACME = lib.asserts.assertMsg ( + config.security.acme.acceptTerms + == true + && config.security.acme.defaults.email != null + ) "ACME must be setup"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:${cfg.server.port}"; + }; + }; + }; + + services.restic.backups = lib.mkMerge [ + (lib.mkIf cfg.server.enable { + prune = { + repository = cfg.server.repositoryPath; + passwordFile = cfg.passwordFile; + initialize = true; + runCheck = true; + paths = null; + timerConfig = timer; + pruneOpts = pruneOpts; + }; + }) + ( + lib.mkIf cfg.enable ( + lib.mapAttrs ( + name: backup: { + repositoryFile = cfg.repositoryFile; + repository = cfg.repository; + passwordFile = cfg.passwordFile; + timerConfig = timer; + backupPrepareCommand = backup.preBackupCommands; + backupCleanupCommand = backup.postBackupCommands; + paths = backup.paths; + } + ) + cfg.backups + ) + ) + ]; + }; +} diff --git a/packages/aarch64-iso.nix b/packages/aarch64-iso.nix index 0466fb0..39fef0f 100644 --- a/packages/aarch64-iso.nix +++ b/packages/aarch64-iso.nix @@ -1,12 +1,12 @@ # this ISO works best with tow-boot or another way of UEFI booting. { inputs, - configLib, + myLib, ... }: (inputs.nixpkgs.lib.nixosSystem { system = "aarch64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; # > Our main nixos configuration file < modules = [ inputs.home-manager.nixosModules.home-manager @@ -19,7 +19,6 @@ pkgs, lib, inputs, - configLib, modulesPath, options, ... @@ -38,26 +37,26 @@ "${modulesPath}/installer/scan/detected.nix" "${modulesPath}/installer/scan/not-detected.nix" ]; - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; gui.enable = true; }; networking.hostName = "nixos-installer"; # Define your hostname. - users.users.${config.host.user} = { + users.users.${config.host.details.user} = { packages = with pkgs; [ neovim gptfdisk ]; }; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { nvim.enable-lsp = false; git = { diff --git a/packages/iso.nix b/packages/iso.nix index d3a9183..be1a522 100644 --- a/packages/iso.nix +++ b/packages/iso.nix @@ -1,11 +1,11 @@ { inputs, - configLib, + myLib, ... }: (inputs.nixpkgs.lib.nixosSystem { system = "x86_64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; # > Our main nixos configuration file < modules = [ inputs.home-manager.nixosModules.home-manager @@ -22,7 +22,6 @@ pkgs, lib, inputs, - configLib, modulesPath, options, ... @@ -36,26 +35,26 @@ "${modulesPath}/installer/scan/detected.nix" "${modulesPath}/installer/scan/not-detected.nix" ]; - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; gui.enable = true; }; networking.hostName = "nixos-installer"; # Define your hostname. - users.users.${config.host.user} = { + users.users.${config.host.details.user} = { packages = with pkgs; [ firefox gptfdisk ]; }; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { nvim.enable-lsp = false; git = { diff --git a/packages/proxmox.nix b/packages/proxmox.nix index d7ac9ff..2865d80 100644 --- a/packages/proxmox.nix +++ b/packages/proxmox.nix @@ -1,11 +1,11 @@ { inputs, - configLib, + myLib, ... }: (inputs.nixpkgs.lib.nixosSystem { system = "x86_64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; modules = [ inputs.home-manager.nixosModules.home-manager ../configs/nixos/sshd.nix @@ -13,7 +13,6 @@ ({ config, pkgs, - configLib, modulesPath, lib, ... @@ -22,19 +21,21 @@ proxmoxLXC.manageHostName = false; boot.loader.grub.enable = lib.mkForce false; boot.loader.systemd-boot.enable = lib.mkForce false; - host.user = "gabe"; - host.fullName = "Gabe Venberg"; + host.details = { + user = "gabe"; + fullName = "Gabe Venberg"; + }; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/packages/rpi3-sd-image.nix b/packages/rpi3-sd-image.nix index 52c7be4..d153659 100644 --- a/packages/rpi3-sd-image.nix +++ b/packages/rpi3-sd-image.nix @@ -1,11 +1,11 @@ { inputs, - configLib, + myLib, ... }: (inputs.nixpkgs.lib.nixosSystem { system = "aarch64-linux"; - specialArgs = {inherit inputs configLib;}; + specialArgs = {inherit inputs myLib;}; modules = [ inputs.home-manager.nixosModules.home-manager inputs.disko.nixosModules.disko @@ -20,13 +20,12 @@ config, pkgs, lib, - configLib, modulesPath, ... }: { imports = ["${modulesPath}/installer/sd-card/sd-image-aarch64.nix"]; hardware.enableRedistributableFirmware = true; - host = { + host.details = { user = "gabe"; fullName = "Gabe Venberg"; }; @@ -55,17 +54,17 @@ # home-manager.sharedModules = [ # inputs.sops-nix.homeManagerModules.sops # ]; - home-manager.users.${config.host.user} = { + home-manager.users.${config.host.details.user} = { inputs, osConfig, lib, ... }: { - host = osConfig.host; + host.details = osConfig.host.details; user = { git = { profile = { - name = config.host.fullName; + name = config.host.details.fullName; email = "gabevenberg@gmail.com"; }; workProfile.enable = false; diff --git a/roles/nixos/graphical-vm.nix b/roles/nixos/graphical-vm.nix index 8c3d5e9..5fcb0a8 100644 --- a/roles/nixos/graphical-vm.nix +++ b/roles/nixos/graphical-vm.nix @@ -10,5 +10,5 @@ imports = [ ./vm.nix ]; - host.gui.enable = true; + host.details.gui.enable = true; } diff --git a/roles/nixos/vm.nix b/roles/nixos/vm.nix index 6d8015c..8ebd390 100644 --- a/roles/nixos/vm.nix +++ b/roles/nixos/vm.nix @@ -6,5 +6,5 @@ ... }: { services.qemuGuest.enable = true; - host.isVm = true; + host.details.isVm = true; }