{ description = "nixos-router: a NixOS-based distribution for routers/firewalls"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; outputs = { self, nixpkgs }: let systems = [ "x86_64-linux" "aarch64-linux" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); in { nixosModules = { router = ./modules/router; default = self.nixosModules.router; # Disk layout contract shared by router-install and target configs. installed-layout = ./modules/installer/layout.nix; # The installer pieces; `lib.mkInstaller` wires them up. installer = ./modules/installer/install-script.nix; installer-iso = ./modules/installer/iso.nix; # Appliance disk image: dm-verity store, volatile root, state on /var. image = ./modules/image; # Configuration from a settings file (JSON), rebuilt on the device. settings = ./modules/settings; }; # A router built from settings.json; this is what the device's own # configuration flake (see mkConfigDir) evaluates. lib.mkRouterSystem = { settings, modules ? [ ], }: nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ self.nixosModules.router self.nixosModules.settings self.nixosModules.installed-layout { routerSettings.file = settings; # The device rebuilds itself, offline if need be: it needs the # sources its configuration flake points to. system.extraDependencies = [ self.outPath nixpkgs.outPath ]; } ] ++ modules; }; # The configuration directory installed to /var/lib/router/config: a # flake pinned to exactly this router source, the settings and an # optional local.nix with extra NixOS configuration. lib.mkConfigDir = { pkgs, settings, local ? null, }: pkgs.runCommand "router-config" { } '' mkdir -p $out cat > $out/flake.nix <<'EOF' { inputs.nixos-router.url = "path:${self.outPath}"; outputs = { nixos-router, ... }: { nixosConfigurations.router = nixos-router.lib.mkRouterSystem { settings = ./settings.json; modules = if builtins.pathExists ./local.nix then [ ./local.nix ] else [ ]; }; }; } EOF cp ${settings} $out/settings.json ${pkgs.lib.optionalString (local != null) "cp ${local} $out/local.nix"} ''; # Installer ISO for a given nixosSystem: # nix build .#installer-example (see packages) lib.mkInstaller = { configuration }: nixpkgs.lib.nixosSystem { inherit (configuration.pkgs.stdenv.hostPlatform) system; modules = [ self.nixosModules.installer-iso { router.installer.system = configuration.config.system.build.toplevel; # Reach the live system with the keys that will reach the router. users.users.root.openssh.authorizedKeys.keys = configuration.config.users.users.root.openssh.authorizedKeys.keys; } ]; }; packages.x86_64-linux.installer-home = (self.lib.mkInstaller { configuration = self.nixosConfigurations.home; }) .config.system.build.isoImage; packages.x86_64-linux.installer-example = (self.lib.mkInstaller { configuration = self.nixosConfigurations.example; }) .config.system.build.isoImage; # A deployable starting point; copy examples/desktop and adapt it. nixosConfigurations.example = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ self.nixosModules.router ./examples/desktop ]; }; # The home gateway replacing antigua's router role (see examples/home). nixosConfigurations.home = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ self.nixosModules.router ./examples/home ]; }; # Appliance image (verity store, volatile root) for trying on hardware: # nix build .#image -> result/*.raw nixosConfigurations.image = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ self.nixosModules.router self.nixosModules.image ./examples/image ]; }; packages.x86_64-linux.image = self.nixosConfigurations.image.config.system.build.image; # `nix run .#deploy -- `: activate a new # configuration with commit-confirm; see scripts/deploy.sh. apps.x86_64-linux.deploy = let pkgs = nixpkgs.legacyPackages.x86_64-linux; deploy = pkgs.writeShellApplication { name = "router-deploy"; runtimeInputs = with pkgs; [ nixos-rebuild openssh coreutils ]; text = builtins.readFile ./scripts/deploy.sh; }; in { type = "app"; program = "${deploy}/bin/router-deploy"; }; # VM tests need KVM; only wired up for x86_64 until aarch64 CI exists. checks.x86_64-linux = import ./tests { inherit self; pkgs = nixpkgs.legacyPackages.x86_64-linux; } // { # The example must keep building as a complete system. example = self.nixosConfigurations.example.config.system.build.toplevel; home = self.nixosConfigurations.home.config.system.build.toplevel; }; # `nix run .#lab`: boot the basic test topology in tmux, one console pane # per VM plus the interactive test-driver REPL. apps.x86_64-linux.lab = let pkgs = nixpkgs.legacyPackages.x86_64-linux; test = self.checks.x86_64-linux.basic; lab = pkgs.writeShellApplication { name = "router-lab"; runtimeInputs = with pkgs; [ tmux util-linux gnugrep coreutils ]; text = import ./tests/lab.nix { inherit (pkgs) lib; driver = test.driverInteractive; nodes = builtins.attrNames test.nodes; }; }; in { type = "app"; program = "${lab}/bin/router-lab"; }; formatter = forAllSystems (pkgs: pkgs.nixfmt); }; }