From 5c8d2d3a8c9d3899327baa4a653b5198ec8be7d1 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:13:04 +0700 Subject: [PATCH 01/12] Advance diagnostic SSH image to alpha.10 --- build/usb/version.env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/usb/version.env b/build/usb/version.env index c85c4412..09f806af 100644 --- a/build/usb/version.env +++ b/build/usb/version.env @@ -1,8 +1,8 @@ -RIGOS_PRODUCT_VERSION=0.0.4-alpha.9 -RIGOS_IMAGE_VERSION=0.0.4-alpha.9 +RIGOS_PRODUCT_VERSION=0.0.4-alpha.10 +RIGOS_IMAGE_VERSION=0.0.4-alpha.10 RIGOS_IMAGE_ID=rigos-usb-amd64 RIGOS_IMAGE_CHANNEL=alpha -RIGOS_BUILD_ORDINAL=9 +RIGOS_BUILD_ORDINAL=10 RIGOS_XMRIG_VERSION=6.26.0 RIGOS_XMRIG_ARCHIVE_SHA256=fc6f8ae5f64e4f17481f7e3be29a1c56949f216a998414188003eae1db20c9e5 RIGOS_XMRIG_BINARY_SHA256=b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49 From 32ce9e97d88a0f9cab916d511ca1b65756f0edab Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:13:15 +0700 Subject: [PATCH 02/12] Allow SSH host identity fallback without ready state --- .../etc/systemd/system/rigos-ssh-hostkeys.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service b/build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service index fe069df2..b4f7e2f0 100644 --- a/build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service +++ b/build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service @@ -1,7 +1,7 @@ [Unit] -Description=Establish persistent RIGOS SSH host identity +Description=Establish RIGOS SSH host identity After=rigos-state-ready.service -Requires=rigos-state-ready.service +Wants=rigos-state-ready.service Before=ssh.service [Service] From c567bed6d4d70dc2a6e18c317f0c434993a9dadc Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:13:23 +0700 Subject: [PATCH 03/12] Use runtime-published SSH host identity for diagnostics --- .../etc/ssh/sshd_config.d/01-rigos-hostkeys.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf b/build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf index 0532202d..7b9489f5 100644 --- a/build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf +++ b/build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf @@ -1 +1,4 @@ -HostKey /var/lib/rigos/system/ssh-hostkeys/ssh_host_ed25519_key +HostKey /run/rigos/ssh-hostkeys/ssh_host_ed25519_key +PasswordAuthentication yes +PermitRootLogin no +AllowUsers rigosadmin From 0a1aa893f1a766a95d51fede5eaa39240dd07a35 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:14:07 +0700 Subject: [PATCH 04/12] Add boot-scoped SSH host identity fallback --- .../usr/lib/rigos/rigos-ssh-hostkeys | 213 ++++++++++++++++-- 1 file changed, 193 insertions(+), 20 deletions(-) diff --git a/build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys b/build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys index 64bbbe43..83880877 100644 --- a/build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys +++ b/build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys @@ -18,6 +18,10 @@ PRIVATE_KEY = KEYS / "ssh_host_ed25519_key" PUBLIC_KEY = KEYS / "ssh_host_ed25519_key.pub" MANIFEST = KEYS / "manifest.json" RUNTIME = Path("/run/rigos") +ACTIVE_KEYS = RUNTIME / "ssh-hostkeys" +ACTIVE_PRIVATE_KEY = ACTIVE_KEYS / "ssh_host_ed25519_key" +ACTIVE_PUBLIC_KEY = ACTIVE_KEYS / "ssh_host_ed25519_key.pub" +ACTIVE_MANIFEST = ACTIVE_KEYS / "manifest.json" STATUS = RUNTIME / "ssh-hostkeys-status.json" STATE_STATUS = RUNTIME / "state-status.json" BOOT_ID = Path("/proc/sys/kernel/random/boot_id") @@ -141,6 +145,20 @@ def ensure_secure_directory(path: Path, mode: int) -> None: raise AuthorityError(f"{path} has unsafe mode") +def ensure_runtime_root() -> None: + if not RUNTIME.exists(): + RUNTIME.mkdir(mode=0o755, parents=True) + os.chown(RUNTIME, 0, 0) + RUNTIME.chmod(0o755) + observed = RUNTIME.lstat() + if stat.S_ISLNK(observed.st_mode) or not stat.S_ISDIR(observed.st_mode): + raise AuthorityError("runtime root is not a real directory") + if observed.st_uid != 0 or observed.st_gid != 0: + raise AuthorityError("runtime root is not owned by root:root") + if stat.S_IMODE(observed.st_mode) != 0o755: + raise AuthorityError("runtime root has unsafe mode") + + def validate_regular_file(path: Path, mode: int) -> None: try: observed = path.lstat() @@ -176,18 +194,21 @@ def key_fingerprint(public_key: Path) -> str: return fields[1] -def validate_keyset() -> str: +def validate_key_pair(private_key: Path, public_key: Path) -> str: + public_fields = public_key.read_text(encoding="ascii").strip().split() + derived_fields = command_output([SSH_KEYGEN, "-y", "-f", str(private_key)]).split() + if len(public_fields) < 2 or derived_fields[:2] != public_fields[:2]: + raise AuthorityError("SSH public and private keys do not match") + return key_fingerprint(public_key) + + +def validate_persistent_keyset() -> str: ensure_secure_directory(KEYS, 0o700) validate_regular_file(PRIVATE_KEY, 0o600) validate_regular_file(PUBLIC_KEY, 0o644) validate_regular_file(MANIFEST, 0o644) - public_fields = PUBLIC_KEY.read_text(encoding="ascii").strip().split() - derived_fields = command_output([SSH_KEYGEN, "-y", "-f", str(PRIVATE_KEY)]).split() - if len(public_fields) < 2 or derived_fields[:2] != public_fields[:2]: - raise AuthorityError("persistent SSH public and private keys do not match") - - fingerprint = key_fingerprint(PUBLIC_KEY) + fingerprint = validate_key_pair(PRIVATE_KEY, PUBLIC_KEY) public_sha256 = hashlib.sha256(PUBLIC_KEY.read_bytes()).hexdigest() manifest = read_json(MANIFEST) if ( @@ -202,21 +223,45 @@ def validate_keyset() -> str: return fingerprint -def remove_stale_staging() -> None: - for candidate in SYSTEM.glob(".ssh-hostkeys.tmp-*"): +def validate_active_keyset( + boot_id: str, mode: str, expected_source_fingerprint: str | None +) -> str: + ensure_secure_directory(ACTIVE_KEYS, 0o700) + validate_regular_file(ACTIVE_PRIVATE_KEY, 0o600) + validate_regular_file(ACTIVE_PUBLIC_KEY, 0o644) + validate_regular_file(ACTIVE_MANIFEST, 0o644) + + fingerprint = validate_key_pair(ACTIVE_PRIVATE_KEY, ACTIVE_PUBLIC_KEY) + public_sha256 = hashlib.sha256(ACTIVE_PUBLIC_KEY.read_bytes()).hexdigest() + manifest = read_json(ACTIVE_MANIFEST) + if ( + manifest.get("schema") != "rigos.ssh-active-hostkeys/v1" + or manifest.get("algorithm") != "ssh-ed25519" + or manifest.get("boot_id") != boot_id + or manifest.get("mode") != mode + or manifest.get("fingerprint") != fingerprint + or manifest.get("public_key_sha256") != public_sha256 + or manifest.get("source_fingerprint") != expected_source_fingerprint + ): + raise AuthorityError("active SSH host identity manifest is invalid") + return fingerprint + + +def remove_stale_staging(parent: Path, prefix: str) -> None: + for candidate in parent.glob(f"{prefix}*"): observed = candidate.lstat() if stat.S_ISLNK(observed.st_mode) or not stat.S_ISDIR(observed.st_mode): raise AuthorityError("unsafe SSH host identity staging path exists") if observed.st_uid != 0 or observed.st_gid != 0: raise AuthorityError("untrusted SSH host identity staging directory exists") shutil.rmtree(candidate) - fsync_directory(SYSTEM) + fsync_directory(parent) -def generate_keyset(boot_id: str) -> str: +def generate_persistent_keyset(boot_id: str) -> str: if KEYS.exists() or KEYS.is_symlink(): raise AuthorityError("persistent SSH host identity exists without a valid manifest") - remove_stale_staging() + remove_stale_staging(SYSTEM, ".ssh-hostkeys.tmp-") temporary = Path(tempfile.mkdtemp(prefix=".ssh-hostkeys.tmp-", dir=SYSTEM)) try: @@ -275,7 +320,92 @@ def generate_keyset(boot_id: str) -> str: finally: if temporary.exists(): shutil.rmtree(temporary) - return validate_keyset() + return validate_persistent_keyset() + + +def install_active_keyset( + boot_id: str, + mode: str, + source_private: Path | None = None, + source_public: Path | None = None, + source_fingerprint: str | None = None, +) -> str: + ensure_runtime_root() + if ACTIVE_KEYS.exists() or ACTIVE_KEYS.is_symlink(): + raise AuthorityError("active SSH host identity exists without a valid manifest") + remove_stale_staging(RUNTIME, ".ssh-hostkeys.active-") + + temporary = Path(tempfile.mkdtemp(prefix=".ssh-hostkeys.active-", dir=RUNTIME)) + try: + os.chown(temporary, 0, 0) + temporary.chmod(0o700) + private_key = temporary / ACTIVE_PRIVATE_KEY.name + public_key = temporary / ACTIVE_PUBLIC_KEY.name + manifest_path = temporary / ACTIVE_MANIFEST.name + + if mode == "persistent": + if source_private is None or source_public is None or source_fingerprint is None: + raise AuthorityError("persistent active identity source is incomplete") + shutil.copyfile(source_private, private_key) + shutil.copyfile(source_public, public_key) + elif mode == "ephemeral": + result = subprocess.run( + [ + SSH_KEYGEN, + "-q", + "-t", + "ed25519", + "-N", + "", + "-C", + "rigos-diagnostic-host", + "-f", + str(private_key), + ], + check=False, + stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE, + text=True, + timeout=30, + ) + if result.returncode != 0: + raise AuthorityError("ephemeral SSH host identity generation failed") + else: + raise AuthorityError("unknown active SSH host identity mode") + + os.chown(private_key, 0, 0) + os.chown(public_key, 0, 0) + private_key.chmod(0o600) + public_key.chmod(0o644) + fingerprint = validate_key_pair(private_key, public_key) + if mode == "persistent" and fingerprint != source_fingerprint: + raise AuthorityError("active SSH host identity differs from persistent source") + + manifest = { + "schema": "rigos.ssh-active-hostkeys/v1", + "algorithm": "ssh-ed25519", + "boot_id": boot_id, + "mode": mode, + "fingerprint": fingerprint, + "public_key_sha256": hashlib.sha256(public_key.read_bytes()).hexdigest(), + "source_fingerprint": source_fingerprint, + } + write_atomic_json(manifest_path, manifest, 0o644) + + for path in (private_key, public_key, manifest_path): + descriptor = os.open(path, os.O_RDONLY) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + fsync_directory(temporary) + os.rename(temporary, ACTIVE_KEYS) + fsync_directory(RUNTIME) + finally: + if temporary.exists(): + shutil.rmtree(temporary) + + return validate_active_keyset(boot_id, mode, source_fingerprint) def write_status(boot_id: str, outcome: str, **extra: object) -> None: @@ -283,6 +413,7 @@ def write_status(boot_id: str, outcome: str, **extra: object) -> None: "schema": "rigos.ssh-hostkeys-status/v1", "boot_id": boot_id, "outcome": outcome, + "active_path": str(ACTIVE_PRIVATE_KEY), "persistent_path": str(PRIVATE_KEY), } value.update(extra) @@ -296,19 +427,61 @@ def main() -> int: boot_id = "unavailable" try: boot_id = current_boot_id() - validate_state_ready(boot_id) - secure_state_root() - ensure_secure_directory(SYSTEM, 0o700) - reused = KEYS.exists() - fingerprint = validate_keyset() if reused else generate_keyset(boot_id) + state_reason = None + try: + validate_state_ready(boot_id) + persistent_state_ready = True + except AuthorityError as error: + persistent_state_ready = False + state_reason = str(error) + + ensure_runtime_root() + active_reused = ACTIVE_KEYS.exists() or ACTIVE_KEYS.is_symlink() + + if persistent_state_ready: + secure_state_root() + ensure_secure_directory(SYSTEM, 0o700) + persistent_reused = KEYS.exists() or KEYS.is_symlink() + persistent_fingerprint = ( + validate_persistent_keyset() + if persistent_reused + else generate_persistent_keyset(boot_id) + ) + fingerprint = ( + validate_active_keyset(boot_id, "persistent", persistent_fingerprint) + if active_reused + else install_active_keyset( + boot_id, + "persistent", + PRIVATE_KEY, + PUBLIC_KEY, + persistent_fingerprint, + ) + ) + mode = "persistent" + else: + persistent_reused = False + fingerprint = ( + validate_active_keyset(boot_id, "ephemeral", None) + if active_reused + else install_active_keyset(boot_id, "ephemeral") + ) + mode = "ephemeral" + write_status( boot_id, "ready", algorithm="ssh-ed25519", fingerprint=fingerprint, - reused=reused, + persistence_mode=mode, + active_reused=active_reused, + persistent_reused=persistent_reused, + state_reason=state_reason, + ) + print( + "RIGOS_SSH_HOSTKEYS=READY " + f"mode={mode} fingerprint={fingerprint} active_reused={str(active_reused).lower()}" ) - print(f"RIGOS_SSH_HOSTKEYS=READY fingerprint={fingerprint} reused={str(reused).lower()}") return 0 except (AuthorityError, OSError, subprocess.SubprocessError) as error: write_status(boot_id, "error", reason=str(error)) From e6a1c1361b71d97bd77ba167bd5f1759c342579c Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:14:53 +0700 Subject: [PATCH 05/12] Advance firstboot image gate assertions to alpha.10 --- crates/rigos-config/tests/firstboot_image_gate.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/rigos-config/tests/firstboot_image_gate.rs b/crates/rigos-config/tests/firstboot_image_gate.rs index e351a434..8b623ffb 100644 --- a/crates/rigos-config/tests/firstboot_image_gate.rs +++ b/crates/rigos-config/tests/firstboot_image_gate.rs @@ -10,15 +10,15 @@ fn repo_file(path: &str) -> String { } #[test] -fn alpha9_build_runs_source_and_exact_image_firstboot_gates() { +fn alpha10_build_runs_source_and_exact_image_firstboot_gates() { let version = repo_file("build/usb/version.env"); let entrypoint = repo_file("scripts/build-usb-image-entrypoint.sh"); let verifier = repo_file("scripts/verify-firstboot-image.sh"); let hook = repo_file("build/usb/hooks/010-rigos.chroot"); - assert!(version.contains("RIGOS_PRODUCT_VERSION=0.0.4-alpha.9")); - assert!(version.contains("RIGOS_IMAGE_VERSION=0.0.4-alpha.9")); - assert!(version.contains("RIGOS_BUILD_ORDINAL=9")); + assert!(version.contains("RIGOS_PRODUCT_VERSION=0.0.4-alpha.10")); + assert!(version.contains("RIGOS_IMAGE_VERSION=0.0.4-alpha.10")); + assert!(version.contains("RIGOS_BUILD_ORDINAL=10")); assert!(entrypoint.contains("--test firstboot_tty")); assert!(entrypoint.contains("bash ./scripts/verify-firstboot-image.sh \"$image\"")); From e4dd778b26e43394a7c51ece8656555153f4502b Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:15:19 +0700 Subject: [PATCH 06/12] Test diagnostic SSH host identity fallback --- crates/rigos-config/tests/diagnostic_ssh.rs | 163 ++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 crates/rigos-config/tests/diagnostic_ssh.rs diff --git a/crates/rigos-config/tests/diagnostic_ssh.rs b/crates/rigos-config/tests/diagnostic_ssh.rs new file mode 100644 index 00000000..ad9abcf2 --- /dev/null +++ b/crates/rigos-config/tests/diagnostic_ssh.rs @@ -0,0 +1,163 @@ +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use std::time::{SystemTime, UNIX_EPOCH}; + +fn repo_path(path: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .join(path) +} + +fn repo_file(path: &str) -> String { + let path = repo_path(path); + fs::read_to_string(&path) + .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display())) +} + +#[test] +fn diagnostic_ssh_keeps_host_identity_mandatory_without_requiring_ready_state() { + let service = repo_file( + "build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service", + ); + let ssh_dropin = repo_file( + "build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf", + ); + let policy = repo_file( + "build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf", + ); + let authority = + repo_file("build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys"); + let version = repo_file("build/usb/version.env"); + + assert!(service.contains("After=rigos-state-ready.service")); + assert!(service.contains("Wants=rigos-state-ready.service")); + assert!(!service.contains("Requires=rigos-state-ready.service")); + assert!(service.contains("Before=ssh.service")); + assert!(ssh_dropin.contains("Requires=rigos-ssh-hostkeys.service")); + + for required in [ + "HostKey /run/rigos/ssh-hostkeys/ssh_host_ed25519_key", + "PasswordAuthentication yes", + "PermitRootLogin no", + "AllowUsers rigosadmin", + ] { + assert!(policy.lines().any(|line| line == required)); + } + + for required in [ + "ACTIVE_KEYS = RUNTIME / \"ssh-hostkeys\"", + "mode = \"persistent\"", + "mode = \"ephemeral\"", + "rigos.ssh-active-hostkeys/v1", + "persistent_state_ready", + "install_active_keyset", + ] { + assert!(authority.contains(required), "authority is missing {required}"); + } + + assert!(version.contains("RIGOS_IMAGE_VERSION=0.0.4-alpha.10")); + assert!(version.contains("RIGOS_BUILD_ORDINAL=10")); +} + +#[test] +fn hostkey_authority_selects_ephemeral_or_persistent_storage_without_touching_unready_state() { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let root = std::env::temp_dir().join(format!("rigos-diagnostic-ssh-{unique}")); + fs::create_dir_all(&root).unwrap(); + + let fixture = r#" +import runpy +import sys +from pathlib import Path + +source = Path(sys.argv[1]) +root = Path(sys.argv[2]) +namespace = runpy.run_path(str(source), run_name='rigos_ssh_hostkeys_test') +g = namespace['main'].__globals__ + + +def configure(case): + runtime = root / case / 'run' + state = root / case / 'state' + runtime.mkdir(parents=True) + g['RUNTIME'] = runtime + g['STATE'] = state + g['SYSTEM'] = state / 'system' + g['KEYS'] = state / 'system' / 'ssh-hostkeys' + g['PRIVATE_KEY'] = g['KEYS'] / 'ssh_host_ed25519_key' + g['PUBLIC_KEY'] = g['KEYS'] / 'ssh_host_ed25519_key.pub' + g['MANIFEST'] = g['KEYS'] / 'manifest.json' + g['ACTIVE_KEYS'] = runtime / 'ssh-hostkeys' + g['ACTIVE_PRIVATE_KEY'] = g['ACTIVE_KEYS'] / 'ssh_host_ed25519_key' + g['ACTIVE_PUBLIC_KEY'] = g['ACTIVE_KEYS'] / 'ssh_host_ed25519_key.pub' + g['ACTIVE_MANIFEST'] = g['ACTIVE_KEYS'] / 'manifest.json' + g['STATUS'] = runtime / 'ssh-hostkeys-status.json' + g['STATE_STATUS'] = runtime / 'state-status.json' + g['current_boot_id'] = lambda: 'boot-test' + g['ensure_runtime_root'] = lambda: None + return state + + +# State unavailable: only a boot-scoped runtime identity may be selected. +state = configure('ephemeral') +recorded = {} +def unavailable(_boot_id): + raise g['AuthorityError']('limited_capacity') +g['validate_state_ready'] = unavailable +g['install_active_keyset'] = lambda boot_id, mode, *args: ( + recorded.update({'boot_id': boot_id, 'mode': mode, 'args': args}) or 'SHA256:ephemeral' +) +g['write_status'] = lambda boot_id, outcome, **extra: recorded.update( + {'status_boot_id': boot_id, 'outcome': outcome, **extra} +) +assert namespace['main']() == 0 +assert recorded['mode'] == 'ephemeral' +assert recorded['persistence_mode'] == 'ephemeral' +assert recorded['state_reason'] == 'limited_capacity' +assert not state.exists(), 'ephemeral fallback touched unverified persistent state' + + +# Verified state: preserve the persistent identity and publish it to runtime. +state = configure('persistent') +recorded = {} +g['validate_state_ready'] = lambda _boot_id: None +g['secure_state_root'] = lambda: None +g['ensure_secure_directory'] = lambda _path, _mode: None +g['generate_persistent_keyset'] = lambda _boot_id: 'SHA256:persistent' +def publish(boot_id, mode, private_key, public_key, source_fingerprint): + recorded.update({ + 'boot_id': boot_id, + 'mode': mode, + 'private_key': private_key, + 'public_key': public_key, + 'source_fingerprint': source_fingerprint, + }) + return source_fingerprint +g['install_active_keyset'] = publish +g['write_status'] = lambda boot_id, outcome, **extra: recorded.update( + {'status_boot_id': boot_id, 'outcome': outcome, **extra} +) +assert namespace['main']() == 0 +assert recorded['mode'] == 'persistent' +assert recorded['persistence_mode'] == 'persistent' +assert recorded['source_fingerprint'] == 'SHA256:persistent' +assert recorded['state_reason'] is None +"#; + + let result = Command::new("python3") + .arg("-c") + .arg(fixture) + .arg(repo_path( + "build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys", + )) + .arg(&root) + .status() + .expect("run diagnostic SSH host-key fixture"); + + let _ = fs::remove_dir_all(root); + assert!(result.success(), "diagnostic SSH host-key fixture failed"); +} From 48660f4311da67985cee2f393fa8d0f1367124a4 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:16:08 +0700 Subject: [PATCH 07/12] Verify diagnostic SSH fallback in appliance image --- scripts/verify-usb-appliance.sh | 34 +++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/verify-usb-appliance.sh b/scripts/verify-usb-appliance.sh index f89df2bf..0b564494 100755 --- a/scripts/verify-usb-appliance.sh +++ b/scripts/verify-usb-appliance.sh @@ -141,28 +141,42 @@ hostkey_service="$temporary/root/etc/systemd/system/rigos-ssh-hostkeys.service" hostkey_policy="$temporary/root/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf" ssh_dropin="$temporary/root/etc/systemd/system/ssh.service.d/rigos-observe.conf" hostkey_authority="$temporary/root/usr/lib/rigos/rigos-ssh-hostkeys" -[[ -x "$hostkey_authority" ]] || die 'persistent SSH host-key authority is missing or not executable' -[[ -L "$temporary/root/etc/systemd/system/multi-user.target.wants/rigos-ssh-hostkeys.service" ]] || die 'persistent SSH host-key service is not enabled' +[[ -x "$hostkey_authority" ]] || die 'SSH host-key authority is missing or not executable' +[[ -L "$temporary/root/etc/systemd/system/multi-user.target.wants/rigos-ssh-hostkeys.service" ]] || die 'SSH host-key service is not enabled' for required in \ 'After=rigos-state-ready.service' \ - 'Requires=rigos-state-ready.service' \ + 'Wants=rigos-state-ready.service' \ 'Before=ssh.service' \ 'ExecStart=/usr/lib/rigos/rigos-ssh-hostkeys' \ 'ReadWritePaths=/var/lib/rigos /run/rigos' do - grep -Fqx "$required" "$hostkey_service" || die "persistent SSH host-key service contract is missing: $required" + grep -Fqx "$required" "$hostkey_service" || die "SSH host-key service contract is missing: $required" done -grep -Fqx 'HostKey /var/lib/rigos/system/ssh-hostkeys/ssh_host_ed25519_key' "$hostkey_policy" || die 'sshd persistent HostKey policy is missing' -grep -Fqx 'Requires=rigos-ssh-hostkeys.service' "$ssh_dropin" || die 'ssh.service does not require persistent host identity' -grep -Fqx 'After=rigos-recovery-access.service rigos-ssh-hostkeys.service' "$ssh_dropin" || die 'ssh.service ordering bypasses persistent host identity' +if grep -Fqx 'Requires=rigos-state-ready.service' "$hostkey_service"; then + die 'SSH diagnostics are still hard-blocked by persistent state readiness' +fi +for required in \ + 'HostKey /run/rigos/ssh-hostkeys/ssh_host_ed25519_key' \ + 'PasswordAuthentication yes' \ + 'PermitRootLogin no' \ + 'AllowUsers rigosadmin' +do + grep -Fqx "$required" "$hostkey_policy" || die "sshd diagnostic access policy is missing: $required" +done +grep -Fqx 'Requires=rigos-ssh-hostkeys.service' "$ssh_dropin" || die 'ssh.service does not require an established host identity' +grep -Fqx 'After=rigos-recovery-access.service rigos-ssh-hostkeys.service' "$ssh_dropin" || die 'ssh.service ordering bypasses host identity establishment' for required in \ 'STATE = Path("/var/lib/rigos")' \ 'KEYS = SYSTEM / "ssh-hostkeys"' \ + 'ACTIVE_KEYS = RUNTIME / "ssh-hostkeys"' \ '"schema": "rigos.ssh-hostkeys/v1"' \ - 'os.rename(temporary, KEYS)' \ - '"persistent SSH host identity exists without a valid manifest"' + '"schema": "rigos.ssh-active-hostkeys/v1"' \ + 'mode = "persistent"' \ + 'mode = "ephemeral"' \ + 'install_active_keyset' \ + 'persistent_state_ready' do - grep -Fq "$required" "$hostkey_authority" || die "persistent SSH host-key authority contract is missing: $required" + grep -Fq "$required" "$hostkey_authority" || die "SSH host-key authority contract is missing: $required" done [[ -x "$temporary/root/usr/lib/rigos/rigos-performance" ]] || die 'performance authority is missing or not executable' From 02c6552c1473749ce0376b735510f88cb2bda56d Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:16:33 +0700 Subject: [PATCH 08/12] Verify diagnostic SSH fallback with RandomX image gate --- scripts/verify-randomx-performance-image.sh | 38 ++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/scripts/verify-randomx-performance-image.sh b/scripts/verify-randomx-performance-image.sh index 1a774407..8040981a 100644 --- a/scripts/verify-randomx-performance-image.sh +++ b/scripts/verify-randomx-performance-image.sh @@ -86,11 +86,11 @@ root="$temporary/squash" [[ -L "$root/usr/sbin/modprobe" || -x "$root/usr/sbin/modprobe" ]] || die 'modprobe entrypoint is missing' [[ -f "$root/usr/lib/rigos/rigos-randomx-msr" ]] || die 'RandomX MSR authority is missing' [[ -f "$root/usr/lib/rigos/rigos-miner-gate" ]] || die 'miner safety gate is missing' -[[ -f "$root/usr/lib/rigos/rigos-ssh-hostkeys" ]] || die 'persistent SSH host-key authority is missing' +[[ -f "$root/usr/lib/rigos/rigos-ssh-hostkeys" ]] || die 'SSH host-key authority is missing' [[ -L "$root/etc/systemd/system/multi-user.target.wants/rigos-randomx-msr.service" ]] \ || die 'RandomX MSR authority is not enabled in the appliance' [[ -L "$root/etc/systemd/system/multi-user.target.wants/rigos-ssh-hostkeys.service" ]] \ - || die 'persistent SSH host-key authority is not enabled in the appliance' + || die 'SSH host-key authority is not enabled in the appliance' python3 -m py_compile \ "$root/usr/lib/rigos/rigos-randomx-msr" \ "$root/usr/lib/rigos/rigos-miner-gate" \ @@ -127,29 +127,43 @@ grep -Fqx 'ExecCondition=/usr/lib/rigos/rigos-miner-gate' "$miner" \ for required in \ 'After=rigos-state-ready.service' \ - 'Requires=rigos-state-ready.service' \ + 'Wants=rigos-state-ready.service' \ 'Before=ssh.service' \ 'ExecStart=/usr/lib/rigos/rigos-ssh-hostkeys' \ 'ReadWritePaths=/var/lib/rigos /run/rigos' do grep -Fqx "$required" "$hostkey_service" \ - || die "persistent SSH host-key service contract is missing: $required" + || die "SSH host-key service contract is missing: $required" +done +if grep -Fqx 'Requires=rigos-state-ready.service' "$hostkey_service"; then + die 'SSH diagnostics are hard-blocked by persistent state readiness' +fi +for required in \ + 'HostKey /run/rigos/ssh-hostkeys/ssh_host_ed25519_key' \ + 'PasswordAuthentication yes' \ + 'PermitRootLogin no' \ + 'AllowUsers rigosadmin' +do + grep -Fqx "$required" "$hostkey_policy" \ + || die "sshd diagnostic access policy is missing: $required" done -grep -Fqx 'HostKey /var/lib/rigos/system/ssh-hostkeys/ssh_host_ed25519_key' "$hostkey_policy" \ - || die 'sshd does not use the persistent RIGOS host identity' grep -Fqx 'Requires=rigos-ssh-hostkeys.service' "$ssh_dropin" \ - || die 'ssh.service does not require persistent host identity' + || die 'ssh.service does not require an established host identity' grep -Fqx 'After=rigos-recovery-access.service rigos-ssh-hostkeys.service' "$ssh_dropin" \ - || die 'ssh.service ordering bypasses persistent host identity' + || die 'ssh.service ordering bypasses host identity establishment' for required in \ 'STATE = Path("/var/lib/rigos")' \ 'KEYS = SYSTEM / "ssh-hostkeys"' \ + 'ACTIVE_KEYS = RUNTIME / "ssh-hostkeys"' \ '"schema": "rigos.ssh-hostkeys/v1"' \ - 'os.rename(temporary, KEYS)' \ - '"persistent SSH host identity exists without a valid manifest"' + '"schema": "rigos.ssh-active-hostkeys/v1"' \ + 'mode = "persistent"' \ + 'mode = "ephemeral"' \ + 'install_active_keyset' \ + 'persistent_state_ready' do grep -Fq "$required" "$hostkey_authority" \ - || die "persistent SSH host-key authority contract is missing: $required" + || die "SSH host-key authority contract is missing: $required" done for required in \ @@ -174,5 +188,5 @@ do done printf 'RIGOS RandomX kernel MSR support: %s\n' "$msr_support" -printf 'RIGOS persistent SSH host-key image verification passed\n' +printf 'RIGOS SSH host-key image verification passed\n' printf 'RIGOS RandomX performance image verification passed: %s\n' "$image" From 0e326500576c2b7e96e47baac9153abd52bfac30 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:17:10 +0700 Subject: [PATCH 09/12] Allow diagnostic SSH after failed state readiness --- scripts/verify-systemd-ordering.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/verify-systemd-ordering.py b/scripts/verify-systemd-ordering.py index 67e13d1b..6a61abc5 100644 --- a/scripts/verify-systemd-ordering.py +++ b/scripts/verify-systemd-ordering.py @@ -123,12 +123,16 @@ def verify(units): includes( hostkeys.words("Unit", "After"), {"rigos-state-ready.service"}, - "SSH host-key authority must follow state readiness", + "SSH host-key authority must follow the state readiness attempt", ) includes( - hostkeys.words("Unit", "Requires"), + hostkeys.words("Unit", "Wants"), {"rigos-state-ready.service"}, - "SSH host-key authority must require state readiness", + "SSH host-key authority must request state readiness", + ) + require( + "rigos-state-ready.service" not in hostkeys.words("Unit", "Requires"), + "diagnostic SSH must survive state readiness failure", ) includes( hostkeys.words("Unit", "Before"), From ee8809057b0eeecbaa260408ed26cc114c0dfa25 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:18:32 +0700 Subject: [PATCH 10/12] Verify alpha.10 diagnostic SSH host-key fallback --- scripts/check-alpha8-ssh-hotfix.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/scripts/check-alpha8-ssh-hotfix.py b/scripts/check-alpha8-ssh-hotfix.py index 5c513ce8..654826e0 100644 --- a/scripts/check-alpha8-ssh-hotfix.py +++ b/scripts/check-alpha8-ssh-hotfix.py @@ -44,8 +44,14 @@ def main() -> int: raise RuntimeError("OpenSSH server package is missing") hostkey_policy = normalized_lf_bytes(HOSTKEY_POLICY) - if hostkey_policy != b"HostKey /var/lib/rigos/system/ssh-hostkeys/ssh_host_ed25519_key\n": - raise RuntimeError("persistent SSH HostKey policy is not exact") + expected_hostkey_policy = ( + b"HostKey /run/rigos/ssh-hostkeys/ssh_host_ed25519_key\n" + b"PasswordAuthentication yes\n" + b"PermitRootLogin no\n" + b"AllowUsers rigosadmin\n" + ) + if hostkey_policy != expected_hostkey_policy: + raise RuntimeError("diagnostic SSH HostKey and authentication policy is not exact") baked_keys = sorted(path for path in SSH_DIRECTORY.glob("ssh_host_*_key*") if path.is_file()) if baked_keys: raise RuntimeError("appliance source contains a baked SSH host private or public key") @@ -67,26 +73,32 @@ def main() -> int: for required in ( 'STATE = Path("/var/lib/rigos")', 'KEYS = SYSTEM / "ssh-hostkeys"', + 'ACTIVE_KEYS = RUNTIME / "ssh-hostkeys"', '"schema": "rigos.ssh-hostkeys/v1"', - 'os.rename(temporary, KEYS)', + '"schema": "rigos.ssh-active-hostkeys/v1"', + 'mode = "persistent"', + 'mode = "ephemeral"', 'or status.get("outcome") != "ready"', - '"persistent SSH public and private keys do not match"', + '"SSH public and private keys do not match"', '"persistent SSH host identity exists without a valid manifest"', + '"ephemeral SSH host identity generation failed"', ): if required not in authority: - raise RuntimeError(f"persistent SSH host-key authority contract is missing: {required}") + raise RuntimeError(f"SSH host-key authority contract is missing: {required}") require_lines( HOSTKEY_UNIT, ( "After=rigos-state-ready.service", - "Requires=rigos-state-ready.service", + "Wants=rigos-state-ready.service", "Before=ssh.service", "ExecStart=/usr/lib/rigos/rigos-ssh-hostkeys", "ReadWritePaths=/var/lib/rigos /run/rigos", "WantedBy=multi-user.target", ), ) + if "Requires=rigos-state-ready.service" in HOSTKEY_UNIT.read_text(encoding="utf-8"): + raise RuntimeError("diagnostic SSH is still hard-blocked by state readiness") require_lines( SSH_DROPIN, ( @@ -96,7 +108,7 @@ def main() -> int: ), ) if "Before=rigos-ssh-hostkeys.service" not in STATE_READY_UNIT.read_text(encoding="utf-8"): - raise RuntimeError("state readiness is not ordered before persistent SSH identity") + raise RuntimeError("state readiness attempt is not ordered before SSH identity selection") dockerfile = DOCKERFILE.read_text(encoding="utf-8") if 'ENV PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:${PATH}"' not in dockerfile: @@ -128,7 +140,7 @@ def main() -> int: if required not in recovery_gate: raise RuntimeError(f"recovery access validator contract is missing: {required}") - print("RIGOS Alpha8 SSH, recovery, and persistent host-key verification passed") + print("RIGOS SSH, recovery, and diagnostic host-key verification passed") return 0 From 1f2981cefff10c1d8fb1a070d454fe7065f74ed7 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:18:52 +0700 Subject: [PATCH 11/12] Gate alpha.10 image builds on diagnostic SSH tests --- scripts/build-usb-image-entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build-usb-image-entrypoint.sh b/scripts/build-usb-image-entrypoint.sh index 2970c3c6..336a12e2 100644 --- a/scripts/build-usb-image-entrypoint.sh +++ b/scripts/build-usb-image-entrypoint.sh @@ -45,6 +45,7 @@ cargo test --locked -p rigos-config --test miner_observer_authority -- --nocaptu cargo test --locked -p rigos-config --test randomx_build_entrypoint -- --nocapture cargo test --locked -p rigos-config --test randomx_msr_authority -- --nocapture cargo test --locked -p rigos-config --test firstboot_tty -- --nocapture +cargo test --locked -p rigos-config --test diagnostic_ssh -- --nocapture ./scripts/build-usb-image.sh From 33e02e253b9d0691909e22464d0eee34056958f6 Mon Sep 17 00:00:00 2001 From: DEADBYTE Date: Sat, 11 Jul 2026 00:42:37 +0700 Subject: [PATCH 12/12] Format diagnostic SSH tests --- crates/rigos-config/tests/diagnostic_ssh.rs | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/rigos-config/tests/diagnostic_ssh.rs b/crates/rigos-config/tests/diagnostic_ssh.rs index ad9abcf2..9251e5d2 100644 --- a/crates/rigos-config/tests/diagnostic_ssh.rs +++ b/crates/rigos-config/tests/diagnostic_ssh.rs @@ -17,17 +17,13 @@ fn repo_file(path: &str) -> String { #[test] fn diagnostic_ssh_keeps_host_identity_mandatory_without_requiring_ready_state() { - let service = repo_file( - "build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service", - ); - let ssh_dropin = repo_file( - "build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf", - ); - let policy = repo_file( - "build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf", - ); - let authority = - repo_file("build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys"); + let service = + repo_file("build/usb/includes.chroot/etc/systemd/system/rigos-ssh-hostkeys.service"); + let ssh_dropin = + repo_file("build/usb/includes.chroot/etc/systemd/system/ssh.service.d/rigos-observe.conf"); + let policy = + repo_file("build/usb/includes.chroot/etc/ssh/sshd_config.d/01-rigos-hostkeys.conf"); + let authority = repo_file("build/usb/includes.chroot/usr/lib/rigos/rigos-ssh-hostkeys"); let version = repo_file("build/usb/version.env"); assert!(service.contains("After=rigos-state-ready.service")); @@ -53,7 +49,10 @@ fn diagnostic_ssh_keeps_host_identity_mandatory_without_requiring_ready_state() "persistent_state_ready", "install_active_keyset", ] { - assert!(authority.contains(required), "authority is missing {required}"); + assert!( + authority.contains(required), + "authority is missing {required}" + ); } assert!(version.contains("RIGOS_IMAGE_VERSION=0.0.4-alpha.10"));