# How DBX Handles SSH Tunnel Connections with Password Authentication

> Learn how DBX handles SSH tunnel connections with password authentication by retrieving encrypted secrets and using Rust for secure, asynchronous authentication. Debug credential failures with clear error messages.

- Repository: [skyler/dbx](https://github.com/t8y2/dbx)
- Tags: how-to-guide
- Published: 2026-07-09

---

**TLDR:** DBX implements SSH password authentication by retrieving encrypted credentials from its secret store, constructing a transport-layer description in TypeScript, and executing asynchronous password authentication in Rust using `session.authenticate_password()`, with explicit timeout handling and clear error messages for credential failures.

DBX is an open-source database client designed to simplify secure connections to remote infrastructure. When key-based authentication is unavailable, DBX supports **SSH tunnel connections with password authentication** through a secure, multi-layered architecture spanning its TypeScript frontend and Rust core.

## Retrieving Passwords from Secure Storage

DBX never stores SSH passwords in plain text. Instead, the system looks up secrets using the key pattern `ssh_tunnels.<hop-id>.password` or the legacy top-level key `ssh_password`. According to the TypeScript builder in [`packages/node-core/src/connections.ts`](https://github.com/t8y2/dbx/blob/main/packages/node-core/src/connections.ts) (lines 191-194), this retrieval happens before the connection is established.

The retrieved string is stored in the `SshTunnelConfig.password` field, defined in [`crates/dbx-core/src/models/connection.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/models/connection.rs). This field is deserialized from the connection JSON and kept in memory only, never written to logs or the UI.

## Building the Transport Layer

When a connection profile has `ssh_enabled: true` and includes one or more `ssh_tunnels`, DBX constructs a **layer description** array. In [`packages/node-core/src/connections.ts`](https://github.com/t8y2/dbx/blob/main/packages/node-core/src/connections.ts) (lines 156-157), the TypeScript builder creates objects with `type: "ssh"` for each hop:

```typescript
if (config.ssh_enabled && Array.isArray(config.ssh_tunnels) && config.ssh_tunnels.length > 0) {
  // Each hop becomes a layer of type "ssh"
  layers.push(...config.ssh_tunnels.map((hop) => ({
    type: "ssh" as const,
    ...hop,
  })));
}

```

This layered approach allows DBX to chain multiple SSH hops if needed.

## Authenticating with the SSH Server

The actual tunnel implementation resides in [`crates/dbx-core/src/db/ssh_tunnel.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/db/ssh_tunnel.rs). When DBX spawns an async `SshTunnel` object, it checks the `auth_method` field. If empty or explicitly set to `"password"`, DBX executes password authentication using the `ssh_password` credential retrieved earlier:

```rust
if auth_method.is_empty() && !ssh_password.is_empty() || auth_method == "password" {
    let auth_res = tokio::time::timeout(
        connect_timeout,
        session.authenticate_password(ssh_user, ssh_password)
    )
    .await
    .map_err(|_| "SSH password authentication timed out".to_string())?;

    if !auth_res {
        return Err("SSH password authentication failed".to_string());  // line 144
    }
}

```

The `tokio::time::timeout` wrapper ensures the connection attempt respects the configured `connect_timeout`, preventing indefinite hangs on unresponsive servers.

## Configuration Example

To configure password authentication for an SSH tunnel, include the `auth_method` and `password` fields in your connection profile:

```json
{
  "id": "my-postgres-db",
  "driver": "postgres",
  "host": "127.0.0.1",
  "port": 5432,
  "username": "dbuser",
  "password": "dbsecret",
  "ssh_enabled": true,
  "ssh_tunnels": [
    {
      "id": "bastion-1",
      "host": "bastion.example.com",
      "port": 22,
      "username": "sshuser",
      "auth_method": "password",
      "password": "my-ssh-password"
    }
  ]
}

```

The UI automatically encrypts and stores the `"password"` field in DBX's secret store (SQLite/SQLCipher), ensuring the plain text never appears on disk.

## Error Handling and Security

DBX provides clear error propagation for authentication failures. If the server rejects the password, the Rust core returns `"SSH password authentication failed"` (line 144 in [`crates/dbx-core/src/db/ssh_tunnel.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/db/ssh_tunnel.rs)). If the connection times out, users receive `"SSH password authentication timed out"`.

Passwords remain encrypted in DBX's secret storage and only travel in memory between the TypeScript frontend and Rust core. This design allows seamless fallback to **key-based authentication** when configured, without requiring external SSH tooling.

## Summary

- **Secure retrieval**: DBX looks up SSH passwords using the `ssh_tunnels.<hop-id>.password` key pattern or legacy `ssh_password`, storing them encrypted in SQLite/SQLCipher.
- **Transport layering**: The TypeScript builder in [`packages/node-core/src/connections.ts`](https://github.com/t8y2/dbx/blob/main/packages/node-core/src/connections.ts) constructs SSH layer descriptions for each hop defined in `ssh_tunnels`.
- **Rust authentication**: The `SshTunnel` implementation in [`crates/dbx-core/src/db/ssh_tunnel.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/db/ssh_tunnel.rs) calls `session.authenticate_password()` with timeout protection.
- **Clear error messages**: Authentication failures and timeouts return explicit error strings to the UI for user feedback.
- **Memory-safe handling**: Credentials are never written to logs or disk in plain text, existing only in memory during the connection attempt.

## Frequently Asked Questions

### Where does DBX store SSH passwords?

DBX stores SSH passwords in its encrypted secret store using SQLite/SQLCipher. Passwords are retrieved using the key pattern `ssh_tunnels.<hop-id>.password` or the legacy `ssh_password` key, and they only exist in memory during the connection process, never appearing in logs or configuration files on disk.

### What happens if SSH password authentication times out?

If the SSH server does not respond to the authentication attempt within the configured `connect_timeout` period, DBX raises a `"SSH password authentication timed out"` error. This timeout is enforced by `tokio::time::timeout` in [`crates/dbx-core/src/db/ssh_tunnel.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/db/ssh_tunnel.rs), preventing the application from hanging indefinitely on unresponsive servers.

### Can I mix password and key-based authentication for different tunnels?

Yes. DBX determines the authentication method per tunnel hop by checking the `auth_method` field. You can set `"password"` for one hop and use key-based authentication (by omitting the password and configuring SSH keys) for another, allowing flexible configurations for complex network topologies with multiple bastion hosts.

### How does DBX handle legacy SSH password configurations?

DBX checks both the modern `ssh_tunnels.<hop-id>.password` path and the legacy `ssh_password` field when deserializing the connection configuration in [`crates/dbx-core/src/models/connection.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/models/connection.rs). This ensures existing configurations continue to function while allowing users to migrate to the newer hierarchical format that supports multiple hops.