rupivbluegreen/omni-sag

GitHub: rupivbluegreen/omni-sag

一款开源的 Linux 特权访问网关,通过 AD+MFA 认证、策略授权、防篡改凭证链和会话录制,为企业提供可审计的 SSH/SFTP 堡垒机能力。

Stars: 0 | Forks: 0

Omni-SAG ### 留存凭证的 SSH 堡垒机 *你服务器的保镖。每一次登录都被核查,每一个 shell/隧道/传输都被授权,每一个字节都 有据可查——附带你可以在网关关闭时离线验证的防篡改凭证链。* [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/rupivbluegreen/omni-sag/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-ffb000.svg)](LICENSE) [![Go 1.25](https://img.shields.io/badge/go-1.25-00ADD8.svg)](go.mod) [![fails](https://img.shields.io/badge/fails-closed%20on%20purpose-ff6b6b.svg)](docs/audit/fail-closed-matrix.md) [![reviewed](https://img.shields.io/badge/reviewed-adversarially-57d98a.svg)](#-how-it-was-built) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-ffb000.svg)](CONTRIBUTING.md) **[🌐 官网](https://rupivbluegreen.github.io/omni-sag/) · [🚀 快速开始](#-60-seconds-to-whoa) · [🧾 审计包](docs/audit/) · [🗺️ 路线图](#️-roadmap)**
## 👋 这到底是什么? 让你的团队指向 **Omni-SAG**,而不是直接指向你的服务器。它会针对 Active Directory(结合 MFA)对他们进行身份验证,精确决定他们可以访问什么—— 真实的 shell、SFTP,或者仅仅是一个转发的端口——并以一种任何人都无法在事后 悄悄篡改的方式记录下*一切*。 默认的回答永远是**拒绝**;访问权限是你按连接争取来的。 它是**开源的**,公开构建,献给所有认为特权访问应该附带凭证的人。 不需要企业销售电话。 🙂 ``` $ ssh -L 5432:db1.lab.local:5432 alice@gateway # alice ∈ dba ✔ ALLOWED · role=dba · tunnel open · evidence: tunnel_decision allow=true $ ssh -L 5432:db1.lab.local:5432 bob@gateway # bob ∉ dba ✘ administratively prohibited · evidence: tunnel_decision allow=false $ omni-verify -bundle ./evidence -pubkey $KEY PASS — evidence bundle is intact and authentic. # 翻转一个字节… FAIL — TAMPER: record hash mismatch. loudly. ``` 那不是样图——而是一次真实的录制运行:**[▶ 观看实时演示](https://rupivbluegreen.github.io/omni-sag/#start)** (相同的实验室,相同的两个用户,相同的篡改行为——原始的 [asciicast](docs/assets/demo.cast) 也在 repo 中)。 ## ✨ 功能 确切的功能,极少的废话——下面的每一行都有代码支撑,而不是路线图 PPT 上的空话。 **🔐 AD + MFA** — 针对 Active Directory 进行 LDAPS 绑定,然后使用 RADIUS MS-CHAPv2 作为第二因素 (从不使用 PAP;交互式 OTP 在 SSH 密码路径上会失败闭合,因为它无法进行提示)。 ``` mfa: enabled: true radius: { server: "radius.corp.local:1812", secret: "...", nas_identifier: "omni-sag" } ``` **🖥️ 目标上的真实 shell 与 SFTP** — SSH 用户名中的 `user%host` 用于选择真实的 目标;网关会开启真正的第二段 SSH 连接,并将实际的 PTY shell 或 SFTP session 代理 给它。绝不是替代品。 ``` $ ssh 'alice%db1.lab.local'@gateway -p 2222 $ sftp 'alice%db1.lab.local'@gateway -P 2222 ``` 匹配的策略规则必须解析为确切的一个主机和一个端口(模糊匹配会 失败闭合,而不是“随便选一个”)。 **🚇 端口转发** — `-L` 隧道按 host:port 受策略控制;`-D` 动态 SOCKS 转发 沿用相同的 `direct-tcpip` 通道,`-J` ProxyJump 也可以使用(跳转 只是另一次 `direct-tcpip` 打开)。 ``` $ ssh -L 5432:db1.lab.local:5432 alice@gateway $ ssh -D 1080 alice@gateway $ ssh -J alice@gateway alice@db1.lab.local ``` `scp` 对于任何当前的 OpenSSH 客户端都能开箱即用——它在底层 默认使用 SFTP 协议,由上述相同的真实 shell/SFTP 路径提供服务。传统的基于 exec 的协议(`scp -O`)也被支持,仅限单文件(不支持 `-r`),但属于选择启用 ——需设置 `enable_scp: true` 才能开启(因为它增加了 exec-channel 攻击面, 所以默认保持关闭)。 不支持:`-R` 远程/反向转发,或 X11 转发。 **🎛️ 功能熔断开关** — 在网关处禁用整类访问权限,独立于 策略(三个中至少必须保持开启一个)。`enable_scp` 的逻辑相反——属于 选择启用,默认关闭。 ``` disable_ssh: true # no interactive shells disable_tunnel: true # no -L/-D forwarding disable_sftp: true # no SFTP subsystem (also blocks default-protocol scp) enable_scp: true # opt-in: serve legacy scp -O (off by default) ``` **🧭 策略** — 绑定 AD 组的角色;每条规则都会设置允许的 host+ports 以及录制 姿态和凭证姿态。`host` 也可以是 CIDR 范围(例如 `10.0.0.0/8`),而不是 每行一个主机名——直接匹配字面 IP,或者如果它解析出的每个地址都 落在该范围内,则匹配解析后的主机名(部分/拆分匹配会失败闭合)。 ``` policy: roles: - name: dba groups: ["dba"] allow: - host: "db1.lab.local" ports: [5432] record: none # none | metadata-only | full (full refuses -L) credential: passthrough # passthrough | prompt | inject | deny require_approval: false # four-eyes-gate this -L tunnel - host: "10.0.0.0/8" # whole subnet, one rule ports: [22] ``` **🎯 单一拨号器 + SSRF 防护** — 每个 `-L`/`-D` 目标 socket 都经过授权,然后 通过同一条代码路径打开;*解析后的* IP(不仅是主机名)会针对 loopback、link-local(包括 `169.254.169.254` 云元数据 IP)、unspecified 以及 multicast/ broadcast 进行检查——从而填补了 DNS-rebinding TOCTOU 漏洞。RFC1918/CGNAT 是 故意允许的;触达内部主机正是堡垒机的核心意义。 **🔑 CyberArk 注入** — `credential: inject` 通过 mTLS 实时从 CyberArk CCP 获取目标凭证;用户永远看不到它。CCP 不可达 = 拒绝,绝不 静默降级为提示/透传。 ``` cyberark: base_url: "https://ccp.lab.local/AIMWebService" client_cert: "cyberark-client.crt" client_key: "cyberark-client.key" ca_cert: "cyberark-ca.crt" app_id: "omni-sag" safe: "targets" ``` **🦠 SFTP 内容检查** — 上传流经 ICAP(AV/DLP)。*每一次*上传——无论干净 与否——都会在 Object-Locked 隔离桶中获得一份永久副本;被拦截的、无法扫描的或 “已修改”的结果会被彻底拒绝。 ``` inspection: enabled: true icap: { endpoint: "icap.lab.local:1344", service: "avscan" } ``` **👀 双人审批 (Four-eyes)** — `require_approval` 会阻塞 `-L` 隧道,直到 第二个人类批准它;另外,每次受检的 SFTP 上传都会在结束时阻塞,直到 第二个人类将其从隔离区放行——基于组划分,因此放行者必须与上传者 共享一个 AD 组。无论哪种方式,都绝不能是你自己。 ``` $ omnisag-ctl approvals $ omnisag-ctl approve ``` **📥 拉取-释放** — 已批准的 SFTP 上传永远不会自动推送到目标; 同一个上传者会在批准窗口期(默认 6h)内从网关的 `/releases` 目录自行 取回。 **🎥 会话录制** — asciicast v2,流式传输到存储中,因此长会话永远不会 完整滞留在内存中。 ``` recording: local_dir: "recordings" ``` `record: full` 目标会录制其 shell;到这些目标的 `-L` 转发会被拒绝( 转发的字节无法被有意义地录制)。 **🧾 防篡改凭证** — 每一个决策(auth、MFA、隧道、凭证、审批、 检查、录制)都是哈希链式、Merkle 检查点化且经过 Ed25519 签名的。 ``` $ omni-verify -bundle ./evidence -pubkey $KEY -head $HEAD PASS — evidence bundle is intact and authentic. ``` **🛰️ 控制平面** — 位于自己监听器上的 HTTP API(mTLS 或 bearer tokens),一个 CLI,以及一个 Bubble Tea TUI。关闭 API,SSH 依然继续服务——它是真正的带外 (out-of-band) 运行。 ``` $ omnisag-ctl sessions $ omnisag-ctl sessions kill $ omnisag-ctl trace alice dba db1.lab.local 5432 $ omnisag-ctl tui ``` **🪵 `-debug`** — 将 auth/MFA 失败和每一个凭证决策镜像到 stdout。仅限 开发环境;它会削弱防枚举姿态。 ``` $ omni-sag -config config.yaml -debug ``` **📦 打包** — UBI9 非 root 镜像,Helm chart(restricted-v2 pod security), 位于独立监听器上的 Prometheus 指标,SIGTERM 上的优雅排水,以及 FIPS 就绪模式(`off` | `warn` | `enforce`)。 ## 🚀 60 秒到“哇哦” 启动一个一次性的实验室(Samba AD、MinIO、FreeRADIUS),然后是两个 SSH 用户,其中 只有 DBA 能获得隧道——凭证日志中会记录这两次尝试。 ``` git clone https://github.com/rupivbluegreen/omni-sag cd omni-sag make lab-up # samba-AD + MinIO + FreeRADIUS make lab-seed # create alice (dba) and bob (not) make binaries # build omni-sag, omnisag-ctl, omni-verify ./bin/omni-sag_linux_amd64 -config deploy/compose/config.example.yaml # ssh -L 5432:db1.lab.local:5432 alice@gw → 允许 # ssh -L 5432:db1.lab.local:5432 bob@gw → 被管理性禁止 # ./bin/omni-verify_linux_amd64 -bundle ./evidence → PASS(篡改一个字节 → FAIL) ``` 要求:**Go 1.25+**、**Docker**(用于实验室)以及一个 SSH 客户端。使用 `make lab-down` 清理。 ## 🧠 工作原理 一个**模块化单体**,具有 CI 强制的 package 边界——易于理解,且免去了 分布式系统的沉重代价。 ``` client ──ssh──▶ [ session ] ──▶ authn + ratelimit ──▶ policy ──┬──▶ [ dialer ] ──▶ target (-L/-D) │ └──▶ 2nd SSH leg ──▶ target (shell/SFTP) ▼ [ evidence ] ── hash-chain · Merkle · Ed25519 · WORM ──▶ omni-verify (offline) ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ out-of-band control plane ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ operator ──mTLS──▶ [ api ] ──▶ sessions · policy · approvals · supervision ▲ (the data path never imports this) omnisag-ctl / TUI ``` **承载核心的常量**(每一项均由 CI 或测试强制执行): 1. **失败闭合 (Fail closed)** — 任何依赖故障都会导致拒绝。由 [29 行失败闭合矩阵](docs/audit/fail-closed-matrix.md) 证明。 2. **无静默凭证降级** — `inject` 失败即拒绝;绝不回退到提示/透传。 3. **凭证非可选且防篡改** — 并且 emit 错误会浮现,绝不会被吞没。 4. **凭证永不成为 Go 字符串** — 仅限 `[]byte`,并清零 ([ADR-0001](docs/decisions/0001-mlock-free-credential-handling.md))。 5. **双人原则 (Four-eyes)** — 审批者 ≠ 请求者,服务端强制执行,重启后依然持久有效。 6. **控制平面带外运行** — 关闭 API,SSH 继续服务。(这是经过测试的,而不是一句口号。) ## 🏗️ 如何构建 薄切片,GA 及以后,每个都可以独立演示——没有五项半成品分支的那种 草率感。 每个切片在开始下一个之前都经过了**对抗性审查关卡**,其中一些 审查捕获了真实的 bug。这才是关键所在。 - **6** 个真实的 bug 被审查/加固发现(一个 ICAP OOM,一个策略评估契约失误,一个 检查失败开启,一个 SFTP 检查缺口绕过……)——全部修复,全部进行回归锁定。 - 在称之为 v1 之前进行了 **12 名审计员的扫描**(经过对抗性验证);real-target 代理以及 组范围审批 + 拉取-释放随后作为经过审查的切片落地。 - 完整故事 + 切片图请查看[网站](https://rupivbluegreen.github.io/omni-sag/)和 [`docs/audit/`](docs/audit/)。 ## 🗺️ 路线图 - **v1(当前):** SSH + SFTP,真实 shell/SFTP 目标代理,AD+MFA,拨号器 authz,凭证链, 录制,内容检查 + 隔离,CyberArk 注入,双人原则(会话隧道 + 组范围的隔离-释放)与拉取-下载,按功能切换开关,API + CLI + TUI + 打包。 ✅ - **v1.x:** SSH 证书颁发机构,Kerberos/GSSAPI,用于 API 的真实 OIDC (JWKS) 验证器 (目前是静态 token 替代品),CRD 支持的策略/审批源(需要活动集群),通过 每个监听器的 FIPS TLS 配置路由。 - **v2:** RDP(原生 mstsc,然后是带录制的浏览器 RDP)。 - **绝不:** 共享进程的多租户,OSS 中的商业化机制,从像素中进行语义命令 重构。 ## 📜 许可证 [MIT](LICENSE) — 随意折腾。
*由过量的咖啡和对默认允许 (default-allow) 的极度不信任所打造。* ☕
标签:EVTX分析, Go语言, SSH网关, 内存分配, 堡垒机, 日志审计, 特权访问管理, 程序破解, 自定义请求头, 请求拦截, 身份认证与访问控制, 运维审计