Geogboe/boxy
GitHub: Geogboe/boxy
Boxy 是一个资源池化与沙盒编排工具,通过预先预热 VM 和容器资源池实现按需秒级沙盒环境交付。
Stars: 1 | Forks: 0
# Boxy
Boxy 是一个资源池化和沙盒编排工具。它预先配置 VM、容器和其他资源池,然后将它们组装成按需提供的沙盒,用于实验室、培训、渗透测试和开发环境。
## 安装
Windows PowerShell、Linux 和 macOS 均提供了发布版安装程序。它们会下载最新发布的 GitHub 版本,根据已发布的 `checksums.txt` 对其进行验证,并将其安装到用户本地的 bin 目录中。
Windows PowerShell:
```
irm https://raw.githubusercontent.com/Geogboe/boxy/main/scripts/install.ps1 | iex
```
Linux / macOS:
```
curl -fsSL https://raw.githubusercontent.com/Geogboe/boxy/main/scripts/install.sh | sh
```
有关支持的平台、版本固定、环境覆盖、PATH 行为和验证的详细信息,请参阅 [docs/install.md](docs/install.md)。
## 工作原理
Boxy 会提前保持通用且即用型资源池处于热备用状态。当用户请求沙盒时,会从池中抽取资源,并通过 hooks 进行个性化设置——设置凭据、配置网络并返回连接信息。用户使用其原生客户端(SSH、RDP、SMB 等)进行连接。Boxy 不是代理。
```
┌──────────────────────────────────────────┐
│ boxy serve │
│ │
│ REST API (CLI) gRPC server (agents) │
│ │ │ │
│ ┌────▼───────────────────▼────────────┐ │
│ │ Core: Pool Manager, Sandbox Mgr │ │
│ │ PolicyController (reconciler) │ │
│ └─────────────────┬───────────────────┘ │
│ │ │
│ ┌─────────────────▼───────────────────┐ │
│ │ Embedded local agent │ │
│ │ (Docker, Hyper-V drivers) │ │
│ └─────────────────────────────────────┘ │
└──────────────────────────────────────────┘
Remote host:
┌──────────────────────────────────────────┐
│ boxy agent │
│ gRPC → server | auto-discovered │
│ | provider drivers │
└──────────────────────────────────────────┘
```
## 核心领域模型
**Resource** — 已配置实例(VM、容器、共享、网络等)的运行时记录。具有 ID、类型、状态、提供者句柄和属性。资源是一次性的:一旦分配给沙盒,它们就永远不会返回到池中 (ADR-0002)。资源还保留不可变的池来源,以便 daemon 即使在分配将它们从就绪清单中移除后也能对容量进行评估。
**Pool** — 命名的、同质的、预配置的资源清单。在配置中声明。每个池都带有自己的配置(`type` 标识提供者/驱动程序,`config` 是由驱动程序解释的不透明数据块)和策略(预热、回收)。池即是规范——没有单独的“蓝图”、“模板”或“规范”实体。
**Sandbox** — 一个面向用户的环境,包含从池中提取的 1..N 个资源。沙盒创建在服务器端是异步的:API 在 `pending` 状态下持久化沙盒请求,reconcile 循环履行该请求,并且沙盒转换为 `ready` 或 `failed`。当资源从池转移到沙盒时,会运行分配后 hooks 以对其进行个性化设置(设置凭据、配置网络等)。Boxy 向用户返回连接信息;它不是代理。
**Provider** — 提供资源的外部系统(Docker、Hyper-V、Podman、VMware 等)。提供者具有映射到驱动程序的类型。提供者连接详细信息(socket、host、certs)由 agent 拥有,而不是服务器。驱动程序会尽可能自动发现其环境。
**Driver** — 知道如何与特定提供者类型通信的代码。解释池的预配配置。位于 `pkg/providersdk/drivers/` 中。驱动程序会自动发现其环境(例如,Docker 检查本地 socket,Hyper-V 通过 PowerShell 发现)。池的 `type` 字段直接映射到驱动程序(例如,`type: docker` → Docker 驱动程序,`type: hyperv` → Hyper-V 驱动程序)。Docker 池在首次预配时会自动拉取缺失的镜像,而无需手动执行 `docker pull`。
**Agent** — 使用驱动程序执行提供者操作的运行时实体。可以是:
- **Embedded (local):** 在 `boxy serve` 内部运行,处理在 `server.providers` 中声明的提供者。
- **Remote (distributed):** 在单独的主机上运行,通过 TLS 上的 gRPC 连接到服务器,自动发现本地提供者。在 `agents:` 配置部分中声明,以便服务器知道预期内容。
Agent 是执行层——Boxy 核心通过 agent 委托所有提供者 IO,从不直接委托给驱动程序。`Provisioner` 接口是 agent 的接缝。
**PolicyController** — 协调器。在 `boxy serve` 内以 tick 为周期运行。将期望的池状态(来自策略)与实际状态进行比较,并通过 agent 触发预配/销毁。无状态且幂等——每个 tick 都会从头重新推导所需内容。一个控制器协调所有池。
**Hooks** — 在事件发生后运行的副作用通知。不属于控制流——hooks 用于“资源已预配 -> 发送 webhook”或“沙盒已分配 -> 设置凭据”,而不是用于触发预配本身。位于 `pkg/hooks/` 中。
## 架构
### Server、CLI 和 Agent(类 Vault 模型)
单个二进制文件 (`boxy`),三种模式:
```
boxy serve — daemon: pool reconciler, REST API, gRPC agent server
boxy — CLI client: talks to daemon via REST
boxy agent — distributed agent: connects to server via gRPC
```
**REST API** — 用于 CLI 到服务器的通信。标准 HTTP REST。
**gRPC over TLS** — 用于 agent 到服务器的通信。双向流:agent 拨号连接到服务器(对 NAT/防火墙友好),服务器通过流推送工作。
### 池路由
池的 `type` 字段标识提供者类型(例如,`docker`、`hyperv`、`podman`、`vmware`)。系统将工作路由到具有匹配提供者的任何 agent(嵌入式或远程)。抽象资源类别(容器、VM)是从驱动程序的功能中派生的,而不是在池上声明的。
如果多个 agent 支持相同的提供者类型,系统会选择一个有能力的 agent。池上的可选 `agent:` 字段可以在需要时将其固定到特定的 agent。
### 协调流
```
serveLoop ticker
└─ PolicyController.Reconcile(pool)
observes: pool has 1 ready, policy says min_ready=3
gap: need 2 more
└─ pool.Manager.EnsureReady(pool, count=2)
└─ Provisioner.Provision(pool) ← agent impl
└─ driver.CreateVM / CreateContainer
```
### 池构建缓存(跨池资源复用)
预配器可以从其他池中“窃取”多余的资源,当它们共享兼容的配置时,而不是从头开始构建。兼容性在运行时自动发现——池之间不需要显式的 `base:` 引用。
**匹配规则:**
- 相同类型,配置是子集 → 缓存命中
- 仅限盈余:仅当 `X.ready > X.policy.preheat.min_ready` 时才从池 X 窃取
- 如果找到匹配项,则提取资源并应用增量(安装包、配置等)
- 如果没有匹配项,则从头开始构建
配置比较是结构性的——Boxy 核心比较不透明的配置数据块,而不理解其内容。YAML 锚点 (`&`/`*`) 可用于配置文件中的 DRY(不要重复自己),而不会创建 Boxy 级别的耦合。
### 分配后 Hooks
当资源从池转移到沙盒时,会运行 hooks 进行个性化设置:
- 设置用户凭据
- 配置主机名/网络
- 应用沙盒特定策略
池中的资源是有意保持通用的(没有特定用户,没有凭据)。Hooks 在分配时使它们变得具体。这意味着凭据在分配之前不存在——它们由 hook 生成/设置,并作为连接信息返回。
### 异步沙盒 API 流程
沙盒创建是一个服务器端的异步工作流:
1. 客户端 `POST` 沙盒请求到 `/api/v1/sandboxes`
2. 服务器持久化沙盒并返回 `202 Accepted` 和 `status: "pending"`
3. Daemon reconcile 循环预配并分配资源
4. 客户端轮询 `GET /api/v1/sandboxes/{id}`,直到状态变为 `ready` 或 `failed`
创建 API 使用资源请求而不是已分配的资源 ID:
```
{
"name": "pentest-lab",
"requests": [
{"type": "container", "profile": "kali", "count": 3},
{"type": "container", "profile": "ubuntu-targets", "count": 1}
]
}
```
### 沙盒访问模型
Boxy 不是代理。当沙盒达到 `ready` 状态时,Boxy 返回每个资源的连接信息:
- Linux VM 的 SSH 主机/端口/密钥
- Windows VM 的 RDP 地址
- 文件共享的 SMB 路径
- 容器 exec/attach 详细信息
- 等等
用户使用其原生客户端进行连接。连接信息由分配后 hooks 生成。
## 配置
### 服务器配置 (`boxy.yaml`)
三个顶级部分:`server`(嵌入式 agent 和服务器设置)、`agents`(服务器期望的远程 agent)和 `pools`(应该运行的内容)。
```
server:
listen: ":9090"
providers: [docker, hyperv]
agents:
- name: build-host
providers: [docker]
pools:
- name: win2022-base
type: hyperv
config: &win2022
template: "Windows Server 2022 Standard"
generation: 2
cpu: 4
memory_mb: 8192
disk_gb: 80
network_switch: "LabSwitch"
policy:
preheat:
min_ready: 5
max_total: 10
recycle:
max_age: 168h
- name: kali
type: docker
config:
image: kalilinux/kali-rolling
command: ["/bin/bash"]
policy:
preheat:
min_ready: 3
max_total: 8
```
**关键设计决策:**
- `server.providers` 声明嵌入式本地 agent 处理的内容。驱动程序自动发现连接详细信息(socket 路径、PowerShell 等)——不需要连接配置。
- `agents:` 声明服务器应期望的远程 agent。如果已声明的 agent 未连接,服务器可以发出警告/警报。远程 agent 通过 GitHub issue 跟踪器中跟踪的 token 引导流程进行身份验证。
- 池 `type` 是提供者类型(`docker`、`hyperv`、`podman`、`vmware`)。它直接映射到驱动程序并确定路由。抽象资源类别(容器、VM)派生自驱动程序的功能。
- 池 `config:` 是由驱动程序解释的不透明数据块。不同的提供者公开不同的配置选项。
- 规范/蓝图不是一个单独的实体。池以内联方式拥有其预配配置。
- 配置是无状态且声明式的。运行时状态(资源、沙盒)位于状态存储 中。配置在启动时读取。
### 池策略结构
```
policy:
preheat:
min_ready: N # target number of ready resources
max_total: N # hard cap across ready + allocated resources from this pool
recycle:
max_age: "168h" # destroy and replace unused resources older than this
```
实现说明:预热/回收规划逻辑有意保留在
`internal/pool` 中(不作为公共 `pkg/` API 公开),因为此策略
是 Boxy 特定的领域行为,而不是通用的可重用 SDK 契约。
### 沙盒定义 (`.sandbox.yaml`)
沙盒类在单独的文件中定义,而不是在服务器配置中。沙盒定义指定从哪些池提取资源以及提取多少:
```
# pentest-lab.sandbox.yaml
name: pentest-lab
resources:
- pool: kali
count: 3
- pool: ubuntu-targets
count: 1
```
沙盒通过 CLI 实例化:
```
# 从文件 (主路径)
boxy sandbox create -f pentest-lab.sandbox.yaml
# 在 daemon 接受请求后返回,而不是等待 ready/failed
boxy sandbox create -f pentest-lab.sandbox.yaml --no-wait
```
基于文件的路径是主要的、可重复的、版本控制的方式。CLI 将规范中的池引用编译为 daemon API 请求,将它们提交给 `boxy serve`,并默认等待终端沙盒状态。如果匹配的池已耗尽其 `max_total` 硬上限,沙盒请求将以 `status: "failed"` 失败,而不是超出上限进行预配。
有关完整配置,请参见 [examples/](examples/)。
## 状态存储
**bbolt**(纯 Go 嵌入式 K/V)用于运行时状态:资源、沙盒、agent 注册。配置(服务器、agent、池)不存储在数据库中——它在启动时从 `boxy.yaml` 读取。
## CLI 接口
有关带有标志和示例输出的权威 CLI 参考,请参见 [`docs/cli-wireframe.md`](docs/cli-wireframe.md)。
```
boxy init — create starter boxy.yaml in current directory
boxy serve — start the daemon (API server + reconcile loop)
boxy status — check server health and summary
boxy config validate — validate config file and exit
boxy sandbox create -f — create sandbox from a spec file (waits by default; use --no-wait to return after acceptance)
boxy sandbox list — list sandboxes
boxy sandbox get — get sandbox details
boxy sandbox delete — delete a sandbox
```
**计划中(尚未实现):**
```
boxy agent list — list agents and connection status
boxy agent token create — create registration token
boxy agent revoke — revoke an agent
```
池是配置驱动的——没有 `boxy pool create` 命令。池状态可通过 API 和 Web 仪表板观察。
## 项目布局
```
cmd/boxy/ — entry point
internal/
cli/ — CLI command wiring (cobra)
config/ — config loading
model/ — core domain types
pool/ — pool manager + Provisioner interface
sandbox/ — sandbox manager
store/ — store interface + bbolt impl
agent/ — agent types and runner
pkg/
hooks/ — hook runner (public, self-contained)
policycontroller/ — reconciler (public, self-contained)
providersdk/ — driver interface + capabilities (public API for driver authors)
drivers/
docker/
hyperv/
process/
resourcepool/ — generic pool data structure (public utility)
```
`internal/` = Boxy 的私有业务逻辑。
`pkg/` = 自包含,没有 `internal/` 依赖。编译器强制执行此边界。
## 待解决问题
- **配置重载:** 配置更改时需要重启,还是 `boxy serve` 应该监视更改并进行协调?重启更简单;热重载更好。
- **构建缓存的子集匹配:** 对不透明配置数据块进行结构比较,以确定一个是否是另一个的“子集”。确切的语义是什么?浅层键比较是否足够,还是我们需要深度的结构比较?
- **CLI 用户身份验证:** 谁被允许请求沙盒?基于 Token?OIDC?目前超出范围?
- **多 agent 路由:** 当多个 agent 支持相同的提供者类型时,服务器如何选择?轮询?基于负载?标签?目前,池上的 `agent:` 固定是逃生出口。
## 状态
早期开发阶段。有关即将开展的工作的设计细节,请参见 GitHub issue 跟踪器。
## 许可证
TODO
标签:EVTX分析, gRPC, OpenCanary, pocsuite3, Python工具, RDP, REST API, SMB, SSH, 安全测试, 实验环境搭建, 容器管理, 开发环境, 批量测试, 攻击性安全, 日志审计, 沙箱化工具, 虚拟化技术, 虚拟机编排, 请求拦截, 资源池化, 资源预热, 跨平台工具, 软件测试, 错误配置检测, 隔离环境, 靶场环境