Documentation
¶
Overview ¶
Package egressproxy 进程级出口强制:一个只监听回环的本地 HTTP 代理 (含 `CONNECT` 隧道),作为「声明了受限出口」的**兑现件**。
它补的是哪个洞 ¶
子进程(以及宿主起的 curl / git / 各种 native 工具)的出站流量在编排进程的 任何 Go 层守卫之外:`HTTP_PROXY` 只是一组环境变量,子进程可以忽略它。 于是「声明了受限出口」与「实际能出网」之间没有任何东西——直到有一个**真的 在中间拦着**的代理,并且子进程只能经它出去。
两层缺一不可:
- 代理(本包):真的拦、真的拒、默认拒绝;
- 环境下发(agent-harness 的 `egress.Spec.Env`):把子进程指过来。
只有其中一层都等于没有:代理没人用是装饰,env 没人拦是空话。
边界 ¶
- **只监听回环**(`127.0.0.1:0`),不对外提供服务;
- 只做**目标准入**(主机 + 端口 + 只读方法),不看内容——`CONNECT` 之后是 TLS,看不到任何字节,这一点在 `Limited` 的文档里写清,不假装能看见;
- 不认识任何业务概念:白/黑名单由调用方给,本包只判。
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BlockedError ¶
BlockedError 准入拒绝(结构化:调用方按需呈现原因与目标)。
func (*BlockedError) Error ¶
func (e *BlockedError) Error() string
type Policy ¶
type Policy struct {
// AllowHosts 允许的目标主机:精确主机名,或通配后缀 "*.example.com"。
//
// 通配语义**只匹配子域**:`*.example.com` 命中 `a.example.com` 与
// `a.b.example.com`,**不**命中 `example.com`(要它请显式写一条),
// 也**不**命中 `evilexample.com`(后缀按标签边界比,不做裸后缀比较)。
// 单独一个 "*" 表示放行全部主机(此时 `DenyHosts` 仍然优先)——
// 这是「只做黑名单」的唯一写法,必须显式写出来。
AllowHosts []string
// DenyHosts 拒绝的目标主机(语义同 AllowHosts,**优先于** AllowHosts)。
DenyHosts []string
// AllowPorts 允许的目标端口。空 ⇒ 只允许 443 与 80。
//
// 为什么空缺省不是「全部端口」:代理最常见的失手是「主机名单对了,
// 但放行了 22 或 3306」——端口是攻击面的一部分,必须显式开。
AllowPorts []int
// Limited 只读模式:**明文**请求只允许 GET / HEAD。
//
// ⚠️ 对 `CONNECT` 不生效且无法生效:CONNECT 之后是 TLS(或任意协议),
// 方法不可见。所以 Limited 对 HTTPS 只保证「目标主机在名单内」,
// 不保证「只读」——这是能力的真实边界,写在这里而不是留给使用者猜。
Limited bool
}
Policy 代理的放行面。
**默认拒绝**:`AllowHosts` 为空 ⇒ 任何目标都不放行(不是「不限」)。 这条缺省与 permgate / sandbox 的口径一致——沉默按最弱处理, 「没配置」绝不能被读成「放行」。
type Proxy ¶
type Proxy struct {
// contains filtered or unexported fields
}
Proxy 本地出口代理(HTTP 转发 + CONNECT 隧道)。
零值不可用:必须 `New` → `Start`。`Close` 幂等。
func (*Proxy) Egress ¶
func (p *Proxy) Egress() sandbox.EgressMode
Egress 实现 sandbox.EgressEnforcer:本代理能兑现的是 `proxy` 档。
**不声称 `denied`**:代理挡得住「经过它的流量」,挡不住绕开代理的原始 socket —— 声称禁网就是让档位表说谎。