Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

crates/gunnar-front/src/keys.rs

//! **The keys the front mints itself** and offers the installer — never fed.
//!
//! Moved here from gunnar-server's `front_role` (lane A 54fa7bd), where the
//! front's role wrongly lived on the gunnar medium.

use std::path::Path;

/// Under the data root: the front's certify key (OpenSSH, 0600).
pub const CERTIFY_KEY_REL: &str = "pairing/certify.key";
/// Under the data root: the certify record (what the old door kept beside
/// its ledger).
pub const CERTIFY_RECORD_REL: &str = "pairing/certify.toml";

/// **The front's certify key**, minted (0600) here if absent. Its public half
/// is offered as `certify_pubkey`; the elected primary installs it as the
/// machine admin `front-certify`, and the twin's enrolment and certificate
/// are signed with it.
pub fn certify_key(root: &Path) -> anyhow::Result<gunnar_core::ssh_key::PrivateKey> {
    // ONE writer (release-3, MEASURED on the mock): the installer's first visit
    // (`offer`) and the role thread asked at once, both wrote `certify.key.tmp`,
    // and the role kept a key that was never offered. The mint is serialized,
    // and every caller returns the key that is ON DISK after it.
    static MINT: std::sync::Mutex<()> = std::sync::Mutex::new(());
    let _one = MINT.lock().unwrap_or_else(|p| p.into_inner());
    let path = root.join(CERTIFY_KEY_REL);
    if !std::fs::read(&path).map(|b| !b.is_empty()).unwrap_or(false) {
        let pem = gunnar_core::mint_ed25519()?;
        gunnar_server::pairing::write_secret(root, CERTIFY_KEY_REL, std::str::from_utf8(&pem)?)?;
    }
    let pem = std::fs::read(&path).map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))?;
    gunnar_core::ssh_key::PrivateKey::from_openssh(&pem)
        .map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))
}

/// Its public key line — what the front offers the installer.
pub fn certify_pubkey(root: &Path) -> anyhow::Result<String> {
    let mut line = certify_key(root)?
        .public_key()
        .to_openssh()
        .map_err(|e| anyhow::anyhow!("{e}"))?;
    line.push(' ');
    line.push_str(gunnar_pairing_door::front::CERTIFY_PRINCIPAL);
    Ok(line)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn the_certify_key_is_minted_once_0600_and_offered_as_one_line() {
        use std::os::unix::fs::PermissionsExt as _;
        let dir = tempfile::tempdir().unwrap();
        let line = certify_pubkey(dir.path()).unwrap();
        assert!(
            line.starts_with("ssh-ed25519 ") && line.ends_with(" front-certify"),
            "{line}"
        );
        assert_eq!(certify_pubkey(dir.path()).unwrap(), line, "minted once");
        let mode = std::fs::metadata(dir.path().join(CERTIFY_KEY_REL))
            .unwrap()
            .permissions()
            .mode()
            & 0o777;
        assert_eq!(mode, 0o600);
        // What the primary installs from the offer is a valid fed principal.
        let feed = std::collections::BTreeMap::from([(
            format!(
                "offer.vm1.{}",
                gunnar_pairing_door::front::OFFER_CERTIFY_PUBKEY
            ),
            line,
        )]);
        let p = gunnar_server::fed_principals::parse(&feed).unwrap();
        assert_eq!(p[0].id, gunnar_pairing_door::front::CERTIFY_PRINCIPAL);
    }

    /// ★ release-3 (MEASURED on the dwarf mock 2026-09-29, install-orig-5):
    /// the installer's first visit (`offer`) and the front's role thread minted
    /// the certify key AT ONCE; both wrote `certify.key.tmp`, one rename lost
    /// ("replacing /data/pairing/certify.key: No such file"), and the role
    /// signed with a key that was never offered — every BecomeTwin refused
    /// [bootstrap-unauthenticated]. However many ask at once, ONE key exists:
    /// every caller gets the key that is on disk.
    #[test]
    fn a_certify_key_minted_by_many_at_once_is_one_key_and_the_one_on_disk() {
        for _ in 0..20 {
            let dir = tempfile::tempdir().unwrap();
            let root = dir.path().to_path_buf();
            let barrier = std::sync::Arc::new(std::sync::Barrier::new(8));
            let got: Vec<String> = (0..8)
                .map(|_| {
                    let (root, barrier) = (root.clone(), std::sync::Arc::clone(&barrier));
                    std::thread::spawn(move || {
                        barrier.wait();
                        certify_key(&root)
                            .map(|k| k.public_key().to_openssh().unwrap())
                            .unwrap_or_else(|e| format!("ERR {e:#}"))
                    })
                })
                .collect::<Vec<_>>()
                .into_iter()
                .map(|h| h.join().unwrap())
                .collect();
            let on_disk = certify_key(&root)
                .unwrap()
                .public_key()
                .to_openssh()
                .unwrap();
            assert!(
                got.iter().all(|g| *g == on_disk),
                "one key, the one on disk: {got:?} vs {on_disk}"
            );
        }
    }
}

#[cfg(test)]
mod kept_tests {
    /// **A new installation on a kept /data keeps the front's certify key**
    /// (stinkies upgrade-front-certify-reminted + recharge-front-new-key-deadlock,
    /// estate-box U3): the first boot of a keep-upgrade cleared `pairing/certify.key`
    /// (gunnar-front PAIRING_STATE), so the front offered a NEW certify_pubkey, main
    /// refused it (a second key on `front-certify` needs its attestation) and the twin
    /// could not be certified; its fresh record re-certified a twin that was certified.
    /// The front keeps every identity key where its disk survives (certify, monetize,
    /// console, guest, clock) — as a gunnar box keeps `wg0.key` — and clears only
    /// what the installer fed and the box's own certificate.
    #[test]
    fn a_fresh_installation_on_a_kept_disk_keeps_the_certify_key_and_its_record() {
        let esp = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(esp.path().join("korp")).unwrap();
        let data = tempfile::tempdir().unwrap();
        let before = super::certify_pubkey(data.path()).unwrap();
        std::fs::write(
            data.path().join(super::CERTIFY_RECORD_REL),
            "certified = true\n",
        )
        .unwrap();
        let cleared = gunnar_server::installer_listener::reset_if_fresh_install_of(
            esp.path(),
            data.path(),
            &crate::PAIRING_STATE,
            &|p| std::fs::write(p, b"x").is_ok(),
        )
        .expect("a first boot");
        assert!(
            !cleared
                .iter()
                .any(|c| c == super::CERTIFY_KEY_REL || c == super::CERTIFY_RECORD_REL),
            "{cleared:?}"
        );
        assert_eq!(
            super::certify_pubkey(data.path()).unwrap(),
            before,
            "the same certify key after the new installation"
        );
        assert!(
            data.path().join(super::CERTIFY_RECORD_REL).exists(),
            "the record kept: an unchanged pair is not certified again"
        );
    }
}