Support host-config login users
This commit is contained in:
@@ -69,7 +69,7 @@ Must work in v1:
|
|||||||
- Encrypted OpenSSH private-key authentication with prompt.
|
- Encrypted OpenSSH private-key authentication with prompt.
|
||||||
- `~/.ssh/config` host aliases for `Host`, `HostName`, `User`, `Port`,
|
- `~/.ssh/config` host aliases for `Host`, `HostName`, `User`, `Port`,
|
||||||
`IdentityFile`, `ProxyJump`, `UserKnownHostsFile`, and `IdentitiesOnly`.
|
`IdentityFile`, `ProxyJump`, `UserKnownHostsFile`, and `IdentitiesOnly`.
|
||||||
- Dosh host config overrides for Dosh-specific UDP host/port/auth policy.
|
- Dosh host config overrides for user, Dosh-specific UDP host/port/auth policy.
|
||||||
- Clear host-key trust and mismatch errors.
|
- Clear host-key trust and mismatch errors.
|
||||||
- Clear auth failure errors that name the attempted key source.
|
- Clear auth failure errors that name the attempted key source.
|
||||||
- Stable reconnect after sleep, network switch, NAT rebinding, and server packet loss.
|
- Stable reconnect after sleep, network switch, NAT rebinding, and server packet loss.
|
||||||
|
|||||||
+38
-5
@@ -173,7 +173,8 @@ async fn main() -> Result<()> {
|
|||||||
.get(&requested_server)
|
.get(&requested_server)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let server = host.ssh.clone().unwrap_or_else(|| requested_server.clone());
|
let raw_server = host.ssh.clone().unwrap_or_else(|| requested_server.clone());
|
||||||
|
let server = ssh_with_user(&raw_server, host.user.as_deref());
|
||||||
let startup_command = startup_command(&args.command).or_else(|| {
|
let startup_command = startup_command(&args.command).or_else(|| {
|
||||||
host.default_command
|
host.default_command
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@@ -490,7 +491,8 @@ fn run_trust_command(config: &dosh::config::ClientConfig, args: &Args) -> Result
|
|||||||
|
|
||||||
let hosts = load_hosts_config(None).unwrap_or_default();
|
let hosts = load_hosts_config(None).unwrap_or_default();
|
||||||
let host_config = hosts.hosts.get(host).cloned().unwrap_or_default();
|
let host_config = hosts.hosts.get(host).cloned().unwrap_or_default();
|
||||||
let server = host_config.ssh.clone().unwrap_or_else(|| host.to_string());
|
let raw_server = host_config.ssh.clone().unwrap_or_else(|| host.to_string());
|
||||||
|
let server = ssh_with_user(&raw_server, host_config.user.as_deref());
|
||||||
let ssh_port = args.ssh_port.or(host_config.ssh_port).or(config.ssh_port);
|
let ssh_port = args.ssh_port.or(host_config.ssh_port).or(config.ssh_port);
|
||||||
let ssh_auth_command = args
|
let ssh_auth_command = args
|
||||||
.ssh_auth_command
|
.ssh_auth_command
|
||||||
@@ -531,7 +533,8 @@ async fn run_doctor_command(config: &dosh::config::ClientConfig, args: &Args) ->
|
|||||||
.ok_or_else(|| anyhow!("usage: dosh doctor <host>"))?;
|
.ok_or_else(|| anyhow!("usage: dosh doctor <host>"))?;
|
||||||
let hosts = load_hosts_config(None).unwrap_or_default();
|
let hosts = load_hosts_config(None).unwrap_or_default();
|
||||||
let host_config = hosts.hosts.get(&requested).cloned().unwrap_or_default();
|
let host_config = hosts.hosts.get(&requested).cloned().unwrap_or_default();
|
||||||
let server = host_config.ssh.clone().unwrap_or_else(|| requested.clone());
|
let raw_server = host_config.ssh.clone().unwrap_or_else(|| requested.clone());
|
||||||
|
let server = ssh_with_user(&raw_server, host_config.user.as_deref());
|
||||||
let ssh_port = args.ssh_port.or(host_config.ssh_port).or(config.ssh_port);
|
let ssh_port = args.ssh_port.or(host_config.ssh_port).or(config.ssh_port);
|
||||||
let dosh_port = args
|
let dosh_port = args
|
||||||
.dosh_port
|
.dosh_port
|
||||||
@@ -994,6 +997,9 @@ fn run_import_ssh(config: &dosh::config::ClientConfig, aliases: &[String]) -> Re
|
|||||||
raw.push_str(&format!("ssh = {}\n", toml_string(alias)));
|
raw.push_str(&format!("ssh = {}\n", toml_string(alias)));
|
||||||
raw.push_str(&format!("dosh_host = {}\n", toml_string(&udp_host)));
|
raw.push_str(&format!("dosh_host = {}\n", toml_string(&udp_host)));
|
||||||
raw.push_str(&format!("port = {}\n", config.dosh_port));
|
raw.push_str(&format!("port = {}\n", config.dosh_port));
|
||||||
|
if let Some(user) = &ssh_config.user {
|
||||||
|
raw.push_str(&format!("user = {}\n", toml_string(user)));
|
||||||
|
}
|
||||||
if let Some(port) = ssh_config.port
|
if let Some(port) = ssh_config.port
|
||||||
&& port != 22
|
&& port != 22
|
||||||
{
|
{
|
||||||
@@ -1183,6 +1189,17 @@ fn ssh_username(server: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ssh_with_user(server: &str, user: Option<&str>) -> String {
|
||||||
|
let Some(user) = user else {
|
||||||
|
return server.to_string();
|
||||||
|
};
|
||||||
|
if user.is_empty() || ssh_username(server).is_some() || server.starts_with("ssh://") {
|
||||||
|
server.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{user}@{server}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct SshConfig {
|
struct SshConfig {
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
@@ -2964,8 +2981,8 @@ mod tests {
|
|||||||
DynamicForward, FrameBuffer, LocalForward, Predictor, RemoteForward, SshConfig,
|
DynamicForward, FrameBuffer, LocalForward, Predictor, RemoteForward, SshConfig,
|
||||||
auth_allows, load_first_native_identity_with_prompt, parse_dynamic_forward,
|
auth_allows, load_first_native_identity_with_prompt, parse_dynamic_forward,
|
||||||
parse_local_forward, parse_remote_forward, parse_ssh_config, raw_contains_host_table,
|
parse_local_forward, parse_remote_forward, parse_ssh_config, raw_contains_host_table,
|
||||||
recv_response_until, requested_env, ssh_destination_host, ssh_username, startup_command,
|
recv_response_until, requested_env, ssh_destination_host, ssh_username, ssh_with_user,
|
||||||
toml_bare_key_or_quoted,
|
startup_command, toml_bare_key_or_quoted,
|
||||||
};
|
};
|
||||||
use dosh::config::{ClientConfig, HostConfig};
|
use dosh::config::{ClientConfig, HostConfig};
|
||||||
use dosh::native::EnvVar;
|
use dosh::native::EnvVar;
|
||||||
@@ -3108,6 +3125,22 @@ mod tests {
|
|||||||
assert_eq!(ssh_username("example.com"), None);
|
assert_eq!(ssh_username("example.com"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn applies_host_config_user_to_plain_ssh_destination() {
|
||||||
|
assert_eq!(
|
||||||
|
ssh_with_user("example.com", Some("palav")),
|
||||||
|
"palav@example.com"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ssh_with_user("root@example.com", Some("palav")),
|
||||||
|
"root@example.com"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ssh_with_user("ssh://example.com", Some("palav")),
|
||||||
|
"ssh://example.com"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auth_preference_accepts_auto_or_named_method() {
|
fn auth_preference_accepts_auto_or_named_method() {
|
||||||
assert!(auth_allows("native,ssh", "native"));
|
assert!(auth_allows("native,ssh", "native"));
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ pub struct ClientConfig {
|
|||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct HostConfig {
|
pub struct HostConfig {
|
||||||
pub ssh: Option<String>,
|
pub ssh: Option<String>,
|
||||||
|
pub user: Option<String>,
|
||||||
pub dosh_host: Option<String>,
|
pub dosh_host: Option<String>,
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub ssh_port: Option<u16>,
|
pub ssh_port: Option<u16>,
|
||||||
|
|||||||
Reference in New Issue
Block a user