# SPDX-FileCopyrightText: 2021-2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later { description = "NixOS system configuration"; inputs = { flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager.url = "github:nix-community/home-manager/release-24.11"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; home-manager-unstable.url = "github:nix-community/home-manager"; home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable"; krops.url = "github:Mic92/krops"; krops.inputs.flake-utils.follows = "flake-utils"; krops.inputs.nixpkgs.follows = "nixpkgs"; nixos-hardware.url = "github:nixos/nixos-hardware/master"; nix-pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix/master"; nix-pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs-unstable"; sops-nix.url = "github:Mic92/sops-nix"; sops-nix.inputs.nixpkgs.follows = "nixpkgs"; infinisilSystem.url = "github:Infinisil/system/9f7cf7497d26d57996edc7f70edcf7d2cccfdf60"; infinisilSystem.flake = false; nixpkgs-overlay.url = "git+https://git.sbruder.de/simon/nixpkgs-overlay"; nixpkgs-overlay.inputs.flake-utils.follows = "flake-utils"; nixpkgs-overlay.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs-overlay.inputs.nix-pre-commit-hooks.follows = "nix-pre-commit-hooks"; bang-evaluator.url = "git+https://git.sbruder.de/simon/bangs"; bang-evaluator.inputs.flake-utils.follows = "flake-utils"; bang-evaluator.inputs.nixpkgs.follows = "nixpkgs"; password-hash-self-service.url = "git+https://git.sbruder.de/simon/password-hash-self-service"; password-hash-self-service.inputs.flake-utils.follows = "flake-utils"; }; outputs = { self , flake-utils , krops , nix-pre-commit-hooks , nixpkgs , nixpkgs-overlay , ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; inherit (pkgs) lib; in { checks = { pre-commit-check = nix-pre-commit-hooks.lib.${system}.run { src = self; hooks = { black.enable = true; nixpkgs-fmt.enable = true; shellcheck.enable = true; reuse = { enable = true; name = "reuse"; entry = "reuse lint"; pass_filenames = false; }; }; }; }; apps = lib.mapAttrs (name: program: { type = "app"; program = toString program; }) (flake-utils.lib.flattenTree { deploy = lib.recurseIntoAttrs (lib.mapAttrs (hostname: machine: let inherit (krops.packages.${system}) writeCommand; inherit (krops) lib; in writeCommand "deploy-${hostname}" { target = lib.mkTarget "root@${machine.config.deployment.targetHost}" // { extraOptions = [ # force allocation of tty to allow aborting with ^C and to show build progress "-t" ]; }; source = lib.evalSource (lib.singleton { config.file = { path = toString self; useChecksum = true; filters = [ { type = "include"; pattern = "/machines/${hostname}/"; } { type = "exclude"; pattern = "/machines/*/"; } ]; }; }); command = targetPath: '' nixos-rebuild switch --flake ${targetPath}/config -L --keep-going ''; } ) self.nixosConfigurations); deploy-local = lib.recurseIntoAttrs (lib.mapAttrs (hostname: machine: pkgs.writeShellScript "deploy-local-${hostname}" '' set -euo pipefail SUBSTITUTE="-s" for i in "$@"; do case $i in -S) SUBSTITUTE="" ;; esac done closure="$(nix build -L --print-out-paths .#nixosConfigurations.${hostname}.config.system.build.toplevel)" nix copy \ $SUBSTITUTE \ -L \ --to ssh://root@${machine.config.deployment.targetHost} \ "$closure" ssh root@${machine.config.deployment.targetHost} nix-env -p /nix/var/nix/profiles/system --set ''${closure} ssh root@${machine.config.deployment.targetHost} ''${closure}/bin/switch-to-configuration switch return="$?" if [ "$return" != 0 ]; then echo "Error(s) occurred while Switching configuration" exit "$retuen" fi '') self.nixosConfigurations); unlock = lib.recurseIntoAttrs (lib.mapAttrs (hostname: machine: let inherit (machine.config.deployment) targetHost unlockOverV4; in pkgs.writeShellScript "unlock-${hostname}" '' set -exo pipefail # opening luks fails if gpg-agent is not unlocked yet pass "devices/${hostname}/luks" | ssh \ ${lib.optionalString unlockOverV4 "-4"} \ -p 2222 \ "root@${targetHost}" \ "cat > /crypt-ramfs/passphrase" '') self.nixosConfigurations); showKeyFingerprint = pkgs.writeShellScript "show-key-fingerprint" '' gpg --with-fingerprint --with-colons --show-key "keys/''${1}.asc" | awk -F: '$1 == "fpr" { print $10; exit }' ''; }); packages = { kexec-bundle = (nixpkgs.lib.nixosSystem { inherit system; modules = [ ./modules/pubkeys.nix ./modules/ssh.nix ({ modulesPath, ... }: { imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ]; }) ]; }).config.system.build.kexecTree; }; devShells.default = pkgs.mkShell { buildInputs = (with pkgs; [ black nixpkgs-fmt reuse shellcheck sops ssh-to-pgp ]); shellHook = '' find ${./keys} -type f -print0 | xargs -0 ${pkgs.gnupg}/bin/gpg --quiet --import '' + self.checks.${system}.pre-commit-check.shellHook; }; }) // { overlays.default = import ./pkgs; nixosConfigurations = nixpkgs.lib.mapAttrs (hostname: { system , extraModules ? [ ] , targetHost ? hostname , unlockOverV4 ? true , nixpkgs ? inputs.nixpkgs }: nixpkgs.lib.nixosSystem rec { inherit system; modules = [ (./machines + "/${hostname}/configuration.nix") { _module.args.inputs = inputs; } # deployment settings ({ lib, ... }: { options.deployment = { targetHost = lib.mkOption { type = lib.types.str; readOnly = true; internal = true; }; unlockOverV4 = lib.mkOption { type = lib.types.bool; readOnly = true; internal = true; description = "Whether to unlock the host over IPv4 (only)"; }; }; config.deployment = { inherit targetHost unlockOverV4; }; }) ] ++ (with inputs; [ # Use newest home-manager for hosts that use unstable, otherwise there are conflicts (if nixpkgs == inputs.nixpkgs-unstable then home-manager-unstable else home-manager).nixosModules.home-manager sops-nix.nixosModules.sops bang-evaluator.nixosModules.bang-evaluator ]) ++ (builtins.attrValues nixpkgs-overlay.nixosModules) ++ extraModules; }) (import ./machines inputs); }; }