feat(foreign-port): add WiFi-only laptop as clan machine ✨
Mirrors the distant-shore pattern: clan-managed (no standalone flake-module), wired into zerotier/data-mesher/dm-pull-deploy with the generated vars. WiFi via NetworkManager (PSK from /etc/secrets/nm.env); locally-signed boot chain (shim chain-loads sbsign-signed systemd-boot + kernel, refreshed every nixos-rebuild). targetHost is the LAN IP for the first push, switch to ZT IPv6 once on the mesh. buildHost = sunken-ship to avoid self-SSH on the closure copy.
This commit is contained in:
parent
610454f0d2
commit
e2cf93e7d6
23 changed files with 310 additions and 0 deletions
111
nixos/hosts/foreign-port.nix
Normal file
111
nixos/hosts/foreign-port.nix
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# NixOS laptop server. WiFi-only, headless, unattended auto-rebuild via
|
||||
# clan dm-pull-deploy. No LUKS (mirrors sunken-ship) so reboots don't
|
||||
# block on a passphrase.
|
||||
#
|
||||
# Blank-slate server for now — no application services. Give it a purpose
|
||||
# later (just add services here and let dm-pull-deploy roll it out).
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./foreign-port-hardware.nix
|
||||
../disko-foreign-port.nix
|
||||
];
|
||||
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
# Firmware-locked Secure Boot: we can't enrol our own keys into the
|
||||
# firmware key DB, so a vendor-signed shim is the firmware-booted binary
|
||||
# and chain-loads a locally-signed systemd-boot + kernel. The NVRAM
|
||||
# entry points at shim; bootctl is kept away from EFI variables so
|
||||
# rebuilds don't clobber the entry.
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
boot.loader.efi.efiSysMountPoint = "/boot"; # matches disko ESP mountpoint
|
||||
|
||||
# --- Locally-signed boot chain --------------------------------------------
|
||||
# On every bootloader install: re-sign systemd-boot and every kernel
|
||||
# image, refresh the shim binary on the ESP, and place the helper binary
|
||||
# beside it. Re-runs on each nixos-rebuild so auto-deployed generations
|
||||
# stay bootable. Signing material lives in /etc/secrets, never the repo.
|
||||
boot.loader.systemd-boot.extraInstallCommands = ''
|
||||
# NixOS's bootloader-install systemd unit runs with a minimal PATH that
|
||||
# doesn't include coreutils, so use absolute paths for cp/mv.
|
||||
KEY=/etc/secrets/MOK.key
|
||||
CRT=/etc/secrets/MOK.crt
|
||||
sb() { ${pkgs.sbsigntool}/bin/sbsign --key "$KEY" --cert "$CRT" --output "$2" "$1"; }
|
||||
# systemd-boot -> shim's chain-load target
|
||||
sb /boot/EFI/systemd/systemd-bootx64.efi /boot/EFI/BOOT/grubx64.efi
|
||||
# shim is the firmware-booted binary; helper binary sits beside it
|
||||
${pkgs.coreutils}/bin/cp -f /etc/secrets/shimx64.efi /boot/EFI/BOOT/BOOTX64.EFI
|
||||
${pkgs.coreutils}/bin/cp -f /etc/secrets/mmx64.efi /boot/EFI/BOOT/mmx64.efi
|
||||
# sign each kernel (skip if already signed; leave initrds untouched)
|
||||
for k in /boot/EFI/nixos/*bzImage.efi; do
|
||||
[ -e "$k" ] || continue
|
||||
if ! ${pkgs.sbsigntool}/bin/sbverify --cert "$CRT" "$k" >/dev/null 2>&1; then
|
||||
${pkgs.sbsigntool}/bin/sbsign --key "$KEY" --cert "$CRT" --output "$k.tmp" "$k" \
|
||||
&& ${pkgs.coreutils}/bin/mv -f "$k.tmp" "$k"
|
||||
fi
|
||||
done
|
||||
'';
|
||||
|
||||
networking.hostName = "foreign-port";
|
||||
# WiFi via NetworkManager. The wpa_supplicant stack hit two issues on this
|
||||
# box: (1) it strips CAP_CHOWN so wpa couldn't create its ctrl_interface,
|
||||
# and (2) dhcpcd didn't grab a lease after the (late) association at boot,
|
||||
# needing a manual restart — fatal for an unattended headless server. NM
|
||||
# handles association + DHCP atomically and connected first-try here.
|
||||
# The PSK stays out of the repo: it's substituted from /etc/secrets/nm.env
|
||||
# ($PSK_INTENO) into the declared profile at activation.
|
||||
networking.networkmanager.enable = true;
|
||||
networking.networkmanager.ensureProfiles.environmentFiles = [ "/etc/secrets/nm.env" ];
|
||||
networking.networkmanager.ensureProfiles.profiles."Inteno-89FE-5GHz" = {
|
||||
connection = { id = "Inteno-89FE-5GHz"; type = "wifi"; autoconnect = true; };
|
||||
wifi = { ssid = "Inteno-89FE-5GHz"; mode = "infrastructure"; };
|
||||
wifi-security = { key-mgmt = "wpa-psk"; psk = "$PSK_INTENO"; };
|
||||
ipv4.method = "auto";
|
||||
ipv6.method = "auto";
|
||||
};
|
||||
hardware.enableRedistributableFirmware = true; # WiFi firmware blobs
|
||||
time.timeZone = "Europe/Copenhagen";
|
||||
|
||||
# It's a laptop acting as a server: keep running with the lid shut.
|
||||
services.logind.settings.Login.HandleLidSwitch = "ignore";
|
||||
services.logind.settings.Login.HandleLidSwitchExternalPower = "ignore";
|
||||
|
||||
# Reduce screen burn-in / power: blank the TTY after a minute.
|
||||
boot.kernelParams = [ "consoleblank=60" ];
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
programs.nix-ld.enable = true; # run dynamically linked binaries (e.g. Claude Code remote CLI)
|
||||
nix.settings.trusted-users = [ "root" "danny" ];
|
||||
system.stateVersion = "25.11";
|
||||
|
||||
users.users.danny = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" "video" "audio" ];
|
||||
openssh.authorizedKeys.keys = [
|
||||
# Mac admin / fleet key (~/.ssh/id_ed25519_sunken_ship) — the key the
|
||||
# Mac uses to reach the fleet; clan machines update relies on it.
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKW/akfIiVU5o63YrTAJVZhMj7kXfYHOnXDtlpVFW7pf danny@mac-admin"
|
||||
# TODO: add a per-host key (~/.ssh/id_ed25519_foreign_port) for
|
||||
# plain `ssh foreign-port`. Generate when convenient.
|
||||
# sunken-ship (dm-pull-deploy push node) — reach foreign-port over ZT.
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB9t4YAaoHvVouqp+qyFOq8o3SAtXMiAmjF6J0ldyx4g danny@sunken-ship"
|
||||
];
|
||||
};
|
||||
users.users.root.openssh.authorizedKeys.keys =
|
||||
config.users.users.danny.openssh.authorizedKeys.keys;
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
};
|
||||
};
|
||||
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
# mokutil + sbsigntool — manage the shim trust chain and inspect signed
|
||||
# bootloader/kernel images when debugging.
|
||||
environment.systemPackages = with pkgs; [ git mokutil sbsigntool ];
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue