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
26 changes: 26 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,32 @@ auto_unlock_on_request = true
portable = false
# portable_dir = "./sshwarden-data"

# 多设备共享模式(适合把 SSHWarden 放在 OneDrive/Dropbox 等同步目录中):
# - 共享:config.toml、local-key-cache.json、bindings.json、keys/、sshwarden_config
# - 每设备独立:session、pid、log、runtime socket、Windows Hello/native unlock slot
# 这样两台机器可以共享同一份 Bitwarden SSH key 本地投影和 Host 绑定,
# 但不会互相覆盖运行态文件。
# 重要:multi_device 本身不改变数据根目录的位置,只在已有数据目录下拆分
# 共享/设备文件。要让数据真正落在同步目录,仍需用 portable=true(配合
# portable_dir)或 SSHWARDEN_HOME / SSHWARDEN_PORTABLE 把数据根指向该
# OneDrive/Dropbox 目录;否则数据仍在平台默认目录(如 %APPDATA%)而不会被共享。
# 若启用,建议同时设置 [ssh_config].path_style = "home_relative",避免
# C:\Users\zheng 与 C:\Users\Administrator 这样的用户名差异写入绝对路径。
multi_device = false
# device_id = "auto" # 默认 auto;也可用 SSHWARDEN_DEVICE_ID 覆盖

# ============================================
# SSH config / Host binding 生成配置
# ============================================
[ssh_config]
# 托管 snippet 路径。留空时:普通模式默认 exe 同目录;multi_device=true 时默认共享目录下的 sshwarden_config。
# managed_path = "sshwarden_config"

# 写入 ~/.ssh/config Include 和托管 snippet 中 IdentityFile 的路径风格:
# - "absolute": 写绝对路径(默认)
# - "home_relative": 对用户 home 下路径写成 ~/...,适合 OneDrive 跨 Windows 用户名共享
path_style = "absolute"

# ============================================
# Socket 配置(高级)
# ============================================
Expand Down
11 changes: 9 additions & 2 deletions crates/sshwarden-agent/src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,13 @@ pub enum ControlAction {
SetPin {
pin: String,
},
Forget,
Forget {
/// In multi-device storage mode, also remove shared remembered-secret
/// cache files (`local-key-cache.json` and legacy `vault.enc`). Plain
/// `forget` is device-only there; on legacy storage this flag is
/// effectively always true for backwards compatibility.
shared: bool,
},
/// Cleanly shut down the daemon: cancel the main loop, stop the agent and
/// control server, and remove the PID file. Used by `stop` / `restart`.
Stop,
Expand Down Expand Up @@ -360,7 +366,8 @@ async fn dispatch_control_command(
"status" => ControlAction::Status { json: false },
"status-json" => ControlAction::Status { json: true },
"sync" => ControlAction::Sync,
"forget" => ControlAction::Forget,
"forget" => ControlAction::Forget { shared: false },
"forget-shared" => ControlAction::Forget { shared: true },
"stop" => ControlAction::Stop,
"bind-hosts-dialog" => ControlAction::BindHostsDialog,
s if s.starts_with("unlock-pin:") => {
Expand Down
14 changes: 2 additions & 12 deletions crates/sshwarden-config/src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;

use anyhow::Context;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -86,7 +83,7 @@ impl LocalKeyCacheFile {
}
let content =
serde_json::to_string_pretty(self).context("Failed to serialize local key cache")?;
write_owner_only_file(&path, content)
crate::write_owner_only_file(&path, content)
.with_context(|| format!("Failed to write local key cache: {}", path.display()))?;
Ok(())
}
Expand All @@ -100,10 +97,3 @@ impl LocalKeyCacheFile {
Ok(())
}
}

fn write_owner_only_file(path: &Path, content: impl AsRef<[u8]>) -> anyhow::Result<()> {
std::fs::write(path, content)?;
#[cfg(unix)]
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
Ok(())
}
Loading
Loading