Skip to content

fix(ssh): ensure environment variable algorithms are always honored#473

Merged
billchurch merged 2 commits intomainfrom
fix/ensure-env-var-algorithms-honored
Jan 27, 2026
Merged

fix(ssh): ensure environment variable algorithms are always honored#473
billchurch merged 2 commits intomainfrom
fix/ensure-env-var-algorithms-honored

Conversation

@billchurch
Copy link
Copy Markdown
Owner

@billchurch billchurch commented Jan 27, 2026

Summary

Ensures user-provided SSH algorithm environment variables (e.g., WEBSSH2_SSH_ALGORITHMS_KEX) are always honored by WebSSH2.

Problem

Problem reported that SSH algorithm environment variables were ignored on first connections but worked on reconnects. Investigation revealed:

  • The ssh2 library uses its own DEFAULT_KEX algorithms when none are explicitly passed to client.connect()
  • ssh2's DEFAULT_KEX only includes modern algorithms (curve25519, ecdh-nistp*, etc.)
  • ssh2's SUPPORTED_KEX includes legacy algorithms like diffie-hellman-group14-sha1
  • When algorithms weren't explicitly passed, ssh2 fell back to its defaults, ignoring user configuration

This caused connections to legacy SSH servers (e.g., those only supporting diffie-hellman-group14-sha1) to fail even when users correctly configured the algorithm via environment variables.

Root Cause

In app/services/ssh/ssh-service.ts, the buildConnectConfig() method only passed algorithms to ssh2 when config.algorithms !== undefined. In edge cases where the connection config lacked algorithms, ssh2 would use its own defaults instead of the server's configured defaults (which honor env vars).

Solution

Always explicitly pass algorithms to ssh2, using server config defaults as fallback:

const serverAlgorithms = this.deps.config.ssh.algorithms
const algorithmsToUse = config.algorithms ?? {
  kex: serverAlgorithms.kex,
  cipher: serverAlgorithms.cipher,
  serverHostKey: serverAlgorithms.serverHostKey,
  hmac: serverAlgorithms.hmac,
  compress: serverAlgorithms.compress
}

if (config.algorithms === undefined) {
  logger('WARNING: No algorithms in connection config, using server defaults')
}

connectConfig.algorithms = algorithmsToUse

Changes

File Change
app/services/ssh/ssh-service.ts THE FIX: Always pass algorithms with fallback + warning log
app/config.ts Debug logging for config loading timeline
app/socket/adapters/ssh-config.ts Per-connection algorithm logging
index.ts Startup verification logging
tests/test-utils.ts Fix mock config to include ssh.algorithms
tests/unit/socket-v2-test-utils.ts Fix mock config to include ssh.algorithms
tests/unit/socket-v2-exec-edge-cases.vitest.ts Fix test expectation to match actual behavior

Verification

1. Test with Custom Algorithms

WEBSSH2_SSH_ALGORITHMS_KEX="ecdh-sha2-nistp256,diffie-hellman-group14-sha1" DEBUG=webssh2:* npm run start

Expected: Config loading should show env vars being read, startup log should show custom KEX algorithms.

2. Test with Legacy SSH Server

WEBSSH2_SSH_ALGORITHMS_KEX="diffie-hellman-group14-sha1" DEBUG=webssh2:* npm run start

Connect to a legacy SSH server - should succeed now.

3. Verify Fallback Warning

The warning WARNING: No algorithms in connection config, using server defaults should NOT appear in normal operation. If it does, it indicates a bug in the config passing flow.

Test plan

  • Build passes
  • Lint passes
  • Type checking passes
  • All 904 unit/integration tests pass
  • Manual test with custom algorithm env vars
  • Manual test with legacy SSH server (if available)

The ssh2 library uses its own DEFAULT_KEX algorithms when none are
explicitly passed to client.connect(). This caused user-provided
algorithm configuration via environment variables (e.g.,
WEBSSH2_SSH_ALGORITHMS_KEX) to be silently ignored in edge cases.

Changes:
- Always pass algorithms to ssh2 with fallback to server config defaults
- Add debug logging for config loading timeline and algorithm values
- Add per-connection algorithm logging in ssh-config adapter
- Add startup verification logging with algorithm summary
- Fix test mocks to include proper ssh.algorithms configuration

The fix ensures legacy SSH servers (e.g., those only supporting
diffie-hellman-group14-sha1) can connect when users configure
the appropriate algorithms via environment variables.
Replace async mainAsync wrapper with top-level await for cleaner
initialization code. Use direct export...from syntax for re-exports.
Addresses SonarLint rules S7785 and S7763.
@sonarqubecloud
Copy link
Copy Markdown

@billchurch billchurch merged commit 9a9077b into main Jan 27, 2026
7 checks passed
@billchurch billchurch deleted the fix/ensure-env-var-algorithms-honored branch January 27, 2026 18:50
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