网络安全
Activepieces 如何隔离出站流量以保护你的内部网络
概述
Activepieces 在两个层面发起出站 HTTP 请求,每个层面都分别进行了加固:
- 工作流中的用户代码 — 代码步骤、Piece 动作、工作流作者可编写的任何内容。通过
AP_NETWORK_MODE加固;请参阅用户代码出口。 - API 的服务端 HTTP — OAuth 令牌获取/刷新、Vault、Conjur、事件目标 Webhook、On-Call 通知器、MCP 工具验证。始终经过过滤,通过
AP_SSRF_ALLOW_LIST调优;请参阅服务端出口。
两个层面都会阻止相同的 IP 集合(RFC1918 私有地址、回环地址、链路本地/云元数据地址、非单播地址),并共享 AP_SSRF_ALLOW_LIST 白名单。
用户代码出口
每个工作流最终都会运行用户提供的代码——代码步骤、Piece 动作、HTTP 请求。如果没有明确边界,这些代码可以访问宿主能够访问的任何内容:127.0.0.1、Redis、Postgres、Kubernetes API、云元数据端点(169.254.169.254)、VPC。AP_NETWORK_MODE 是控制此边界的开关。
| 值 | 效果 |
|---|---|
UNRESTRICTED | 无出站防护。用户代码可访问 Worker 能访问的任何主机。在加固栈验证完成前的默认值。 |
STRICT | 启用下文中所有三层保护。用户代码到私有、回环、链路本地和云元数据 IP 的出站连接被阻止。 |
相关环境变量:
AP_SSRF_ALLOW_LIST— 逗号分隔的 IP 列表,用于绕过阻止(例如内部数据库或 Sidecar)。HTTP_PROXY/HTTPS_PROXY— 如果设置,引擎将通过代理路由所有出站 HTTP(S) 流量。回环代理端口会自动豁免。
隔离工作原理
在 STRICT 模式下,三层保护叠加。每一层都是一道防线——如果请求通过了一层,下一层会将其拦截。
flowchart LR
UC["用户代码<br/>(代码步骤 · Piece · fetch)"]
subgraph L1["第 1 层 — 引擎 SSRF 防护(进程内)"]
direction TB
L1check{"IP 在被阻止<br/>范围内?"}
end
subgraph L2["第 2 层 — 出口代理(回环)"]
direction TB
L2check{"任何 A/AAAA<br/>记录被阻止?"}
end
subgraph L3["第 3 层 — iptables 封锁(仅 SANDBOX_PROCESS)"]
direction TB
L3check{"目标地址<br/>是代理端口?"}
end
NET(["公网"])
B1["SSRFBlockedError"]
B2["403 出口被阻止"]
B3["内核丢弃数据包"]
UC --> L1check
L1check -- 是 --> B1
L1check -- 否 --> L2check
L2check -- 是 --> B2
L2check -- 否 --> L3check
L3check -- 否 --> B3
L3check -- 是 --> NET
classDef blocked fill:#fde2e2,stroke:#d33,color:#7a1414
classDef allowed fill:#e3f7e4,stroke:#2a7,color:#14532d
class B1,B2,B3 blocked
class NET allowed
各沙箱模式的适用情况
网络安全层叠加在沙箱执行模式之上——它们是独立的选择。下表显示了各沙箱在 STRICT 模式下启用的功能:
| 沙箱模式 | 引擎 SSRF 防护 | 出口代理 | 内核封锁 |
|---|---|---|---|
无沙箱(UNSANDBOXED) | ✅ | ✅ | ❌ — 无可用的按 UID 隔离 |
V8 沙箱(SANDBOX_CODE_ONLY) | ✅ | ✅ | ❌ — 引擎进程与宿主共享 UID |
内核命名空间(SANDBOX_PROCESS + isolate) | ✅ | ✅ | ✅ — iptables 规则限定在沙箱 UID 范围内 |
请参阅沙箱了解各沙箱模式在进程级别的隔离方式。网络安全是正交的:它隔离沙箱可访问的内容,而不管其运行方式。
验证你的配置
在测试工作流的 Code 步骤中尝试:
const res = await fetch('http://169.254.169.254/latest/meta-data/')
设置 AP_NETWORK_MODE=STRICT 后,你应该会看到 SSRFBlockedError(来自引擎防护)或出口代理返回 403(针对通过主机名解析的请求)。设置为 UNRESTRICTED 时,如果宿主允许,请求将成功——确认防护已关闭。
逐步推广
- 从
UNRESTRICTED(默认)开始,识别合法工作流需要访问的内部服务(内部 API、代码步骤使用的数据库等)。 - 将这些 IP 添加到
AP_SSRF_ALLOW_LIST。 - 切换到
AP_NETWORK_MODE=STRICT。观察 Worker 日志中的SSRFBlockedError或Egress proxy refused request——每条记录要么是攻击、配置错误的工作流,要么是缺少白名单条目。
服务端出口
与流代码不同,API 服务器本身会代表管理员和用户发起出站 HTTP 请求——OAuth 令牌获取/刷新、Hashicorp Vault、CyberArk Conjur、事件目标 Webhook、On-Call 通知器、MCP 工具验证。URL 来自管理员配置(Vault 服务器 URL)或用户输入(Webhook 目标、MCP 服务器 URL),因此同样的 SSRF 风险依然存在。
与用户代码出口不同,这一层始终开启——它不需要 AP_NETWORK_MODE=STRICT。它实现为附加到 @activepieces/server-utils 中共享 axios 实例的 request-filtering-agent 包装器;每个出站请求都流经该包装器。被阻止的范围与引擎防护相同(RFC1918、回环、链路本地/云元数据、非单播)。
当请求被阻止时,管理 UI 中显示的 axios 错误会包含 AP_SSRF_ALLOW_LIST 提示,以便运维人员在连接测试对话框中直接看到修复方法。
使用私有 IP 的自托管服务提供者
如果 Vault、Conjur、本地 OAuth2 令牌端点或内部 Webhook 解析为私有 IP,服务端过滤器将拒绝连接,直到将目标添加到 AP_SSRF_ALLOW_LIST:
AP_SSRF_ALLOW_LIST=10.0.5.12,192.168.10.0/24
宽松 TLS 仍会过滤
接受自签名证书的连接器(例如私有集群中的 CyberArk Conjur)使用 rejectUnauthorized: false。在此设置下 SSRF 过滤器仍然保留——TLS 验证被放宽,但 SSRF 保护不会被绕过。