Skip to content

fix(ios): test the SSH tunnel against real OpenSSH servers and name its failures - #3687

Open
datlechin wants to merge 4 commits into
mainfrom
test/ios-ssh-tunnel-openssh-matrix
Open

datlechin wants to merge 4 commits into
mainfrom
test/ios-ssh-tunnel-openssh-matrix

Conversation

@datlechin

@datlechin datlechin commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Summary

  • apple/scripts/ssh-test-servers.sh and scripts/lib/openssh-fixture.sh: pinned OpenSSH 10.3 and 9.9 servers (PQ-only, MAC-only, RSA SHA-2, keyboard-interactive, TOTP, MaxAuthTries, two hops, loss), published on 127.0.0.1 only. sftp-test-server.sh uses the same pin and banner wait.
  • apple/scripts/probes/check-ssh-tunnel-matrix.sh: builds the iOS tunnel sources for the simulator against the shipped libssh2 and runs 98 cells through a Redis PING, two-hop chains included. A probe, not CI: no CI job has Docker.
  • iOS SSH errors are named instead of "error -N": no common key exchange, host key, cipher or MAC (with the server software and TablePro's list), FIDO key, passphrase missing or wrong, second sign-in step, unanswerable prompt, unsupported auth method.
  • iOS password sign-in falls back to keyboard-interactive, answering only prompts that read as a password.
  • iOS auth reads the server's method list first. An encrypted key goes without its .pub (unless it is a certificate), so a wrong passphrase fails locally.
  • LibSSH2ChannelIO (both apps): a transport error ends the relay as a hangup, not a channel close. An iOS tunnel relaying data now stops at once on a dead server instead of after about 37 s.
  • SSHChannelRelay (both apps) sends with MSG_NOSIGNAL: a client closing during channel data no longer kills the process with SIGPIPE. feat(ios): open connections through SSH jump hosts #3683 landed the same send flag; its test and this PR's are one case now.
  • Jump hosts (feat(ios): open connections through SSH jump hosts #3683): a named error at a hop comes back as jumpHostFailed, which names the hop and keeps the words. One by-case classifier, ErrorClassifier.sshTunnelFailure, holds the jump host cases and this PR's.
  • iOS tunnel: sockets closed after libssh2_session_free; channel free retried until done (SSHChannelFreePump) instead of leaving an unsent CLOSE; channel open polls 100 ms until its 6 s deadline, which fixed 30 clients on one tunnel.
  • TableProSSHTransport: SSHNegotiationFailure, SSHPrivateKeyTraits, SSHAuthMethods, KeyboardInteractivePromptKind (the Mac's KBDINTPromptType, moved).
  • Docs: docs(ios): sign in to keyboard-interactive SSH servers with a password docs#57.

PQ-only server, before: SSH handshake failed: Handshake failed (error -5). After: TablePro and the SSH server have no key exchange or host key method in common. The server runs OpenSSH_10.3. TablePro supports these key exchange methods: curve25519-sha256, ...

Test plan

  • Fail on main, pass here: SSHChannelRelayTests (transport failure, closed client), SSHNegotiationFailureTests, SSHPrivateKeyTraitsTests, SSHAuthMethodsTests, KeyboardInteractivePromptKindTests, SSHChannelFreePumpTests, LibSSH2ChannelIOTests, LibSSH2AuthenticationTests, SSHTunnelErrorClassifierTests.
  • SSHTunnelFactoryTests.unsupportedAuthMethodDiscards (Keyboard Interactive since feat(ios): open connections through SSH jump hosts #3683) now expects authMethodUnsupported(.keyboardInteractive). New: namedHopFailureNamesTheHop. KeyboardInteractiveContextTests gains only import TableProSSHTransport.
  • apple/scripts/probes/check-ssh-tunnel-matrix.sh on the simulator: 93 of 93 before feat(ios): open connections through SSH jump hosts #3683. After merging it: the two-hop, agent and loss cells 8 of 8 (a killed hop stops a relaying tunnel at once). On main the key, passphrase, FIDO, PQ, MAC and keyboard-interactive cells show only error numbers, and a relaying tunnel takes about 37 s to notice a dead server.
  • The faster dead-server notice on the Mac is inferred from the shared code: the matrix has no Mac cells yet.

Part of #3662

Not in this PR

  • macOS tunnel cells and the Linux suite additions.
  • Connecting to a PQ-only server: libssh2 1.11.1 has no ML-KEM or sntrup, and ML-KEM in libssh2 master needs OpenSSL 3.5.
  • A dead-tunnel signal from IOSSSHProvider to the session, as the Mac's onDeath does.
  • iOS sets no TCP keepalive, so an idle tunnel waits on the 30 s keep-alive.
  • The same teardown, channel free and channel-open wait defects in the macOS tunnel.
  • libssh2 1.11.1 ignores the partial-success flag, so a second factor is read from a re-read method list. With MaxAuthTries 1 that extra try turns it into a plain failure.
  • FIDO signing and TOTP on iOS.

…penssh-matrix

# Conflicts:
#	apple/TableProMobile/TableProMobile/Localizable.xcstrings
…penssh-matrix

# Conflicts:
#	apple/Packages/TableProCore/Sources/TableProSSHTransport/SSHChannelRelay.swift
#	apple/Packages/TableProCore/Tests/TableProSSHTransportTests/SSHChannelRelayTests.swift
#	apple/TableProMobile/TableProMobile/Helpers/AppError.swift
#	apple/TableProMobile/TableProMobile/Localizable.xcstrings
#	apple/TableProMobile/TableProMobile/SSH/SSHTunnel.swift
#	apple/TableProMobile/TableProMobile/SSH/SSHTunnelFactory.swift
#	apple/TableProMobile/TableProMobile/SSH/SSHTunnelStages.swift
#	apple/TableProMobile/TableProMobileTests/SSH/SSHTunnelFactoryTests.swift
@datlechin
datlechin disabled auto-merge October 20, 2026 04:33

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant