Порівняти коміти

...
1 Коміти
Автор SHA1 Повідомлення Дата
l0ng-ai 9497693454 fix(remote): trust SSH host keys and passwords on the host that connects
In a remote window (SSH, WSL, dev container) deployments, data source tests and run targets
connect from the server, but "Trust Host Key and Retry", a password entered for an SSH
configuration and the SSH settings page's checks went to the desktop's SSH manager. The server
then still didn't know the key ("no host key to trust" on the desktop), so a configuration
that worked in a local window could never connect from the remote one. The SSH configuration
commands are now served by the remote host too, like db_trust_ssh_host already was.

Fixes #3313
2026-11-14 16:09:24 +08:00
3 змінених файлів з 44 додано та 0 видалено

No files matched your search

+2
Переглянути файл
@@ -31,6 +31,7 @@ pub mod scaffold;
pub mod sdk;
pub mod search;
pub mod spell;
mod ssh;
pub mod syntax;
pub mod term;
pub mod tools;
@@ -95,6 +96,7 @@ pub fn register_all(d: &mut crate::rpc::Dispatcher) {
deploy::register(d);
docker::register(d);
db::register(d);
ssh::register(d);
}
impl Host {
+29
Переглянути файл
@@ -0,0 +1,29 @@
//! SSH configuration commands shared by the desktop shell's IPC commands and the remote
//! server ([`crate::rpc`]). A remote window connects from the server (deployment, database
//! tunnels, run targets, SSH sessions), so the host keys it trusts and the passwords it is
//! given belong to the server's [`ide_ssh::SshManager`], not the desktop's.
use crate::rpc::Dispatcher;
use ide_core::remote_settings::SshConfiguration;
pub(crate) fn register(d: &mut Dispatcher) {
d.add("ssh_test", |c| async move {
let (config, secret) = (c.arg::<SshConfiguration>("config")?, c.arg::<Option<String>>("secret")?);
c.host.ssh.test(&config, secret).await
});
d.add("ssh_set_secret", |c| async move {
let (id, secret, save) = (c.arg::<String>("id")?, c.arg::<Option<String>>("secret")?, c.arg::<bool>("save")?);
c.host.ssh.set_secret(&id, secret, save);
Ok(())
});
d.add("ssh_password_saved", |c| async move { Ok(c.host.ssh.password_saved(&c.arg::<String>("key")?)) });
d.add("ssh_needs_secret", |c| async move {
let ssh = &c.host.ssh;
Ok(ssh.needs_secret(&ssh.config(&c.arg::<String>("id")?)?))
});
d.add("ssh_trust_host", |c| async move { c.host.ssh.trust_host(&c.arg::<String>("id")?).await });
d.add("ssh_connected", |c| async move { Ok(c.host.ssh.connected()) });
d.add("ssh_disconnect", |c| async move {
c.host.ssh.disconnect(&c.arg::<String>("id")?);
Ok(())
});
}
+13
Переглянути файл
@@ -365,6 +365,13 @@ async fn local_deployment_runs_on_the_project_host() {
"db_table_page",
"db_apply_edits",
"db_console_path",
"ssh_test",
"ssh_set_secret",
"ssh_password_saved",
"ssh_needs_secret",
"ssh_trust_host",
"ssh_connected",
"ssh_disconnect",
] {
assert!(dispatcher.commands().contains(&cmd), "missing host command {cmd}");
}
@@ -373,6 +380,12 @@ async fn local_deployment_runs_on_the_project_host() {
tokio::spawn(rpc::serve(host, dispatcher, slot, sr, sw));
let (cr, cw) = tokio::io::split(client_io);
let peer = Peer::connect(cr, cw, Arc::new(Client::default()));
// A remote window's host-key and password remedies reach the server's SSH manager,
// the one its deployment / database connections failed in.
let trust = invoke(&peer, "ssh_trust_host", json!({"id": "nothing-pending"})).await.unwrap_err();
assert!(trust.message.contains("no host key to trust"), "{trust:?}");
invoke(&peer, "ssh_set_secret", json!({"id": "deploy", "secret": "pw", "save": false})).await.unwrap();
assert_eq!(invoke(&peer, "ssh_connected", json!({})).await.unwrap(), json!([]));
invoke(&peer, "project_trust_set", json!({"path": project, "trusted": true, "folder": false})).await.unwrap();
invoke(&peer, "open_project", json!({"path": project})).await.unwrap();
let deployment = json!({"servers": [{"id":"local", "kind":"local", "rootPath":target}], "defaultServer":"local"});