feat(e2e): client-direction real-sshd e2e (w5-e2e-client)

The wave-5 client-direction real-tool e2e: a dedicated
`tests/e2e_client.rs` binary dialing a per-row spawned real OpenSSH
server with the shipped crate — exec (exit-status parity through real
reaping), the direct-tcpip dial, the `-R` leg (port-0 mint + accepted
arrival + data leg + cancel tearing the real listen down + unknown-
cancel ListenRefused), and the decline rows (sftp placeholder, host-key
mismatch hard-decline, server-refused direct-tcpip with the connection
staying usable). Fixture shape decided + recorded in the task file:
per-row spawned sshd, minted ed25519 host key, publickey-only knobs,
the decrypt-at-dial vault posture, the case-1 pin posture. No src/
changes; every row check-gated skipped-with-reason and deadline-bounded.

Verification: `scripts/matrix.sh` green (cargo test per build none/git/
session/tunnel/sftp/--all-features — e2e_client rides 2/2/3/5/2/7 rows,
clippy x2 `-D warnings`, fmt, doc), plus 10 consecutive all-features
e2e_client runs green. taskgraph validate: 42 tasks valid.
This commit is contained in:
glm-5.3-flash committed 2026-10-30 07:55:13 +00:00
1 parent 567a34a5e1
commit 69d888a231
2 files changed
+1037 -8

No files matched your search

+59 -8
View File
@@ -1,7 +1,7 @@
---
id: w5-e2e-client
name: Wave 5 — real-tool e2e, client direction (the door against a real sshd)
status: pending
status: completed
depends_on: [review-wave-4]
scope: moderate
risk: medium
@@ -50,16 +50,16 @@ matrix task's binaries), under the check-gated
## Acceptance Criteria
- [ ] The sshd fixture lands (spawned real server, ed25519 host +
- [x] The sshd fixture lands (spawned real server, ed25519 host +
user keys, the policy pin posture); its shape decided +
recorded
- [ ] Exec, direct-tcpip, and the `-R` leg (port-0 mint + accepted
- [x] Exec, direct-tcpip, and the `-R` leg (port-0 mint + accepted
arrival + cancel) green against real sshd (or reasoned-skipped)
- [ ] The decline rows green: sftp placeholder, unfeatured
- [x] The decline rows green: sftp placeholder, unfeatured
direct-tcpip (connection stays usable), host-key mismatch
hard-decline
- [ ] Every finding fixed in-task or booked for the gate (recorded)
- [ ] `cargo test` (+ the builds the new binary rides), clippy ×2
- [x] Every finding fixed in-task or booked for the gate (recorded)
- [x] `cargo test` (+ the builds the new binary rides), clippy ×2
(`-D warnings`), fmt, `cargo doc --no-deps` green
## References
@@ -80,8 +80,59 @@ matrix task's binaries), under the check-gated
## Notes
> To be filled by implementation agent
**The sshd fixture decided**: `tests/e2e_client.rs` (the new binary), reusing `tests/common` — the
wave-4 client binaries stay single-writer per the decomposition's file map; this task edited only
its own binary (`tests/e2e_client.rs`, an in-file `support` module). **Fixture shape: per-row
spawned real `sshd`** — each row's config knobs differ (`AllowTcpForwarding` yes/no) and each row
dials its own server, so a shared per-binary instance buys nothing; per-row isolation (own workdir,
own minted ed25519 host key via `ssh-keygen`, own probe-verified port from the e2e-server's
monotonic allocator posture) beats the spawn cost. Knob set: publickey-only, `StrictModes no` (the
throwaway workdir carries none of the real home-dir hygiene), `UsePAM no`, `UseDNS no`,
`PidFile none`, `PermitRootLogin prohibit-password`, host key + authorized_keys (fixture `alpha`'s
public blob) at absolute paths, per-row `AllowTcpForwarding`. Readiness is a bounded TCP probe; a
server that never comes up is the row's **reasoned skip** with the server's own `-e` log tail as
the reason (worked as non-root in this environment — verified against OpenSSH 9.6p1). The dial
rides the decrypt-at-dial path over the in-memory vault double (`VaultKeySource` over `TestVault`,
`alpha` sealed) and the case-1 pin posture (`MemHostKeyPolicy::pin`) accepts only the minted host
fingerprint.
**Row inventory** (all check-gated, `#[cfg(unix)]` + feature-gated per row group; deadline-bounded
end to end — the POC-1 §5 posture; `Contract stamp:` markers cite `client.md` / `reverse-runner.md`):
| Row | Build(s) | What it pins |
|---|---|---|
| `exec_row::real_sshd_exec_pumps_streams_and_reports_the_exit_status` | session (+all) | the wave-4 double-driven exec row re-landed: stdout/stderr separated through a real server, the **nonzero** exit (3) reported in-band last — the real server's reaping, parity not a canned 0; the case-1 pin accept decision on record against the really-presented key |
| `direct_tcpip_row::real_sshd_direct_tcpip_round_trips_a_live_tcp_connection` | tunnel (+all) | the `-L`-shaped dial through a real server's forwarding into a live loopback echo server, bytes end to end |
| `reverse_rows::real_sshd_port_zero_mint_serves_the_accepted_arrival_and_the_data_leg` | tunnel (+all) | the port-0 mint surfaces the actually-bound port (verified by dialing it), the server-initiated `forwarded-tcpip` arrives into the installed acceptor, the external dialer's bytes round trip — the reverse-runner consumer pattern end to end |
| `reverse_rows::real_sshd_cancel_tears_the_listen_down_and_unknown_cancels_refuse` | tunnel (+all) | explicit-port listen actually bound; the listener handle's `cancel()` frees the **real** bound port (later dials refused); a stateless cancel of the unknown listener is `ListenRefused`; connection stays usable |
| `declines::real_connection_sftp_placeholder_declines_with_the_connection_intact` | **every build** | the OQ-13 placeholder against a real authenticated connection: typed decline before any wire op, connection intact |
| `declines::real_presented_key_mismatch_hard_declines_and_the_right_pin_dials` | **every build** | the wrong-pinned policy hard-declines the dial (`DialError::HostKeyRejected`), the policy log records the **real** fingerprint as rejected, and the correctly-pinned dial to the same server succeeds — the decline is the policy's decision, not the server's |
| `unfeatured_direct_decline::real_server_refused_direct_tcpip_declines_and_the_connection_stays_usable` | tunnel+session (all-features) | a real server with `AllowTcpForwarding no` refuses the typed open → `OpenDeclined(Prohibited)`, and a following exec is served — the connection-stays-usable fact end to end |
**Decisions recorded** (for the wave-5 gate):
- The "connection stays usable" decline row is gated `tunnel` **and** `session` (rides
`--all-features` only): the post-decline exec is the stays-usable proof's vehicle, and exec is
a `session`-gated op. Under tunnel-only builds the row is absent; the fake-double mirror
(`tests/client_ops.rs`) carries the tunnel-only stays-usable rows.
- The sftp and host-key-mismatch rows run in **every** build (feature-free ops on
`SshConnection`), so even the no-feature matrix build exercises two real-server rows.
- The fixture skips are honest: no `sshd` in the environment / no `ssh-keygen` / spawn refusal /
never-listening (the server's `-e` log tail carried in the reason) — never silently green.
**Findings**: no src/ changes needed — every observed issue was e2e-graph local
(compile/fmt-level only). No bookings for the gate beyond the decisions above.
## Summary
> To be filled on completion
Completed 2026-10-30. The client-direction real-tool e2e landed as `tests/e2e_client.rs` (7 rows,
fixture shape above): exec (exit-status parity, nonzero through real reaping), the direct-tcpip
dial, the `-R` leg (port-0 mint + accepted arrival + data leg + cancel tearing the real listen
down + unknown-cancel `ListenRefused`), and the three decline rows (sftp placeholder, host-key
mismatch hard-decline with the real presented fingerprint on record, server-refused direct-tcpip
with the connection staying usable). Verification: full `scripts/matrix.sh` run green — `cargo
test` per build (none / git / session / tunnel / sftp / --all-features; the new binary compiles
in every build and runs only its rows' builds: 2 / 2 / 3 / 5 / 2 / 7 rows), both clippy runs
`-D warnings` clean, fmt clean, `cargo doc --no-deps` clean; plus 10 consecutive all-features
e2e_client runs green (no flakes; per-row workdirs + the e2e-server's port allocator carried
over). Skips are reasoned prints only; on this host sshd 9.6p1 is present, so every row ran.