Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ Versions follow [Semantic Versioning](https://semver.org/).

## Unreleased

### Features
- **OmnySSH asks for the login password when no key gets in.** A host without a working key and without a saved password — typically one imported from `~/.ssh/config` — failed with "SSH authentication failed" and offered no way in short of saving its password to disk. A terminal now asks the way `ssh` does, right in the tab (`user@host's password:`), and a file session asks in a dialog. Three tries, then the login fails; Ctrl+C or Cancel ends it. The password is kept in memory until you quit, never written to disk, and only once the server has accepted it; the dashboard, and any tunnel on that host, pick it up and connect on their own — they never ask themselves. The first time OmnySSH meets a server, the prompt shows the host key it just recorded, so you can check it before typing. A saved password is still tried first, and a key whose passphrase is needed still comes before any password when the host names it as its identity file.

### Bug Fixes
- **Devices that only take the password by keyboard-interactive log in.** UniFi consoles such as the Dream Machine Pro, and other servers with `PasswordAuthentication no`, accept a password only through keyboard-interactive — which is what `ssh` and PuTTY fall back to without telling you. OmnySSH sent the saved password by the password method alone, so these hosts showed as offline with "SSH authentication failed". It now offers the password the other way too, and remembers which one a server takes, so a wrong password costs one failed login, not two. A server that asks for a one-time code instead of a password is told so rather than sent the password.
- **A silent or refusing SSH agent no longer leaves every host stuck on "connecting".** Every login asks the agent first, and nothing bounded that: an agent that accepts connections but never answers — as the launchd agent does on some macOS Tahoe setups — or one that turns a signature down (a declined 1Password or Secretive approval, a key added with `ssh-add -c`) held the login forever, password hosts included. The agent now gets five seconds to list its keys and a minute to sign, a refused signature moves on to the next method, and a signature you turned down is not asked for again by background reconnects.
- **The dashboard card says why a host is down.** A failed host showed a grey "offline" with the reason nowhere on screen. The card now shows it — "SSH connection failed: Connection refused", an authentication failure, a timeout — hidden in streamer mode like the tunnel's reason. The terminal app keeps the whole cause in the host's detail view, and a terminal that fails to open no longer replaces its reason with "SSH session closed.".
- **`omny -v` no longer writes login passwords to its log.** At debug level the SSH library dumps the first login request it sends, and when a password was the first thing tried, that dump carried it. Those dumps are now kept out of the log whatever `RUST_LOG` asks for.
- **The Linux AppImage opens on current graphics drivers.** On distributions with a recent Mesa — Arch and CachyOS, Bazzite, Nobara — it aborted at launch with `Could not create default EGL display: EGL_BAD_PARAMETER` and never showed a window, and the software-rendering restart added in 1.1.2 ran into the same abort: the failure happens while the graphics driver is being loaded, before WebKit picks a renderer, so no WebKit setting can reach it. The cause was the bundle itself. It carried the build machine's own Wayland and X client libraries and put them ahead of yours on the library path, so your Mesa was loaded against a libwayland older than the one it is built against and a symbol it needs was missing. Those ten libraries are no longer packed into the AppImage; your system's copies are used, as they already were for libX11. The `.deb`, `.rpm`, macOS and Windows builds were never affected.
- **Keys with a passphrase work.** A host whose identity file is encrypted failed with a bare "authentication failed", because the key was always read without a passphrase. Both apps now ask for it, once per key, and keep it in memory until you quit — it is never written to disk. The dashboard and any tunnel waiting on that key reconnect as soon as it is unlocked; a terminal or file session that stopped on it has to be opened again. A wrong passphrase says so, a key changed on disk is asked for again, and hosts behind a `ProxyJump` bastion are asked the same way. Cancelling the prompt keeps it away until you open a terminal or file session that needs the key. The host's Password field stays the login password, not the key passphrase.

Expand Down
11 changes: 11 additions & 0 deletions crates/omnyssh-core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ pub enum CoreEvent {
/// A private key is encrypted and no passphrase is cached for it yet.
/// Frontends prompt once per key path and call [`crate::ssh::identity::unlock`].
KeyPassphraseRequired { host_name: HostId, key_path: String },
/// A connection to `host_name` waits for the login password of `login`
/// (`user@host`). Frontends answer with [`crate::ssh::password::answer`];
/// `retry` says the previous one was refused, and `new_host_key` is the
/// fingerprint of a host key first seen on this connection.
PasswordRequired {
request_id: u64,
host_name: HostId,
login: String,
retry: bool,
new_host_key: Option<String>,
},

// -----------------------------------------------------------------------
// Update checker events
Expand Down
13 changes: 10 additions & 3 deletions crates/omnyssh-core/src/ssh/key_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use tokio::time;
use tracing::{error, info, warn};

use crate::ssh::client::Host;
use crate::ssh::session::{self, SshSession};
use crate::ssh::session::{self, Passwords, SshSession};

// ---------------------------------------------------------------------------
// Constants
Expand Down Expand Up @@ -662,7 +662,9 @@ async fn setup_key_internal(
test_host.identity_file = Some(private_key_path.to_string_lossy().to_string());
test_host.password = None; // Force key-only auth.

match time::timeout(verify_timeout, SshSession::connect(&test_host)).await {
// Keys only: a password typed earlier this session would let a broken key
// pass, and the next step turns password logins off.
match time::timeout(verify_timeout, connect_keys_only(&test_host)).await {
Ok(Ok(test_session)) => {
info!("Key authentication verified successfully!");
test_session.disconnect().await;
Expand Down Expand Up @@ -763,7 +765,7 @@ async fn setup_key_internal(
if let Some(ref tx) = progress_tx {
let _ = tx.send(KeySetupStep::FinalCheck).await;
}
match time::timeout(verify_timeout, SshSession::connect(&test_host)).await {
match time::timeout(verify_timeout, connect_keys_only(&test_host)).await {
Ok(Ok(final_session)) => {
info!("Final verification passed! Key setup complete.");
final_session.disconnect().await;
Expand All @@ -790,6 +792,11 @@ async fn setup_key_internal(
}
}

/// Connects with the host's keys and nothing else.
async fn connect_keys_only(host: &Host) -> Result<SshSession> {
SshSession::connect_with(host, Passwords::KeysOnly).await
}

/// Attempts to rollback sshd_config to the most recent OmnySSH backup.
async fn emergency_rollback(session: &SshSession) -> Result<()> {
warn!("Attempting emergency rollback of sshd_config");
Expand Down
1 change: 1 addition & 0 deletions crates/omnyssh-core/src/ssh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod identity;
pub mod jump;
pub mod key_setup;
pub mod metrics;
pub mod password;
pub mod pool;
pub mod probe;
pub mod pty;
Expand Down
Loading
Loading