From 4f6d25160fc4bda6dbd98a6f896e6902854d4a7d Mon Sep 17 00:00:00 2001 From: Qubasa Date: Tue, 23 Apr 2024 18:55:00 +0200 Subject: [PATCH] Add --ssh-pubkey FILE argument --- checks/flash/flake-module.nix | 2 +- clanModules/disk-layouts/default.nix | 1 + flake.lock | 40 ++++++++++- flake.nix | 5 +- nixosModules/installer/default.nix | 4 +- nixosModules/iso/default.nix | 1 + pkgs/clan-cli/clan_cli/__init__.py | 1 + pkgs/clan-cli/clan_cli/flash.py | 100 ++++++++++++++++++++++++--- pkgs/clan-cli/clan_cli/nix.py | 8 +-- pkgs/clan-cli/default.nix | 9 ++- pkgs/clan-cli/flake-module.nix | 1 + pkgs/clan-cli/shell.nix | 3 + pkgs/clan-vm-manager/shell.nix | 2 + pkgs/flake-module.nix | 1 + pkgs/installer/flake-module.nix | 45 +++++++++++- 15 files changed, 192 insertions(+), 31 deletions(-) diff --git a/checks/flash/flake-module.nix b/checks/flash/flake-module.nix index f43c4357..be293a68 100644 --- a/checks/flash/flake-module.nix +++ b/checks/flash/flake-module.nix @@ -41,7 +41,7 @@ }; testScript = '' start_all() - machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine") + machine.succeed("clan --debug --flake ${../..} flash --yes --disk main /dev/vdb test_install_machine") ''; } { inherit pkgs self; }; }; diff --git a/clanModules/disk-layouts/default.nix b/clanModules/disk-layouts/default.nix index 78f2295a..b2e5f130 100644 --- a/clanModules/disk-layouts/default.nix +++ b/clanModules/disk-layouts/default.nix @@ -20,6 +20,7 @@ boot = { size = "1M"; type = "EF02"; # for grub MBR + priority = 1; }; ESP = { size = "512M"; diff --git a/flake.lock b/flake.lock index 73a064f0..f69703bc 100644 --- a/flake.lock +++ b/flake.lock @@ -15,7 +15,7 @@ "type": "github" }, "original": { - "owner": "nix-community", + "owner": "Qubasa", "repo": "disko", "type": "github" } @@ -55,6 +55,22 @@ "type": "github" } }, + "nixos-2311": { + "locked": { + "lastModified": 1713397591, + "narHash": "sha256-1P6Plf9a9KwgERtuijPpET/s4AwIZUYqIu1nuVJqPPU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "392320f29b07e74131d4e4a7b435e8e9b9b85adf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nixos-generators": { "inputs": { "nixlib": "nixlib", @@ -76,6 +92,27 @@ "type": "github" } }, + "nixos-images": { + "inputs": { + "nixos-2311": "nixos-2311", + "nixos-unstable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1713523007, + "narHash": "sha256-kEnwogkcBn0omgIsGo3zbfAP9nJTDUhp+Q9QWXxsUd0=", + "owner": "nix-community", + "repo": "nixos-images", + "rev": "f064936faf1d12452212030c38e8c325f5b4dfe5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixos-images", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1714290118, @@ -97,6 +134,7 @@ "disko": "disko", "flake-parts": "flake-parts", "nixos-generators": "nixos-generators", + "nixos-images": "nixos-images", "nixpkgs": "nixpkgs", "sops-nix": "sops-nix", "treefmt-nix": "treefmt-nix" diff --git a/flake.nix b/flake.nix index e81b3d21..3b289028 100644 --- a/flake.nix +++ b/flake.nix @@ -8,14 +8,15 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small"; - - disko.url = "github:nix-community/disko"; + disko.url = "github:Qubasa/disko"; disko.inputs.nixpkgs.follows = "nixpkgs"; sops-nix.url = "github:Mic92/sops-nix"; sops-nix.inputs.nixpkgs.follows = "nixpkgs"; sops-nix.inputs.nixpkgs-stable.follows = ""; nixos-generators.url = "github:nix-community/nixos-generators"; nixos-generators.inputs.nixpkgs.follows = "nixpkgs"; + nixos-images.url = "github:nix-community/nixos-images"; + nixos-images.inputs.nixos-unstable.follows = "nixpkgs"; flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; treefmt-nix.url = "github:numtide/treefmt-nix"; diff --git a/nixosModules/installer/default.nix b/nixosModules/installer/default.nix index acb2784a..5dab8a96 100644 --- a/nixosModules/installer/default.nix +++ b/nixosModules/installer/default.nix @@ -16,7 +16,7 @@ (modulesPath + "/profiles/installation-device.nix") (modulesPath + "/profiles/all-hardware.nix") (modulesPath + "/profiles/base.nix") - (modulesPath + "/installer/cd-dvd/iso-image.nix") + #(modulesPath + "/installer/cd-dvd/iso-image.nix") ]; services.openssh.settings.PermitRootLogin = "yes"; system.activationScripts.root-password = '' @@ -65,5 +65,5 @@ cat /var/shared/qrcode.utf8 fi ''; - isoImage.squashfsCompression = "zstd"; + #isoImage.squashfsCompression = "zstd"; } diff --git a/nixosModules/iso/default.nix b/nixosModules/iso/default.nix index db3afe56..c3f0d132 100644 --- a/nixosModules/iso/default.nix +++ b/nixosModules/iso/default.nix @@ -43,6 +43,7 @@ let boot = { size = "1M"; type = "EF02"; # for grub MBR + priority = 1; # Needs to be first partition }; ESP = { size = "100M"; diff --git a/pkgs/clan-cli/clan_cli/__init__.py b/pkgs/clan-cli/clan_cli/__init__.py index 51674ccb..13daeb3d 100644 --- a/pkgs/clan-cli/clan_cli/__init__.py +++ b/pkgs/clan-cli/clan_cli/__init__.py @@ -58,6 +58,7 @@ def create_parser(prog: str | None = None) -> argparse.ArgumentParser: "--debug", help="Enable debug logging", action="store_true", + default=False, ) parser.add_argument( diff --git a/pkgs/clan-cli/clan_cli/flash.py b/pkgs/clan-cli/clan_cli/flash.py index a03c1af5..8b8ab224 100644 --- a/pkgs/clan-cli/clan_cli/flash.py +++ b/pkgs/clan-cli/clan_cli/flash.py @@ -1,8 +1,8 @@ import argparse import importlib +import json import logging import os -import shlex import shutil import textwrap from collections.abc import Sequence @@ -20,8 +20,48 @@ from .nix import nix_shell log = logging.getLogger(__name__) +def list_available_ssh_keys(ssh_dir: Path = Path("~/.ssh").expanduser()) -> list[Path]: + """ + Function to list all available SSH public keys in the default .ssh directory. + Returns a list of paths to available public key files. + """ + public_key_patterns = ["*.pub"] + available_keys: list[Path] = [] + + # Check for public key files + for pattern in public_key_patterns: + for key_path in ssh_dir.glob(pattern): + if key_path.is_file(): + available_keys.append(key_path) + + return available_keys + + +def read_public_key_contents(public_keys: list[Path]) -> list[str]: + """ + Function to read and return the contents of available SSH public keys. + Returns a list containing the contents of each public key. + """ + public_key_contents = [] + + for key_path in public_keys: + try: + with open(key_path.expanduser()) as key_file: + public_key_contents.append(key_file.read().strip()) + except FileNotFoundError: + log.error(f"Public key file not found: {key_path}") + + return public_key_contents + + def flash_machine( - machine: Machine, mode: str, disks: dict[str, str], dry_run: bool, debug: bool + machine: Machine, + *, + mode: str, + disks: dict[str, str], + system_config: dict[str, Any], + dry_run: bool, + debug: bool, ) -> None: secret_facts_module = importlib.import_module(machine.secret_facts_module) secret_facts_store: SecretStoreBase = secret_facts_module.SecretStore( @@ -58,12 +98,17 @@ def flash_machine( disko_install.extend(["--extra-files", str(local_dir), upload_dir]) disko_install.extend(["--flake", str(machine.flake) + "#" + machine.name]) disko_install.extend(["--mode", str(mode)]) + disko_install.extend( + [ + "--system-config", + json.dumps(system_config), + ] + ) cmd = nix_shell( ["nixpkgs#disko"], disko_install, ) - print("$", " ".join(map(shlex.quote, cmd))) run(cmd, log=Log.BOTH, error_msg=f"Failed to flash {machine}") @@ -72,6 +117,7 @@ class FlashOptions: flake: Path machine: str disks: dict[str, str] + ssh_keys_path: list[Path] dry_run: bool confirm: bool debug: bool @@ -99,11 +145,13 @@ def flash_command(args: argparse.Namespace) -> None: flake=args.flake, machine=args.machine, disks=args.disk, + ssh_keys_path=args.ssh_pubkey, dry_run=args.dry_run, confirm=not args.yes, debug=args.debug, mode=args.mode, ) + machine = Machine(opts.machine, flake=opts.flake) if opts.confirm and not opts.dry_run: disk_str = ", ".join(f"{name}={device}" for name, device in opts.disks.items()) @@ -114,8 +162,38 @@ def flash_command(args: argparse.Namespace) -> None: ask = input(msg) if ask != "y": return + + root_keys = read_public_key_contents(opts.ssh_keys_path) + if opts.confirm and not root_keys: + msg = "Should we add your SSH public keys to the root user? [y/N] " + ask = input(msg) + if ask == "y": + pubkeys = list_available_ssh_keys() + root_keys.extend(read_public_key_contents(pubkeys)) + elif not opts.confirm and not root_keys: + pubkeys = list_available_ssh_keys() + root_keys.extend(read_public_key_contents(pubkeys)) + # If ssh-pubkeys set, we don't need to ask for confirmation + elif opts.confirm and root_keys: + pass + elif not opts.confirm and root_keys: + pass + else: + raise ClanError("Invalid state") + + user_keys = { + "users": { + "users": {"root": {"openssh": {"authorizedKeys": {"keys": root_keys}}}} + } + } + flash_machine( - machine, opts.mode, disks=opts.disks, dry_run=opts.dry_run, debug=opts.debug + machine, + mode=opts.mode, + disks=opts.disks, + system_config=user_keys, + dry_run=opts.dry_run, + debug=opts.debug, ) @@ -147,7 +225,13 @@ def register_parser(parser: argparse.ArgumentParser) -> None: choices=["format", "mount"], default="format", ) - + parser.add_argument( + "--ssh-pubkey", + type=Path, + action="append", + default=[], + help="ssh pubkey file to add to the root user. Can be used multiple times", + ) parser.add_argument( "--yes", action="store_true", @@ -160,10 +244,4 @@ def register_parser(parser: argparse.ArgumentParser) -> None: default=False, action="store_true", ) - parser.add_argument( - "--debug", - help="Print debug information", - default=False, - action="store_true", - ) parser.set_defaults(func=flash_command) diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index e922fd5e..84de3520 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -106,13 +106,7 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: if os.environ.get("IN_NIX_SANDBOX"): return cmd return [ - *nix_command( - [ - "shell", - "--inputs-from", - f"{nixpkgs_flake()!s}", - ] - ), + *nix_command(["shell", "--offline", "--inputs-from", f"{nixpkgs_flake()!s}"]), *packages, "-c", *cmd, diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index c2452160..47c3f63e 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -23,11 +23,11 @@ zbar, tor, git, - nixpkgs, qemu, gnupg, e2fsprogs, mypy, + nixpkgs, clan-core-path, }: let @@ -95,17 +95,16 @@ let description = "dependencies for the clan-cli"; inputs = { - nixpkgs.url = "nixpkgs"; + nixpkgs.url = "path://${nixpkgs}"; }; outputs = _inputs: { }; } EOF ln -s ${nixpkgs} $out/path - nix flake lock $out \ + nix flake update $out \ --store ./. \ - --extra-experimental-features 'nix-command flakes' \ - --override-input nixpkgs ${nixpkgs} + --extra-experimental-features 'nix-command flakes' ''; in python3.pkgs.buildPythonApplication { diff --git a/pkgs/clan-cli/flake-module.nix b/pkgs/clan-cli/flake-module.nix index 317990c7..2e4969b6 100644 --- a/pkgs/clan-cli/flake-module.nix +++ b/pkgs/clan-cli/flake-module.nix @@ -38,6 +38,7 @@ ''; in { + devShells.clan-cli = pkgs.callPackage ./shell.nix { inherit (self'.packages) clan-cli; }; packages = { clan-cli = pkgs.python3.pkgs.callPackage ./default.nix { diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index 1daef3e6..6d7f6372 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -12,6 +12,7 @@ let rope setuptools wheel + ipdb pip ]); in @@ -21,6 +22,8 @@ mkShell { ruff ] ++ devshellTestDeps; + PYTHONBREAKPOINT = "ipdb.set_trace"; + shellHook = '' export GIT_ROOT="$(git rev-parse --show-toplevel)" export PKG_ROOT="$GIT_ROOT/pkgs/clan-cli" diff --git a/pkgs/clan-vm-manager/shell.nix b/pkgs/clan-vm-manager/shell.nix index 758b06eb..a7214007 100644 --- a/pkgs/clan-vm-manager/shell.nix +++ b/pkgs/clan-vm-manager/shell.nix @@ -40,6 +40,8 @@ mkShell { desktop-file-utils # verify desktop files ]); + PYTHONBREAKPOINT = "ipdb.set_trace"; + shellHook = '' export GIT_ROOT=$(git rev-parse --show-toplevel) export PKG_ROOT=$GIT_ROOT/pkgs/clan-vm-manager diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix index bea7c2dd..b3dbdb14 100644 --- a/pkgs/flake-module.nix +++ b/pkgs/flake-module.nix @@ -1,4 +1,5 @@ { ... }: + { imports = [ ./clan-cli/flake-module.nix diff --git a/pkgs/installer/flake-module.nix b/pkgs/installer/flake-module.nix index 69f32e90..6889060b 100644 --- a/pkgs/installer/flake-module.nix +++ b/pkgs/installer/flake-module.nix @@ -1,12 +1,22 @@ { self, lib, ... }: let installerModule = - { config, ... }: + { + config, + pkgs, + modulesPath, + ... + }: { imports = [ self.nixosModules.installer self.inputs.nixos-generators.nixosModules.all-formats + self.inputs.disko.nixosModules.disko + (modulesPath + "/installer/cd-dvd/iso-image.nix") ]; + + isoImage.squashfsCompression = "zstd"; + # Provide convenience for connecting to wifi networking.wireless.enable = false; @@ -34,9 +44,35 @@ let { disko.memSize = 4096; } # FIXME: otherwise the image builder goes OOM ]; }; + + flashInstallerModule = + { config, pkgs, ... }: + { + imports = [ + self.nixosModules.installer + self.clanModules.diskLayouts + ]; + # Provide convenience for connecting to wifi + networking.wireless.enable = false; + + # Use iwd instead of wpa_supplicant. It has a user friendly CLI + networking.wireless.iwd = { + settings = { + Network = { + EnableIPv6 = true; + RoutePriorityOffset = 300; + }; + Settings = { + AutoConnect = true; + }; + }; + enable = true; + }; + system.stateVersion = config.system.nixos.version; + nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux; + }; in { - clan = { clanName = "clan-core"; directory = self; @@ -44,6 +80,11 @@ in imports = [ installerModule ]; fileSystems."/".device = lib.mkDefault "/dev/null"; }; + machines.flash-installer = { + imports = [ flashInstallerModule ]; + clan.diskLayouts.singleDiskExt4.device = "/dev/sda"; + boot.loader.grub.enable = lib.mkForce true; + }; }; flake.packages.x86_64-linux.install-iso = installer.config.formats.iso; flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath;