panduraju50/zero-trust-boundary-vault-cli
GitHub: panduraju50/zero-trust-boundary-vault-cli
该工具是一个零信任生产环境访问 CLI,通过整合 OIDC、HashiCorp Vault 和 Boundary 三层门控,实现基于短时动态凭证的安全代理会话,消除静态密钥风险。
Stars: 0 | Forks: 0
# zero-trust-boundary-vault-cli (`ztb`)
[](https://www.rust-lang.org/)
[](https://www.vaultproject.io/)
[](https://www.boundaryproject.io/)
[](https://openid.net/connect/)
[](./LICENSE)
## 问题陈述
大多数进入生产环境的事故都源于一开始就不应该存在的凭证:`.env` 文件中长期的数据库密码、超出人员使用需求的个人 SSH 密钥、被粘贴到操作手册中的云访问密钥。**长期存在的静态凭证是生产运维中最大的可避免攻击面。** 它们可能会被窃取,很少能按时轮换,难以进行归因,并且单个泄露的凭证所授予的权限往往远超当前任务所需。
更安全的模型是**零信任、即时、代理访问**:
- **默认不信任任何事物。** 每次访问请求都会针对企业身份提供商 (OIDC) 重新证明操作者*是谁*,而不是依赖于对密钥的持有。
- **凭证按需生成并迅速失效。** Vault 会签发一个具有短 TTL 的*动态* secret,以取代静态密码,并在任务结束时在源头将其撤销。
- **操作者永远不会直接路由到目标。** HashiCorp Boundary 负责代理会话:目标的网络位置永远不会暴露,并且连接可以被集中授权、限制时间以及取消。
这也提供了一个清晰的**气隙 / 破窗**方案。生产目标位于 Boundary worker 之后,没有来自操作者网络的入站路径;唯一的进入方式是获得新授权、完全审计且短暂的会话,该会话携带一分钟前不存在、一分钟后也将不存在的凭证。
`ztb` 是一个单一的 Rust CLI,它将这三层缝合在一个命令中,因此操作者的*简单*路径同时也是*安全*的路径。
## 威胁模型 / 安全模型
### 这可以防御什么
| 威胁 | 缓解措施 |
| --- | --- |
| **凭证窃取 / 泄露** | 没有可窃取的静态 secret。凭证是动态的、短 TTL 的,并在拆除时撤销。token 存储在 OS keyring 中,从不存储在磁盘或日志中。 |
| **静态 / 过度访问** | 每次访问都需要实时的 OIDC 登录;Vault 角色遵循最小权限原则;Boundary 会话有时间限制且可集中撤销。 |
| **横向移动** | Boundary 仅将会话代理给特定授权的目标;操作者没有通往更广网络的路由,也没有可用来进行跳板攻击的可重用凭证。 |
| **授权码拦截** | 带有 **PKCE** (S256) 的 OIDC Authorization Code 流;CLI 是一个公共客户端,没有可泄露的 client secret。 |
| **OIDC 回调上的 CSRF** | 在环回重定向上验证随机的 `state` 参数。 |
| **ID token 重放** | 请求中嵌入随机 `nonce` 并在返回的 ID token 中进行验证;根据签发者的 JWKS 验证签名和标准声明。 |
| **中间人攻击** | 始终强制执行 TLS 验证;Vault CA 通过配置进行固定。 |
| **事后归因缺口** | 每一层都会发出审计事件 —— IdP 登录、Vault lease 签发、Boundary 会话授权 —— 并以相同的已验证身份为键。 |
### 多层门控
只有在 **所有三个** 独立层按顺序成功时,才会授予访问权限。任何一层的失败都会拒绝会话;没有回退到较弱路径的机制。
- **第 1 层 —— 身份 (OIDC)。** 操作者通过 Authorization Code + PKCE 针对企业 IdP(例如 Google Workspace)进行身份验证。返回的 ID token 会被验证(通过 JWKS 验证签名、`iss`、`aud`、`exp`、`nonce`)。这确立了*谁*在进行请求。Group/claim 成员资格驱动下游的所有操作。
- **第 2 层 —— 凭证门控。** 已验证的 OIDC token 通过 JWT/OIDC auth method 登录到 Vault,将身份映射到**最小权限的 Vault 角色**上。该角色授权从 secrets engine 请求*动态* secret(数据库、SSH、云、PKI),并按需签发且具有短 TTL 和硬性 `max_ttl`。不存在处于静态、容易被攻破的 secret。
- **第 3 层 —— 会话代理。** 相同的身份向 Boundary controller 进行身份验证,后者授权到特定目标的会话。Boundary worker 将连接代理到**仅限环回**的本地代理。在支持的情况下,Vault 签发的凭证会作为会话的代理凭证注入,因此操作者永远不会直接处理 secret。
### 原则
- **最小权限** —— Vault 角色和 Boundary 目标根据已验证的身份声明,授予完成任务所需的最小范围权限。
- **短 TTL** —— 凭证的生命周期以分钟为单位,并在服务端设有上限。
- **磁盘上无 secret** —— token 进入 OS keyring;没有任何敏感信息被写入文件、环境变量或日志。
- **显式拆除** —— `logout`/退出会话会主动撤销 lease 并取消会话,这建立在自动 TTL 过期的基础之上(纵深防御)。
- **可审计性** —— 相同的已验证身份贯穿每一层。
## 架构
```
ztb connect
┌────────┐ │
│Operator│ ▼
└───┬────┘ ┌───────────────────┐
│ local loopback proxy │ ztb CLI │
│ (127.0.0.1 only) │ (Rust, clap) │
│◄────────────────────────────┤ │
│ └─────────┬─────────┘
│ │
│ Layer 1: OIDC (Auth Code + PKCE, state + nonce)
│ ▼
│ ┌───────────────────────┐
│ │ OIDC IdP │ ID/access token
│ │ (Google Workspace) │ TTL ~1h, in keyring
│ └───────────┬───────────┘
│ │ verified access token
│ Layer 2: Vault JWT/OIDC login ▼
│ ┌───────────────────────┐
│ │ HashiCorp Vault │ dynamic secret
│ │ role: least-priv │ TTL ~15m (max_ttl)
│ │ engine: db/ssh/pki │ leased, revocable
│ └───────────┬───────────┘
│ │ short-lived credential
│ Layer 3: authorize + broker ▼
│ ┌───────────────────────┐
│ │ HashiCorp Boundary │ session
│ │ controller + worker │ time-boxed,
│ │ cred injection │ cancelable
│ └───────────┬───────────┘
│ │ brokered session
│ ▼
│ ┌───────────────────────┐
└──────────────────────────►│ Target host / DB │ (air-gapped;
(proxied through worker) │ (no direct route) │ no inbound path)
└───────────────────────┘
Lifetimes (illustrative; enforced server-side):
OIDC access token ...... ~1h, cached in OS keyring, never on disk
Vault dynamic secret ... ~15m, hard max_ttl, revoked on teardown
Boundary session ....... time-boxed by target config, centrally cancelable
```
## CLI 用法
```
ztb [OPTIONS]
```
### 命令
| 命令 | 描述 |
| --- | --- |
| `ztb login` | 通过 OIDC(Authorization Code + PKCE)进行身份验证;将 token 缓存在 OS keyring 中。 |
| `ztb connect ` | 重新验证身份,请求短暂的 Vault 凭证,并代理到 `` 的 Boundary 会话。 |
| `ztb status` | 显示当前身份、凭证 TTL 和活动会话。 |
| `ztb whoami` | 从缓存的 OIDC token 中打印身份(subject/email)。 |
| `ztb logout` | 撤销 Vault lease,取消 Boundary 会话,并清除 keyring。 |
### 全局选项
| 标志 | 环境变量 | 描述 |
| --- | --- | --- |
| `--vault-addr ` | `VAULT_ADDR` | HashiCorp Vault 地址。 |
| `--boundary-addr ` | `BOUNDARY_ADDR` | Boundary controller 地址。 |
| `--oidc-issuer ` | `ZTB_OIDC_ISSUER` | OIDC 签发者 URL。 |
| `--ttl ` | — | 请求的凭证 TTL(例如 `15m`);受 Vault 角色上限限制。 |
| `--json` | — | 输出机器可读的 JSON。 |
| `-v`, `-vv` | `RUST_LOG` | 增加日志详细程度。 |
### 示例
```
# 1) 登录到企业 IdP(打开浏览器;PKCE loopback flow)。
ztb login \
--oidc-issuer https://accounts.google.com \
--client-id ztb-cli.apps.googleusercontent.com
# 2) 向生产数据库目标代理一个 10 分钟的会话。
ztb connect prod/orders-db-readonly \
--vault-addr https://vault.internal:8200 \
--boundary-addr https://boundary.internal:9200 \
--ttl 10m
# 3) 以 JSON 格式检查当前状态(例如用于工具 / 仪表板)。
ztb status --json
# 4) 根据缓存的 token,我是谁?
ztb whoami
# 5) 销毁一切:撤销 leases、取消会话、清除 keyring。
ztb logout
```
## 配置
`ztb` 按优先级从低到高的顺序读取配置:
1. `~/.config/ztb/config.toml`
2. 环境变量
3. 命令行标志
查看 [`config.example.toml`](./config.example.toml) 获取完整模板。
配置文件仅包含**地址和非机密标识符** —— 绝不包含 token 或密码。
```
# ~/.config/ztb/config.toml(节选)
[oidc]
issuer = "https://accounts.google.com"
client_id = "ztb-cli.apps.googleusercontent.com"
callback_port = 8477
scopes = ["openid", "email", "profile"]
[vault]
addr = "https://vault.internal.example.com:8200"
auth_mount = "oidc"
role = "prod-operator"
secret_path = "database/creds/prod-readonly"
ttl = "15m"
ca_cert = "/etc/ztb/vault-ca.pem"
[boundary]
addr = "https://boundary.internal.example.com:9200"
auth_method_id = "amoidc_1234567890"
default_scope = "global"
```
### 环境变量
| 变量 | 用途 |
| --- | --- |
| `VAULT_ADDR` | Vault 地址(覆盖 `[vault].addr`)。 |
| `BOUNDARY_ADDR` | Boundary controller 地址。 |
| `ZTB_OIDC_ISSUER` | OIDC 签发者 URL。 |
| `ZTB_OIDC_CLIENT_ID` | OIDC 公共客户端 ID(由 `ztb login` 使用)。 |
| `RUST_LOG` | 日志过滤器(例如 `ztb=debug`);覆盖 `-v`。 |
## 快速开始
### 前置条件
- **Rust** 1.74+ (`rustup`)。
- 一个 **OIDC provider**(例如 Google Workspace、Okta、Keycloak),并注册了一个用于环回重定向(`http://127.0.0.1:/callback`)的公共客户端。
- 一台启用了 JWT/OIDC auth method、配置了最小权限角色以及动态 secrets engine(数据库/SSH/PKI/…)的 **HashiCorp Vault** 服务器。
- 一个带有 OIDC auth method 且至少包含一个目标(最好配置了 Vault 凭证注入)的 **HashiCorp Boundary** controller + worker。
### 构建与安装
```
git clone https://github.com/panduraju50/zero-trust-boundary-vault-cli.git
cd zero-trust-boundary-vault-cli
# 构建
cargo build --release
# 原地运行
./target/release/ztb --help
# 或者安装到 ~/.cargo/bin
cargo install --path .
```
### 首次运行
```
mkdir -p ~/.config/ztb
cp config.example.toml ~/.config/ztb/config.toml
# 修改 issuer / client_id / vault 和 boundary 地址以匹配你的 env
ztb login
ztb connect
```
## 安全考量
- **Token 存储。** OIDC token 和任何缓存的 Vault token 都存储在 OS keyring 中(macOS Keychain / Secret Service / Windows Credential Manager),**绝不**以明文文件或环境变量的形式存储。
- **PKCE (S256)。** CLI 是一个公共 OAuth 客户端。PKCE 将 token 交换绑定到初始请求,并消除了对 client secret 的任何需求。
- **State 与 nonce。** `state` 保护回调免受 CSRF 攻击;`nonce` 保护 ID token 免受重放攻击。在身份被信任之前,这两者都会被验证。
- **TLS 验证。** 始终强制执行。Vault CA 可以通过配置进行固定;不提供任何类似 `--insecure` 的绕过方式。
- **不记录凭证。** 机密材料被视为不透明数据,并从所有的 tracing/log 输出中进行脱敏处理。
- **会话拆除。** `ztb logout` 和正常的会话退出会主动撤销 Vault lease 并取消 Boundary 会话,在自动 TTL 过期的基础上将暴露窗口缩小至接近零。
- **设计上的最小权限。** 授权由已验证的 OIDC 声明驱动,并映射到范围严格的 Vault 角色和特定的 Boundary 目标。
## 路线图
- [ ] 完善基于 JWKS 的 OIDC PKCE 环回流程和 ID token 验证。
- [ ] Vault JWT/OIDC 登录 + 动态 secret 签发(database/SSH/PKI 引擎)。
- [ ] Boundary 会话授权、代理和凭证注入。
- [ ] 由真实 keyring + controller 状态提供支持的 `status`/`whoami`。
- [ ] 拆除时显式的 lease 撤销和会话取消。
- [ ] 针对无头/跳板机环境的可选 device-code 流程。
- [ ] 面向 SIEM 摄取的结构化审计事件发射 (JSON)。
- [ ] 强化的配置验证和 CA pinning 测试。
- [ ] 针对开发模式 Vault + Boundary 的端到端集成测试。
## 许可证
基于 [Apache License 2.0](./LICENSE) 授权。
## 作者
**Panduranga Rajau M (Pandu)** — Senior SRE / DevSecOps,东京。CKS 持有者。
[GitHub](https://github.com/panduraju50) ·
[LinkedIn](https://www.linkedin.com/in/pandurangaraju/)
标签:HashiCorp Boundary, HashiCorp Vault, OIDC认证, Python安全, Rust, 动态密钥管理, 可视化界面, 网络流量审计, 运维工具, 通知系统, 零信任网络