Repository navigation
feat(ssh_config): wire the parsed-but-unused ssh_config keywords #281
Description
Part of the OpenSSH drop-in compatibility epic.
Problem
bssh's ssh_config parser accepts roughly 70 keywords, but the resolved host config is barely consumed. Of 81 fields on the config struct, only about 10 are read anywhere outside src/ssh/ssh_config/: hostname, user, port, identity files, StrictHostKeyChecking, ProxyJump, Compression, AddressFamily, ServerAliveInterval and ServerAliveCountMax.
Everything else is parsed, validated, merged, and discarded. A user writes a directive, bssh accepts it without complaint, and the behavior does not change. Two instances were severe enough to get their own issues (UserKnownHostsFile and ProxyCommand), but the pattern is the general case, not the exception.
The following fields are assigned by the resolver and never read: add_keys_to_agent, batch_mode, bind_interface, ca_signature_algorithms, certificate_files, check_host_ip, clear_all_forwardings, connection_attempts, control_master, control_path, control_persist, dynamic_forward, enable_ssh_keysign, escape_char, exit_on_forward_failure, fingerprint_hash, fork_after_authentication, forward_agent, forward_x11, forward_x11_timeout, forward_x11_trusted, gateway_ports, global_known_hosts_file, gssapi_authentication, hash_known_hosts, host_key_algorithms, host_key_alias, hostbased_accepted_algorithms, hostbased_authentication, identities_only, identity_agent, ipqos, kex_algorithms, keyboard_interactive_authentication, known_hosts_command, local_command, local_forward, log_level, macs, no_host_authentication_for_localhost, number_of_password_prompts, password_authentication, permit_local_command, permit_remote_open, preferred_authentications, proxy_command, proxy_use_fdpass, pubkey_accepted_algorithms, pubkey_authentication, rekey_limit, remote_command, remote_forward, request_tty, required_rsa_size, send_env, session_type, stdin_null, syslog_facility, tcp_keep_alive, update_host_keys, user_known_hosts_file, verify_host_key_dns, visual_host_key.
Scope
This is a tracking issue. Wire the keywords in the order the regression suite exercises them, and split out any individual keyword that turns out to be large enough to deserve its own issue.
First wave, each directly blocking a named regress test:
-
SendEnvandSetEnv(envpass) -
LocalCommandandPermitLocalCommand(localcommand) -
RequestTTYandSessionType(ssh-tty,match-subsystem) -
RemoteCommand(percent,host-expand) -
LocalForward,RemoteForward,DynamicForward,ClearAllForwardings,ExitOnForwardFailure(forwarding,dynamic-forward,forward-control) -
Ciphers,MACs,KexAlgorithms,HostKeyAlgorithms,PubkeyAcceptedAlgorithms(try-ciphers,kextype,keytype,limit-keytype,integrity) -
IdentitiesOnly,CertificateFile,PreferredAuthentications,PubkeyAuthentication,PasswordAuthentication,NumberOfPasswordPrompts,BatchMode(multipubkey,cert-file,cert-userkey,key-options) -
RekeyLimit(rekey) -
CheckHostIP,UpdateHostKeys,HashKnownHosts,KnownHostsCommand,VerifyHostKeyDNS(knownhosts,knownhosts-command,hostkey-rotate) -
ConnectionAttempts,TCPKeepAlive,IPQoS,BindAddress,BindInterface
Second wave, no regress coverage but user-facing: AddKeysToAgent, ForwardAgent, IdentityAgent, EscapeChar, FingerprintHash, VisualHostKey, LogLevel, SyslogFacility, StdinNull, ForkAfterAuthentication, RequiredRSASize, GatewayPorts, PermitRemoteOpen.
Out of scope for this epic and to be rejected as unimplemented rather than silently accepted: GSSAPIAuthentication, HostbasedAuthentication, HostbasedAcceptedAlgorithms, EnableSSHKeysign, ForwardX11, ForwardX11Trusted, ForwardX11Timeout.
Implementation decomposition
- feat(ssh_config): add directive registry and transport runtime foundation #295 — registry, provenance, generic
-o, algorithms, retries, and TCP keepalive. Depends on fix(ssh_config): honor HostKeyAlias when looking up host keys #279 and fix(ssh_config): execute ProxyCommand instead of parsing and discarding it #280. - feat(ssh): enforce ssh_config authentication policy and user certificates #296 — authentication policy and user certificates. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295.
- feat(ssh): apply ssh_config environment, command, TTY, and session policy #297 — environment, local/remote command, TTY, and session policy. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295.
- feat(ssh): apply ssh_config forwarding directives to live connections #298 — live forwarding directives. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295 and feat(ssh): apply ssh_config environment, command, TTY, and session policy #297.
- feat(ssh): enforce known-host policy, DNS checks, and secure key rotation #299 — known-host policy, DNS checks, and secure key rotation. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295.
- feat(net): honor ssh_config source binding and IPQoS #300 — source binding and IPQoS. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295 and feat(ssh): apply ssh_config environment, command, TTY, and session policy #297.
- feat(ssh): enforce RekeyLimit through russh transport limits #301 — RekeyLimit transport enforcement. Depends on feat(ssh_config): add directive registry and transport runtime foundation #295.
Keep this tracker open until all seven implementation issues are merged, every first-wave directive has a behavior test, the registry invariant passes, and the named regress set is rerun against the integrated main branch.
Structural requirement
Whatever the outcome per keyword, the silently-parsed state must stop existing. Every keyword the parser accepts must either change behavior at runtime or produce a warning that names it as unimplemented. Add a test that walks the accepted-keyword table and asserts each entry falls into one of those two buckets, so a future keyword cannot be added in the parse-and-discard shape.
Acceptance criteria
- Each first-wave keyword is consumed at runtime and covered by a test asserting the behavior changes.
- Unimplemented keywords warn once, naming the keyword and the line, instead of being discarded.
- The accepted-keyword table has no entry that is neither consumed nor declared unimplemented, enforced by a test.
- The regress tests named against the first wave pass in the harness.
Part of #275.
Activity
feat: add SSH config runtime foundation (#295) (#302)
fix(ssh): finalize config keyword support invariant (#309)