ssh-flash & documentation #1350
@ -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; };
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
boot = {
|
||||
size = "1M";
|
||||
type = "EF02"; # for grub MBR
|
||||
priority = 1;
|
||||
};
|
||||
ESP = {
|
||||
size = "512M";
|
||||
|
40
flake.lock
40
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"
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ let
|
||||
boot = {
|
||||
size = "1M";
|
||||
type = "EF02"; # for grub MBR
|
||||
priority = 1; # Needs to be first partition
|
||||
};
|
||||
ESP = {
|
||||
size = "100M";
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./clan-cli/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;
|
||||
|
Loading…
Reference in New Issue
Block a user