a73x

tests/ssh_auth_latency_test.rs

Ref:   Size: 1.8 KiB   History

mod common;

use std::process::Command;
use std::time::{Duration, Instant};

use common::ServerHarness;

/// OpenSSH probes with a "none" attempt and then offers each identity in
/// turn, so a client with one stray key ahead of the enrolled one is rejected
/// twice before it is accepted. The server must answer every rejection
/// promptly: it only accepts public keys, so a delay on rejection slows every
/// legitimate connection and protects nothing.
///
/// Measured before the fix: about one second per rejection, so a little over
/// two seconds here. After it: well under one.
#[test]
fn rejected_keys_are_answered_without_a_delay() {
    let harness = ServerHarness::new("latency");
    let stray = harness.named_key("stray");
    harness.authorize_named_keys(&["enrolled"]);
    let enrolled = harness.named_key("enrolled");

    let started = Instant::now();
    let output = Command::new("ssh")
        .args([
            "-p",
            &harness.ssh_port().to_string(),
            "-i",
            stray.to_str().unwrap(),
            "-i",
            enrolled.to_str().unwrap(),
            "-o",
            "StrictHostKeyChecking=no",
            "-o",
            "UserKnownHostsFile=/dev/null",
            "-o",
            "IdentitiesOnly=yes",
            "-o",
            "BatchMode=yes",
            "-o",
            "ConnectTimeout=5",
            "-v",
            "git@127.0.0.1",
            "true",
        ])
        .output()
        .expect("failed to run ssh");
    let elapsed = started.elapsed();

    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(
        stderr.contains("Authenticated to"),
        "the enrolled key must be accepted; ssh said:\n{stderr}"
    );
    assert!(
        elapsed < Duration::from_secs(1),
        "two rejections then an accept took {elapsed:?}; the server is delaying rejections"
    );
}