Build Charac server and client with OIDC, SSH sessions, and packages
Verify / verify (ubuntu-latest, linux-bionic-arm64) (push) Canceled after 0s
Verify / verify (ubuntu-latest, linux-x64) (push) Canceled after 0s
Verify / verify (windows-latest, win-x64) (push) Canceled after 0s
Verify / postgres (push) Canceled after 0s

This commit is contained in:
Nitload
2026-10-05 14:02:50 +08:00
parent 4bc3552167
commit 0ea679932e
185 changed files with 10653 additions and 772 deletions
+38
View File
@@ -0,0 +1,38 @@
# Android CLI 客户端
Android 首期支持 ARM64 设备上的 Termux 终端,CLI 使用 `linux-bionic-arm64` RID。另有独立的 .NET Android 启动器项目 `src/android/CharacAndroid.csproj`,包 ID 为 `com.nitload.charac`;Server 仍只部署在 Windows 或 Linux。
执行 `./build.ps1 --target Publish --runtime linux-bionic-arm64 --locked-restore`,或在 Linux 上使用 `bash ./build.sh`。随后运行 `./packaging/package.ps1 -Target ClientTermuxDeb`,生成 `artifacts/packages/charac_0.1.0-1_aarch64.deb`。该包面向标准包名 `com.termux` 的 apt 版 aarch64 Termux,将 Client 安装到 `$PREFIX/opt/charac`,将 `charac` 入口装到 `$PREFIX/bin`,并依赖 Termux 的 `dotnet-runtime-10.0`。在 Termux 中复制 DEB 后运行 `apt install ./charac_0.1.0-1_aarch64.deb`;当前尚未发布到 Termux 仓库,因此 `pkg install charac` 还不能从公共仓库下载它。APK 的环境检查会识别包提供的固定入口。
如果暂时不使用 DEB,也可以复制 `artifacts/publish/linux-bionic-arm64/client/` 到 Termux 私有目录,例如 `$HOME/.local/opt/charac`。这是 framework-dependent 发布,需要自行安装 .NET 10 或更高兼容运行时。为 APK 提供固定入口 `$PREFIX/bin/charac`,其内容用 Termux 的 `sh` 调用 `dotnet $HOME/.local/opt/charac/charac.dll` 并原样传递参数;入口必须可执行。若应用宿主无法定位运行时,可在 Termux 中运行 `dotnet $HOME/.local/opt/charac/charac.dll --help` 检查。
手动复制方案在 Termux 中完成后,创建入口:
```sh
cat > "$PREFIX/bin/charac" <<'SH'
#!/data/data/com.termux/files/usr/bin/sh
exec dotnet "$HOME/.local/opt/charac/charac.dll" "$@"
SH
chmod 700 "$PREFIX/bin/charac"
```
两种安装方式都要设置 Client 的默认入口:
```sh
mkdir -p "$HOME/.config/charac"
printf '[client]\nserver=https://access.example.com\n' > "$HOME/.config/charac/client.config"
```
将 `access.example.com` 改为实际 RAC HTTPS 入口。当前代码含 `login` OIDC PKCE 浏览器登录、本机 loopback 回调、`connect` 正式 WebSocket 交互及一次性 `ls` 诊断,但尚未在 Android/Termux 实机验证浏览器回调或交互。终端原始模式、键盘映射、窗口尺寸同步与 WebSocket 自动重连仍待实现。跨平台构建和发布不等于设备运行验收;验收须在真实 ARM64 Android/Termux 环境检查启动、认证回调、前后台切换和网络恢复。
参考:[.NET RID 目录](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog)、[Termux .NET 包](https://github.com/termux/termux-packages/tree/master/packages/dotnet10.0)。
## One UI APK 与 Termux
APK 包名使用 `com.nitload.charac`,厂商标识为 `nitload`,要求 Android 12 或更高版本。启动器检测 `com.termux` 与自身的 `com.termux.permission.RUN_COMMAND` 授权;用户点击环境检查后,通过 Termux 的 `RUN_COMMAND` 在后台检查 `$PREFIX/bin/charac` 与 `dotnet`,成功才允许启动固定命令 `charac connect`。Termux 里的 Client 自动读取 `~/.config/charac/client.config`。APK 不保存 SSH 私钥、OIDC 令牌或 Termux 的设备凭据。
Android 应用不能直接读取、改写 Termux 私有目录,也不能替用户静默授予跨应用执行权限。用户仍需在 Android 设置中授权 charac 的 `RUN_COMMAND` 权限,并在 Termux 的 `~/.termux/termux.properties` 设置 `allow-external-apps=true`;然后安装上面的 DEB(自动安装 .NET 运行时依赖)或按手动方案复制 CLI,并设置用户级配置。启动器有权限设置入口;检测不到 Termux 时显示安装提示。One UI 的后台启动限制可能要求在通知栏点开 Termux,或授予 Termux“悬浮窗”权限。
在安装了 .NET Android workload 和 Android SDK 的构建机上,运行 `dotnet build src/android/CharacAndroid.csproj -c Debug -p:NuGetAudit=false` 可生成调试签名 APK;再运行 `./packaging/package.ps1 -Target ClientApkDebug` 将其收集到 `artifacts/packages/charac-client_0.1.0_android-debug.apk`;调试签名只用于设备验证,正式升级发布需要 nitload 保管固定签名密钥。Android/Termux 实机的权限回调、CLI 启动、浏览器登录与键盘交互尚未验收。
依据:[Termux RUN_COMMAND 官方说明](https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent)。
+34 -12
View File
@@ -1,17 +1,39 @@
# 平台发布与部署
# 平台部署
| 项目 | Windows | Linux |
| --- | --- | --- |
| 发布 RID | `win-x64`、`win-arm64` | `linux-x64`、`linux-arm64` |
| 产品入口 | `WorkspaceAccessHost.exe host` / `agent` | `WorkspaceAccessHost host` / `agent` |
| 服务管理 | SCM 注册、启动类型和账户配置 | systemd unit、服务账户和目录权限 |
| 用户代理启动 | 用户登录启动任务或用户启动项 | 用户上下文启动组件或 user unit |
| 本机通信路径 | 命名管道 | `/run` 下受控 socket 路径 |
当前提供 Windows x64 Client MSI、Fedora/RHEL 系 Linux x64 Server RPM 和 Client RPM,以及 Termux aarch64 Client DEB。RPM/MSI 由 .NET 10 发布目录生成;`packaging/package.ps1` 只负责封装,发布目录必须先经过 NUKE `Publish`(含 `Verify`)。Android/One UI 的 Termux 启动器 APK 已可生成调试包,需与 Termux DEB 配合,见 [Android 客户端](android-client.md)。
NUKE `Publish` 先执行 Verify,再发布单一产品的 self-contained 产物,并复制平台部署模板。输出位于 `artifacts/publish/<runtime>/`,根目录包含可执行文件、配置与运行时依赖,`deploy/` 包含部署模板。Host 和 Agent 使用同一可执行文件,通过启动参数选择角色,整套产物统一部署和更新。
在仓库根目录执行:
服务配置使用发布目录中的 `appsettings.json` 和 .NET 环境变量覆盖。初始 HTTP 端点位于 loopback。远程传输集成时由受控 TLS 入口提供 WSS,并配置 host 白名单、认证及 Origin 策略。
```powershell
$env:NuGetAudit='false'
.\build.ps1 --target Publish --runtime linux-x64 --locked-restore
.\build.ps1 --target Publish --runtime win-x64 --locked-restore
.\build.ps1 --target Publish --runtime linux-bionic-arm64 --locked-restore
.\packaging\package.ps1 -Target ServerRpm
.\packaging\package.ps1 -Target ClientRpm
.\packaging\package.ps1 -Target ClientMsi
.\packaging\package.ps1 -Target ClientTermuxDeb
```
x64 的 Windows 与 Linux 验证流程由 CI 分别执行。ARM64 产物通过指定 RID 发布,并在对应硬件或虚拟机完成运行验收。
封装需要 [nFPM](https://nfpm.goreleaser.com/) 2.47.0 和 [WiX Toolset](https://wixtoolset.org/) 6.0.2。脚本默认从 `temp/tools/nfpm/nfpm.exe`、`temp/tools/wix.exe` 寻找,也可传 `-NfpmPath`、`-WixPath`。输出在 `artifacts/packages/`;这两个工具和发布产物均不提交仓库。Fedora/RHEL 的 ARM64 RPM 尚未配置。
部署步骤见 [Windows](../../deploy/windows/README.md) 与 [Linux](../../deploy/linux/README.md)。服务安装由部署人员执行。
## Server RPM 首次部署前提
这套部署复用现有 authentik、PostgreSQL、Caddy 和内网 sshd,不需要在 Server RPM 内再部署一套身份平台。安装包只装程序、systemd 单元和非 root 服务账户,不会自动填入生产配置、创建数据库、配置反向代理或向目标 sshd 登记公钥。首次上线按下列顺序准备:
1. 确认 Server 主机能访问 PostgreSQL、authentik 的 OIDC discovery/JWKS,以及内网各目标的 SSH 端口;公网只暴露 Caddy 的 HTTPS 入口,不暴露 PostgreSQL 和目标 SSH。
2. 在 PostgreSQL 创建专用数据库和受限角色。安装 RPM 后将配置写入 `/etc/charac/workspace-access.config`,填 `[database]`、`[oidc]`、`[server]` 和 `[ssh_keys]`;文件只允许 `charac` 服务账户读取。用同一配置执行 `char_rac_server --config /etc/charac/workspace-access.config database migrate`,然后核对迁移结果。服务正常启动不会自动迁移。
3. 在 authentik 为 CLI 准备 public OIDC Provider,启用 PKCE 和本机动态 loopback 回调;Server 配置正确的 issuer、client ID 和 discovery URL。详情见[认证](../authentication.md)。
4. 创建服务账户持有的 `/var/lib/charac/keys`(目录 `0700`,私钥 `0600`),从可信渠道核对目标 sshd 主机公钥。使用本机 `endpoint_regist` 导入 Server 登录私钥、登记目标,并授权 OIDC subject 到目标 OS 账户;目标账户的 `authorized_keys` 也须信任相应登录公钥。详情见[SSH 登记](../ssh-session.md#三参数交互式端点登记)。
5. 配置 Caddy 的域名、TLS 和到 Server 监听地址的反向代理;需要读取真实客户端 IP 时再设置 `[server] trusted_proxy`。最后启动 `charac-server.service`,检查 `systemctl status` 与 `journalctl -u charac-server`,再从 Client 完成真实登录和 SSH 连接。
当前 `/health/ready` 还包含未完成能力标记,可能返回 503;不要将它作为首次部署成功的唯一判据。运行时可核对进程状态、数据库迁移、OIDC 登录和实际工作区连接。Server RPM 不启动 PostgreSQL 容器;数据库可以是独立容器或现有 PostgreSQL 实例。
Server RPM 安装自包含程序到 `/opt/charac/server`,安装 `charac-server.service` 和专用 `charac` 系统账户。包不会携带运行配置、数据库密码或 SSH 私钥,也不会在安装时自动启动服务。部署机上准备 `/etc/charac/workspace-access.config`(只允许服务账户读取),在 `[ssh_keys]` 指定 `/var/lib/charac/keys`,该目录及私钥由 `charac` 拥有且不允许其他用户访问。配置样例安装于 `/usr/share/doc/charac-server/workspace-access.config.example`。准备 PostgreSQL 数据库并执行迁移后,再用 `systemctl enable --now charac-server` 启动。服务监听地址和 Caddy 等反向代理按实际部署配置;Server 只需能访问内网 SSH 目标。详情见 [Linux 部署](../../deploy/linux/README.md)。
Server 运行时只需 `--config /etc/charac/workspace-access.config`。这一个 INI 保存监听地址、OIDC 信息、PostgreSQL 连接参数和 `[ssh_keys] directory`;它不负责启动 PostgreSQL 容器。PostgreSQL 容器及其数据卷应由独立的数据库部署管理,RAC 只使用受限数据库账户连接。`charac` 是非 root 的系统服务账户,`StateDirectory=charac` 负责 `/var/lib/charac`,`/var/lib/charac/keys` 建议设为该账户所有、目录 `0700`、私钥 `0600`。当前没有磁盘会话缓存或单独日志文件:工作区状态留在 Server 内存与数据库历史中,标准日志进入 systemd journal,可用 `journalctl -u charac-server` 查看。无需额外配置 `CacheDirectory` 或 `LogsDirectory`;将来确有持久快照或文件日志时再增加。服务通过高端口监听,由 Caddy 承接 443,无需为网络入口提升 Server 到 root。
Client RPM 安装自包含程序到 `/opt/charac/client`,并提供 `/usr/bin/charac` 命令。Windows MSI 安装到 `Program Files\nitload\Charac Client`,把安装目录加入系统 `PATH`;新终端可运行 `charac.exe --help`。MSI 内嵌所需文件,不依赖独立 CAB。两个 Client 包均不预置登录令牌或目标配置。
RPM 尚需在目标 Fedora/RHEL 机器进行实际安装、升级、卸载和 systemd 验收;Termux DEB 尚需在真实 Android/Termux 设备上安装、启动和登录验收;MSI 尚需在正常启用 Windows Installer 服务的 Windows 机器进行安装、升级、卸载验收。当前构建环境完成了 RPM 生成及文件头检查、Termux DEB 控制字段和文件路径检查、MSI 生成及文件表反编译检查;受限环境里的 ICE 校验无法连接 Windows Installer 服务。
Client 可在用户级 INI 的 `[client] server` 写入默认 HTTPS origin,样例见仓库根目录 `client.config.example`;Client RPM 另安装到 `/usr/share/doc/charac-client/client.config.example`,MSI 则安装到程序目录。Windows 默认读取 `%APPDATA%\Charac\client.config`;Linux/Termux 默认读取 `$XDG_CONFIG_HOME/charac/client.config`,未设置时读取 `~/.config/charac/client.config`。之后直接执行 `charac connect`(Windows 为 `charac.exe connect`);`--server <origin>` 可临时覆盖,`--config <路径>` 可在命令前选用其他配置。该文件只存入口地址,不存 OIDC 令牌;旧版 `%APPDATA%\CharRAC`、`~/.config/char-rac` 配置在新位置不存在时仍会自动读取。Client 安装级设备凭据仍独立存于用户本地应用数据目录,不能复制到多台设备共用。
+6 -13
View File
@@ -1,16 +1,9 @@
# 进程权限与隔离
# 进程与安全边界
| 层次 | Windows | Linux |
| 项目 | Windows | Linux |
| --- | --- | --- |
| 账户权限 | 用户访问令牌、SID 与文件 ACL | UID、GID、附加组与文件权限 |
| 代理 IPC | 命名管道 ACL 与对端身份校验 | socket 路径权限与 peer credentials |
| 进程归属 | Job Object 与进程句柄 | 进程组与 systemd/cgroup |
| 系统管理 | 令牌权限及提升策略 | capabilities、sudo、polkit |
| 目标进程权限 | sshd 按目标账户令牌创建 | sshd/PAM 按目标 UID/GID 创建 |
| Server 权限 | 独立服务账户,保护 CA 凭据 | 独立服务账户及受限文件权限 |
| SSH 配置 | Windows OpenSSH 服务配置 | Linux sshd 与 PAM 配置 |
网络宿主以服务账户执行网络与授权逻辑;用户代理以目标账户执行终端程序。身份切换或代理创建接口接受经过验证的账户映射和限定启动参数。
工作区清理追踪完整进程树。显式结束先执行有序退出,再按策略终止剩余进程,并释放 PTY 及本机通信资源。
完全复用用户环境时,终端具备该用户相应的系统访问能力。部署权限策略决定进程提升、系统管理和共享目录访问范围。文件权限在实际存储后端验收,见[存储说明](storage.md)。
验收包括不同账户的代理访问、伪造映射、越权工作目录、子进程清理,以及普通账户的管理权限边界。
Server 必须先验证 OIDC、授权、账户映射和目标主机密钥,再建立 SSH 连接。目标 sshd 的账户权限限制文件与系统操作;不能把 Server 服务账户权限传给终端用户。证书应短期有效且限定 principal。关闭工作区时先尝试正常关闭 SSH channel,再按明确策略处理残留目标进程;目标进程清理能力须在实际 sshd 环境验收。
+10 -9
View File
@@ -1,16 +1,17 @@
# 服务宿主与本机通信
# 服务宿主
| 平台 | 系统入口 | 用户代理通信 |
| 项目 | Windows | Linux |
| --- | --- | --- |
| Windows | Windows Service Control Manager;`AddWindowsService` | 带 SID ACL 的命名管道 |
| Linux | systemd system unit;`AddSystemd` | 带文件权限和对端凭据检查的 Unix domain socket |
| Server | Windows Service | systemd service |
| SSH 服务 | OpenSSH sshd 系统服务 | OpenSSH sshd 系统服务 |
| 日志 | Windows 事件与应用日志 | journal 与应用日志 |
Host 负责远程连接与控制权仲裁,用户代理持有工作区。服务账户具备配置读取与本机通信所需的权限。IPC 连接建立后验证协议版本、对端系统身份和目标账户。
Server 作为独立服务运行,管理认证、SSH Broker 和工作区。目标 sshd 独立运行且只在受控内网接受来自 Server 的连接。Server 的存活端点与业务就绪端点分开;后者须以 OIDC、CA、SSH、Session Manager 和 WebSocket 的实际状态为准。当前实现服务入口、健康端点与客户端状态探测;公网域名由 `workspace-access.config` 的 `[server] domain` 指定,并由可信反向代理转发,见[通信入口](../client-server-transport.md)。部署见[平台部署](deployment.md)。
Windows 代理在目标用户登录时启动。Linux 用户代理由用户上下文启动组件管理。本机通信路径按 host 与系统用户标识确定,进程启动后执行注册握手。
## 本机管理命令
当前 Host 已集成系统服务生命周期与健康检查。IPC 注册属于后续集成项。部署入口见 [Windows](../../deploy/windows/README.md) 与 [Linux](../../deploy/linux/README.md)。
运行中的 Server 在本机建立与配置文件绝对路径绑定的命名管道。新启动的 `char_rac_server --config <同一路径> status`、`sessions` 或 `reload` 作为管理客户端连接现有进程,不启动第二份 HTTP 服务,也不向运行中的进程注入代码。`status` 返回进程 ID 和当前 SSH 工作区数量;`sessions` 列出工作区 ID、OIDC issuer/subject、目标、系统账户、附着状态、时间以及当前 WebSocket 的连接来源;`reload` 重新读取 INI,仅在线应用 `[Logging]` 设置。修改监听地址、OIDC、数据库、SSH 目标或其他设置时,命令明确拒绝并提示重启,原配置和工作区继续运行。配置文件路径必须与服务启动时解析出的路径相同。
验收:服务随系统启动;服务停止完成资源清理;代理识别正确服务身份;其他账户访问 IPC 时按规则返回拒绝结果。
管道使用 `PipeOptions.CurrentUserOnly`,因此管理命令必须以与服务相同的系统账户执行。配置路径的哈希只用于定位管道,不是认证凭据。Linux 的命名管道底层使用 Unix 域套接字;部署模板保留 `UMask=0077`,并且不隔离 `/tmp`,以便该账户在另一个终端连接。Windows 服务使用独立账户时,管理命令也须在该账户上下文中运行。此管理通道不在 HTTP/WebSocket 或 Caddy 入口上暴露。必须限制服务账户的交互登录;同账户运行的进程可以调用该管道。连接来源默认取 Server 直接看到的网络对端;经 Caddy 代理时默认显示代理地址。若需要显示 Caddy 上游客户端地址,在 `[server] trusted_proxy=<Caddy 到达 Server 所用的 IP>` 中显式信任这一跳,Server 才读取 `X-Forwarded-For` 的最后一跳。其他来源传入的转发头不被信任。完整配置热替换需要逐组件设计切换与回滚,目前不支持。
参考:[Windows Service](https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service)、[systemd 集成](https://devblogs.microsoft.com/dotnet/net-core-and-systemd/)。
切断用户连接需在 INI 中设置 `[management] allow_disconnect=true` 并重启 Server,默认关闭。管理员在服务账户上下文中运行 `char_rac_server --config <同一路径> disconnect --subject <OIDC-sub> --confirm`,Server 按配置的 issuer 和给定 subject 精确匹配,断开该身份当前附着的所有 Client/WebSocket,令旧连接的输入租约立即失效并记录日志。该操作同时释放当前 Client 设备的占用;SSH 工作区继续运行,用户持有效令牌且仍获授权时可以从任一设备重新连接。这是一次性断开操作,不改变授权;持续禁止访问须另行撤销数据库授权。命令需要显式 subject 和 `--confirm`,Server 还会检查启用开关;这些参数用于减少误操作,真正的本机权限边界是管道所属系统账户。`allow_disconnect` 不支持热重载。
+10 -10
View File
@@ -1,16 +1,16 @@
# 会话生命周期
| 事件 | Windows | Linux |
| 阶段 | Windows | Linux |
| --- | --- | --- |
| 创建工作区 | 已登录用户代理建立 ConPTY | 用户上下文中的代理建立 PTY |
| 网络断开 | 代理继续持有终端和进程 | 代理继续持有终端和进程 |
| 同身份重新连接 | 重新授权并附着原工作区 | 重新授权并附着原工作区 |
| 锁屏或关闭桌面连接 | 用户登录会话仍存在时保持工作区 | 按用户代理所属服务生命周期保持工作区 |
| 用户注销 | 用户代理及进程受 Windows 登录会话清理影响 | 按 user service、会话清理及 linger 配置执行 |
| 系统启动 | 等待目标用户代理注册后建立新工作区 | 服务准备用户上下文后建立新工作区 |
| 创建 | Server 通过 SSH 请求目标 OpenSSH 创建 ConPTY Shell | Server 通过 SSH 请求目标 OpenSSH 创建 POSIX PTY Shell |
| 断开客户端 | Server 保持 SSH channel 并持续读取输出 | 同左 |
| 后端中断 | 原 SSH channel 消失 | 同左 |
持久性范围为系统本次运行期间、用户代理存活时的工作区。客户端断线仅解除连接;工作区终止是独立的授权操作。
当前 `SshWorkspaceManager` 按工作区分别保存身份归属、控制权、SSH channel 和最近 256 KiB 输出。一个工作区同时只有一个输入控制者;Server 可管理多个工作区。客户端断线不终止 SSH;显式结束、后端 SSH EOF 或 Server 停止会释放资源。后端结束时,Server 会先排空该观察者队列,再通过 WebSocket 发送 `ended` 事件并关闭连接;接管造成的旧观察者断开不会声称 SSH 已结束。撤销授权会阻止新的附着与结束请求,但尚不会自动终止已经附着的连接。Server 重启无法凭 SSH CA 恢复已经断开的交互终端,如需跨重启持久化,应设计目标侧会话管理。协议见[连接与会话](../connection-protocol.md)。
连接归属在断线后保留。同一身份重连获取新控制权,接管撤销旧连接。会话管理器完成进程清理后释放工作区归属。broker 重启后的代理重新登记属于协议集成验收范围。
正式工作区创建时,Server 将身份键、目标、系统账户、认证方式与创建时间写入 PostgreSQL `access.workspace_records`。正常关闭会补写结束时间和原因,Server 正常停止记录 `server_stopped`;崩溃可能留下未补写的历史行。历史行始终保留,但它不是持久化的 SSH channel,也不会在重启后出现在可附着资源列表。测试用例只清理各自创建的临时数据,Server 停止不清理已登记身份、密钥或历史记录。
验收分别执行网络断开、浏览器关闭、锁屏、用户注销、代理退出和显式终止,记录工作区状态与进程结果。系统重启后验证新环境的建立条件。
同一 OIDC 身份的活跃 Client 连接由 Server 按安装凭据仲裁:一台 Client 可同时附着多个工作区,另一台 Client 在前者仍有活跃连接时被拒绝。所有 WebSocket 断开后,设备占用释放,但 SSH 工作区继续运行。管理端 `disconnect` 仅中断该身份当前的 Client/WebSocket 并释放占用;它不删除工作区或修改目标授权。
资源列表仅暴露当前身份仍有权访问且账户映射一致的存活工作区,并携带创建时间及 `attached`/`detached` 状态。列表是快照,不预占控制权;查询后退出、接管、授权撤销等变化以实际附着请求的结果为准。Server 不为资源列表探测或新建 SSH 连接。
+3 -28
View File
@@ -1,30 +1,5 @@
# 文件存储与 VirtioFS
# 文件存储
| 项目 | Windows | Linux |
| --- | --- | --- |
| 本地文件 | NTFS 等本地卷,使用 Windows 文件 API 与权限 | 原生文件系统,使用 POSIX 文件 API 与权限 |
| 宿主共享目录 | Windows VirtioFS 驱动与 WinFsp | VirtioFS 客户端与宿主目录 |
| 身份映射 | 验证 SID、挂载配置和宿主 UID/GID 的关系 | 验证 UID/GID、附加组及宿主权限 |
| 变更与锁 | 验证 Windows 通知、共享模式和锁的传播 | 验证文件通知、锁及跨挂载访问行为 |
目标用户的文件访问由目标操作系统账户、文件系统权限及挂载配置决定。Server 通过 SSH 提供终端连接,不代理文件系统操作。若目标工作区使用 VirtioFS,须在所选驱动、挂载与宿主配置上验证读写权限、文件锁、变更通知、原子保存及重命名。
## 路径安排
服务配置、运行元数据及用户配置优先放在操作系统本地卷。项目文件可放在 VirtioFS 工作目录。挂载可用性和目标用户访问检查在创建工作区前执行。
并行编辑为本地操作与远程操作分配独立工作目录。代码项目可以采用独立 clone 或 Git worktree,通过版本控制整合修改。远程连接独占管理客户端控制权,文件写入由应用与存储权限决定。
## 兼容性记录
每个部署记录 PVE、virtiofsd、Windows virtio-win / WinFsp 版本、宿主文件系统、导出路径和挂载选项。
| 验证项 | 场景 |
| --- | --- |
| 可见性 | 目标用户代理上下文访问预期盘符或挂载点 |
| 权限 | 两个系统账户分别读取、创建、修改和删除文件 |
| 外部变更 | Windows 本地程序、宿主和其他 guest 修改文件后,编辑器观察变化 |
| 锁 | 不同进程及不同访问入口的独占锁和共享模式 |
| 保存 | 临时文件写入后重命名替换、刷新、重复保存 |
| 路径 | Unicode、大小写、长路径与符号链接 |
| 开发工具 | Git 操作、构建输出、文件监听和高频小文件访问 |
参考:[Windows VirtioFS](https://github.com/virtio-win/kvm-guest-drivers-windows/wiki/Virtiofs:-Shared-file-system)、[Git worktree](https://git-scm.com/docs/git-worktree)。
Windows 需记录 VirtioFS 驱动和 WinFsp 版本,并验证 ACL 与账户 SID 的实际行为。Linux 需验证 UID/GID 映射、权限位、缓存和挂载选项。终端连接成功不等于存储行为已经验收。
+3 -15
View File
@@ -1,17 +1,5 @@
# 系统管理权限
# 系统管理操作
| 能力 | Windows | Linux |
| --- | --- | --- |
| 本机关机和重启 | 系统用户权限分配,包含 `SeShutdownPrivilege` | 登录会话授权、polkit、sudo 或 root 权限 |
| 网络发起系统关机 | 对应远程管理权限及服务策略 | 对应远程管理服务和账户策略 |
| 提升执行权限 | Windows 用户令牌与提升机制 | sudo、capabilities 及服务管理接口 |
工作区内命令以目标 SSH 系统账户运行。Windows 管理权限由目标账户令牌及 UAC/系统策略决定;Linux 管理权限由目标账户、sudo/polkit 等策略决定。Server 不向工作区授予额外系统权限,也不把 SSH CA 凭据暴露给客户端。
终端使用目标系统账户的权限。部署阶段明确该账户的系统管理能力,并使用操作系统策略控制可执行的管理操作。通过远程终端运行的程序属于目标机器上的本地进程。
复用登录用户时,账户级权限策略也作用于该用户的其他程序。需要不同权限边界的部署使用专用执行账户或经过验证的受限上下文。
管理入口与普通终端输入分别授权。系统管理操作记录请求身份、目标、时间和执行结果。系统启动后的工作区状态由[用户环境](user-environment.md)和[生命周期](session-lifecycle.md)定义。
验收使用系统权限查询和具有代表性的系统管理 API,确认工作区与管理入口符合配置。PTY 输入作为终端字节流处理。
参考:[Windows 关机 API 权限](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-exitwindowsex)。
验收应分别使用普通账户和允许执行管理操作的账户,确认权限边界与审计行为。授权映射必须明确限制可登录的目标和系统账户。
+4 -11
View File
@@ -2,15 +2,8 @@
| 项目 | Windows | Linux |
| --- | --- | --- |
| 伪终端 | ConPTY | POSIX PTY master/slave |
| 进程连接 | 伪控制台通过扩展启动属性关联用户进程 | session、控制终端、标准流和前台进程组 |
| 尺寸变更 | `ResizePseudoConsole` | 窗口尺寸 ioctl 与终端信号行为 |
| 生命周期 | 用户进程、管道和伪控制台句柄 | 子进程、进程组和 PTY 文件描述符 |
| 伪终端 | 目标 OpenSSH 使用 ConPTY | 目标 OpenSSH 使用 POSIX PTY |
| Shell | 可将目标用户默认 Shell 设为 PowerShell 7 | 使用目标账户允许的 Shell |
| 尺寸 | SSH window-change 请求 | SSH window-change 请求 |
共享接口为 `IPtySessionFactory` 与 `IPtySession`。终端输入输出按字节传递;用户代理负责持续读出、缓冲边界及终端状态。平台实现明确句柄所有权、取消和资源释放顺序。
Windows 后端在用户代理上下文中创建 shell。Linux 后端使用适合托管运行时的原生启动边界完成进程和终端设置,子进程进入目标程序前完成系统调用配置。
验收覆盖交互 shell、UTF-8、方向键、Ctrl+C、全屏编辑器、尺寸变更、大量输出、子进程退出及显式终止。断线后的输出持续被消费,重连恢复见[连接协议](../connection-protocol.md)。
参考:[ConPTY 会话](https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session)、[Linux PTY API](https://man7.org/linux/man-pages/man3/openpty.3.html)。
Server 只持有 SSH shell channel,转发输入、输出和尺寸变化,并管理客户端附着。伪终端创建、用户 Shell 启动与本地进程资源由目标 sshd 负责。需通过真实目标验收 PowerShell 7、交互提示、全屏 TUI、颜色、控制键和尺寸变化。Server 已实现内部密码 SSH Broker、PTY Shell、受保护的 WAN 会话入口与有界输出保持;真实目标上的 `ls` 和完整交互仍待验收,见[SSH 会话设计](../ssh-session.md)。
+7 -18
View File
@@ -1,20 +1,9 @@
# 用户执行环境
# 用户环境
| 平台 | 环境建立 | 身份标识 | 就绪条件 |
| --- | --- | --- | --- |
| Windows | 复用目标用户登录后的代理上下文 | Windows SID | 指定用户的代理已注册 |
| Linux | 为现有系统账户设置 UID、GID、附加组及用户会话 | UID 与组信息 | 账户解析、权限设置及代理启动完成 |
| 项目 | Windows | Linux |
| --- | --- | --- |
| 账户标识 | SID | UID、GID 与附加组 |
| 登录环境 | OpenSSH 建立目标用户环境,默认 Shell 可配置 | sshd/PAM 建立目标用户环境 |
| 主目录 | 目标账户配置决定 | 目标账户与 PAM 配置决定 |
## Windows
标准部署使用目标用户登录后启动的 Agent。它继承用户权限和用户配置,终端启动时设置工作目录、环境变量和 shell 参数。专用本地账户可以作为远程工作区账户。
Host 根据外部身份映射匹配该用户的 SID。目标用户代理尚未注册时,工作区状态为 `WaitingForUserSession`。锁屏状态下保留用户会话;用户注销后的行为见[生命周期](session-lifecycle.md)。
## Linux
系统账户由操作系统维护。启动组件按授权映射查询账户,初始化附加组、主组、UID、HOME、工作目录和登录环境;部署所需的 PAM account/session 处理归入该组件。用户代理以目标账户身份持有 PTY。
## 验收
检查终端中的系统身份、主目录、配置目录、环境变量和文件访问结果。Windows 验证登录前的等待状态与登录后的代理注册;Linux 验证无人交互登录时的用户上下文建立。认证归属与系统账户映射见[认证文档](../authentication.md)。
Server 将经 authentik 认证的身份映射到授权目标与系统账户,再通过 SSH 证书登录。工作区进程由目标 sshd 按系统账户启动;Server 不启动用户 Agent,也不直接切换用户身份。需在目标系统验收环境变量、主目录、工作目录、组权限与登录策略。认证设计见[身份认证](../authentication.md)。