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