Clarify proxied SSH UDP requirements
ci / test (push) Has been cancelled
ci / fuzz-smoke (push) Has been cancelled
ci / windows-client (push) Has been cancelled
ci / package-release (linux-x86_64, ubuntu-latest) (push) Has been cancelled
ci / package-release (macos-aarch64, macos-14) (push) Has been cancelled
ci / package-release (macos-x86_64, macos-13) (push) Has been cancelled
ci / package-release (windows-x86_64, windows-latest) (push) Has been cancelled
ci / remote-bench (push) Has been cancelled
ci / test (push) Has been cancelled
ci / fuzz-smoke (push) Has been cancelled
ci / windows-client (push) Has been cancelled
ci / package-release (linux-x86_64, ubuntu-latest) (push) Has been cancelled
ci / package-release (macos-aarch64, macos-14) (push) Has been cancelled
ci / package-release (macos-x86_64, macos-13) (push) Has been cancelled
ci / package-release (windows-x86_64, windows-latest) (push) Has been cancelled
ci / remote-bench (push) Has been cancelled
This commit is contained in:
Generated
+1
-1
@@ -436,7 +436,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dosh"
|
name = "dosh"
|
||||||
version = "1.0.0-rc24"
|
version = "1.0.0-rc25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dosh"
|
name = "dosh"
|
||||||
version = "1.0.0-rc24"
|
version = "1.0.0-rc25"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ dosh_host = "prod.example.com"
|
|||||||
port = 50000
|
port = 50000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`ProxyJump` and `ProxyCommand` can be used for SSH setup, but the Dosh UDP
|
||||||
|
endpoint still has to be reachable directly from the client. Set `dosh_host` to
|
||||||
|
the reachable UDP hostname or IP when it differs from the SSH alias target.
|
||||||
|
|
||||||
## Rust Library
|
## Rust Library
|
||||||
|
|
||||||
Dosh exposes a Rust transport for encrypted, reconnecting application streams.
|
Dosh exposes a Rust transport for encrypted, reconnecting application streams.
|
||||||
|
|||||||
+68
-8
@@ -960,6 +960,14 @@ async fn run_doctor_for_host(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!("[info] native endpoint: {udp_host}:{dosh_port}");
|
println!("[info] native endpoint: {udp_host}:{dosh_port}");
|
||||||
|
if let Some(warning) = native_proxy_udp_warning(
|
||||||
|
&parsed_ssh_config,
|
||||||
|
requested_udp_host.as_deref(),
|
||||||
|
&udp_host,
|
||||||
|
dosh_port,
|
||||||
|
) {
|
||||||
|
println!("[warn] native endpoint: {warning}");
|
||||||
|
}
|
||||||
match resolve_addr(&udp_host, dosh_port) {
|
match resolve_addr(&udp_host, dosh_port) {
|
||||||
Ok(addr) => println!("[ok] udp resolve: {addr}"),
|
Ok(addr) => println!("[ok] udp resolve: {addr}"),
|
||||||
Err(err) => println!("[fail] udp resolve: {err:#}"),
|
Err(err) => println!("[fail] udp resolve: {err:#}"),
|
||||||
@@ -3532,6 +3540,25 @@ fn selected_udp_host(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ssh_config_uses_proxy(ssh_config: &SshConfig) -> bool {
|
||||||
|
ssh_config.proxy_jump.is_some() || ssh_config.proxy_command.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn native_proxy_udp_warning(
|
||||||
|
ssh_config: &SshConfig,
|
||||||
|
requested_udp_host: Option<&str>,
|
||||||
|
udp_host: &str,
|
||||||
|
dosh_port: u16,
|
||||||
|
) -> Option<String> {
|
||||||
|
if ssh_config_uses_proxy(ssh_config) && requested_udp_host.is_none() {
|
||||||
|
Some(format!(
|
||||||
|
"SSH uses ProxyJump/ProxyCommand, but Dosh UDP still needs a directly reachable host; set dosh_host if {udp_host}:{dosh_port} is not reachable from this client"
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ssh_username(server: &str) -> Option<String> {
|
fn ssh_username(server: &str) -> Option<String> {
|
||||||
let (user, _) = server.rsplit_once('@')?;
|
let (user, _) = server.rsplit_once('@')?;
|
||||||
if user.is_empty() {
|
if user.is_empty() {
|
||||||
@@ -7756,20 +7783,21 @@ mod tests {
|
|||||||
cleanup_stream_state, clear_cached_credentials, ensure_tui_safe_status_overlay,
|
cleanup_stream_state, clear_cached_credentials, ensure_tui_safe_status_overlay,
|
||||||
input_contains_focus_in, input_matches_escape, is_local_status_target,
|
input_contains_focus_in, input_matches_escape, is_local_status_target,
|
||||||
is_resume_response_for_client, latest_release_download_url,
|
is_resume_response_for_client, latest_release_download_url,
|
||||||
load_first_native_identity_with_prompt, parse_dynamic_forward, parse_escape_key,
|
load_first_native_identity_with_prompt, native_proxy_udp_warning, parse_dynamic_forward,
|
||||||
parse_local_forward, parse_remote_forward, parse_ssh_config, parse_update_options,
|
parse_escape_key, parse_local_forward, parse_remote_forward, parse_ssh_config,
|
||||||
post_submit_hold_duration, queue_pending_user_input, raw_contains_host_table,
|
parse_update_options, post_submit_hold_duration, queue_pending_user_input,
|
||||||
recv_response_until, refresh_live_addr, release_tag_download_url,
|
raw_contains_host_table, recv_response_until, refresh_live_addr, release_tag_download_url,
|
||||||
release_tag_from_effective_url, release_version_from_tag, render_status_clear,
|
release_tag_from_effective_url, release_version_from_tag, render_status_clear,
|
||||||
render_status_overlay, requested_env, resolved_startup_command, retire_stream_state,
|
render_status_overlay, requested_env, resolved_startup_command, retire_stream_state,
|
||||||
retransmit_stream_opens, rewrite_forward_command, selected_predict_mode, selected_udp_host,
|
retransmit_stream_opens, rewrite_forward_command, selected_predict_mode, selected_udp_host,
|
||||||
server_version_mismatch, should_flush_terminal_input_after_contact,
|
server_version_mismatch, should_flush_terminal_input_after_contact,
|
||||||
should_hold_during_startup_gate, should_hold_post_submit_input,
|
should_hold_during_startup_gate, should_hold_post_submit_input,
|
||||||
should_repaint_idle_alternate_screen, split_after_command_submit, ssh_command_target,
|
should_repaint_idle_alternate_screen, split_after_command_submit, ssh_command_target,
|
||||||
ssh_destination_host, ssh_username, ssh_with_user, startup_command, status_ssh_target,
|
ssh_config_uses_proxy, ssh_destination_host, ssh_username, ssh_with_user, startup_command,
|
||||||
strip_stale_mouse_reports, strip_terminal_focus_reports, terminal_private_mode_transition,
|
status_ssh_target, strip_stale_mouse_reports, strip_terminal_focus_reports,
|
||||||
toml_bare_key_or_quoted, unix_update_script, update_installer_url, update_version_status,
|
terminal_private_mode_transition, toml_bare_key_or_quoted, unix_update_script,
|
||||||
upsert_managed_block, valid_forward_host, vscode_safe_alias, windows_update_script,
|
update_installer_url, update_version_status, upsert_managed_block, valid_forward_host,
|
||||||
|
vscode_safe_alias, windows_update_script,
|
||||||
};
|
};
|
||||||
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
||||||
use dosh::native::EnvVar;
|
use dosh::native::EnvVar;
|
||||||
@@ -8141,6 +8169,38 @@ mod tests {
|
|||||||
assert!(selected_udp_host(Some("any"), "alias", &ssh).is_err());
|
assert!(selected_udp_host(Some("any"), "alias", &ssh).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssh_config_proxy_detection_covers_jump_and_command() {
|
||||||
|
assert!(!ssh_config_uses_proxy(&SshConfig::default()));
|
||||||
|
assert!(ssh_config_uses_proxy(&SshConfig {
|
||||||
|
proxy_jump: Some("bastion".to_string()),
|
||||||
|
..SshConfig::default()
|
||||||
|
}));
|
||||||
|
assert!(ssh_config_uses_proxy(&SshConfig {
|
||||||
|
proxy_command: Some("ssh bastion -W %h:%p".to_string()),
|
||||||
|
..SshConfig::default()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_proxy_udp_warning_requires_missing_explicit_dosh_host() {
|
||||||
|
let proxied = SshConfig {
|
||||||
|
proxy_jump: Some("bastion".to_string()),
|
||||||
|
..SshConfig::default()
|
||||||
|
};
|
||||||
|
let warning = native_proxy_udp_warning(&proxied, None, "internal.lan", 50000)
|
||||||
|
.expect("proxied SSH without dosh_host should warn");
|
||||||
|
assert!(warning.contains("ProxyJump/ProxyCommand"));
|
||||||
|
assert!(warning.contains("internal.lan:50000"));
|
||||||
|
assert!(
|
||||||
|
native_proxy_udp_warning(&proxied, Some("public.example.com"), "internal.lan", 50000)
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
native_proxy_udp_warning(&SshConfig::default(), None, "internal.lan", 50000).is_none()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_user_from_ssh_destination() {
|
fn parses_user_from_ssh_destination() {
|
||||||
assert_eq!(ssh_username("alice@example.com"), Some("alice".to_string()));
|
assert_eq!(ssh_username("alice@example.com"), Some("alice".to_string()));
|
||||||
|
|||||||
Reference in New Issue
Block a user