-
Notifications
You must be signed in to change notification settings - Fork 0
Security
IPClick 的服务端会代替调用方请求任意 URL。这句话决定了它的威胁模型: 一台配置不当的 IPClick 就是一个开放的内网跳板。
四条防线,各管一件事,不能互相替代:
| 防线 | 回答的问题 |
|---|---|
| SSRF 防护 | 能打到哪儿 |
| 鉴权 | 谁能用 |
| TLS | 链路上能不能被看见/改掉 |
| 机密管理 | 凭据会不会漏出去 |
[SECURITY]
allowed_schemes = ["http", "https"]
block_metadata_endpoints = true # 默认开
block_private_networks = false # 默认关
allowlist = []拦截云厂商实例元数据地址(169.254.169.254 等)。这是 SSRF 最典型的目标——
一次请求就能拿到实例的 IAM 凭据。没有正当的代理用途,建议保持开启。
拦截回环 / 私网 / 保留地址。
默认关是为了兼容"本机代理本机服务"的用法(开发、内网工具链)。
部署到不可信网络时必须改成 true:
[SECURITY]
block_private_networks = true
allowlist = ["10.0.0.5", "internal-api.corp"] # 确实要放行的不开的话,任何能调你服务的人都能让它去请求 http://192.168.1.1/、
http://localhost:6379/(Redis 协议在 HTTP 之上也能被利用)等等。
只允许 http / https。file://、gopher://、dict:// 这些是 SSRF 的经典放大器。
不在白名单里的协议会得到 ValidationError,在发出去之前就被拦掉。
页面里的 JS 自己发的请求,走的是浏览器的网络栈,[SECURITY] 的 URL 策略对它
完全不起作用。
[BROWSER]
allow_scripts = false # 默认关,保持这样打开它等于把 SSRF 防线让开一整条。只有在确认调用方全部可信时才打开。
# .env
IPCLICK_AUTH_TOKEN=<一串够长的随机>Downloader(host="10.0.0.1", token="<同一串>")留空 = 不鉴权,服务端启动时会打显著告警:
未配置鉴权令牌,任何能连到本端口的调用方都可以使用本服务。
监听在非回环地址而不鉴权,等于把这台机器的出口 IP 和内网可达性送给任何人。
环境变量只接受单个令牌。轮换期间要新旧并存的话,只能写在配置里:
[SECURITY]
auth_token = ["新令牌", "旧令牌"]
allow_secrets_in_config = true # 否则每次启动都会被点名轮换完记得删掉旧的、改回环境变量。
集群内部用共享密钥派生每台的令牌,见集群:
token = HMAC-SHA256(secret, "ipclick-node:" + node_id)
每台令牌不同(拿到 B 的不能调 C),但只需要分发一个密钥。
grpc.health.v1 不需要令牌——K8s 探针和服务网格没法带凭据。它只返回
SERVING / NOT_SERVING,不泄露任何信息。不想暴露就 [MONITOR].health_check = false。
[SECURITY.tls]
enabled = false
cert_file = ""
key_file = ""
ca_file = ""
require_client_cert = false
server_name_override = ""默认关闭(明文)以兼容旧部署。但要清楚:令牌鉴权保护的是"谁能用", 明文链路上令牌本身会被同网段原样嗅探到。 公网或不可信网络务必打开。
[SECURITY.tls]
enabled = true
cert_file = "/etc/ipclick/server.crt"
key_file = "/etc/ipclick/server.key"[SECURITY.tls]
enabled = true
cert_file = "/etc/ipclick/server.crt"
key_file = "/etc/ipclick/server.key"
ca_file = "/etc/ipclick/ca.crt" # 验证客户端证书用
require_client_cert = true
⚠️ require_client_cert = true却不配ca_file会启动时直接报错。 那种组合下任何自签名证书都能通过,等于没验证——所以不会静默降级。
[SECURITY.tls]
enabled = true
ca_file = "" # 留空用系统信任库(对接公网 CA 时就该留空)
cert_file = "" # 只有服务端要 mTLS 时才填
key_file = ""
server_name_override = "" # 证书名字与连接地址不一致时用server_name_override 会跳过主机名匹配(比如证书签给 ipclick.internal 而你连
10.0.0.5)。只应在自签名证书的内网环境里用。
规则:.env 只放机密,ipclick.toml 只放行为。
# .env —— 600 权限,绝不进版本库
IPCLICK_AUTH_TOKEN=...
IPCLICK_WEB_USER=admin
IPCLICK_WEB_PASSWORD=...
IPCLICK_PROXY_AUTH_KEY=...
IPCLICK_PROXY_AUTH_PASSWORD=...
IPCLICK_CLUSTER_SECRET=...ipclick init 生成的 .env 是 600 权限,并自动追加到 .gitignore。
机密写进 ipclick.toml 仍然生效——受信环境里图省事是合理的。只是启动时会点名提醒:
ipclick.toml 通常要进版本库,机密会跟着进 git、进备份、进 CI 日志。
设 allow_secrets_in_config = true 可以关掉提醒。
两边都写时环境变量优先。
查每个机密来自哪里:
ipclick config-info 机密来源:
gRPC 鉴权令牌 环境变量 / .env
Web 管理端密码 配置文件 ⚠️ 建议改用环境变量
集群共享密钥 未配置
见 Web 管理端 · 安全设计。要点:
- 默认只监听
127.0.0.1 - 无默认口令,不设就随机生成
- CSRF、
HttpOnly+SameSite=Strict、登录限速 - CSP
default-src 'none';script-src用内联脚本的 sha256 哈希, 不是'unsafe-inline'——注入进来的<script>哈希对不上 就执行不了。connect-src 'self'把 fetch 限死在本源 - 从不显示任何机密。「生成凭据」也不破这条:生成的值只在那一次响应里出现, 服务端不保存、不写进任何文件
-
安全边界不可从网页修改 ——
[SECURITY]全部、Web 自己的凭据、集群密钥、allow_scripts。攻破 Web 登录不该等于能关掉 SSRF 防护
唯一放开的一项:能装 / 卸可选组件。 "装依赖要在机器上执行命令"值得停一下, 放开它的理由是:这个界面已经能改配置、能代发任意请求,再加上"装它自己声明过的 那五个可选依赖"没有实质性地扩大攻击面。约束仍然在:
- 包名只能来自白名单常量,表单传来的值必须精确匹配,绝不拼接;
- 命令以列表形式交给
subprocess(shell=False),做不到"执行任意命令"; - 绑定当前解释器,不依赖 PATH。
换句话说,它能装的东西是闭集,不是"任意 pip 包",更不是任意命令。
它本身是明文 HTTP,远程访问请走 SSH 隧道或 TLS 终止的反向代理。
[SERVER]
host = "10.0.0.5" # 不要 0.0.0.0,除非确实需要
[SECURITY]
block_private_networks = true # ← 最容易漏的一项
block_metadata_endpoints = true
allowlist = [] # 只列确实需要的
[SECURITY.tls]
enabled = true
cert_file = "..."
key_file = "..."
[BROWSER]
allow_scripts = false # 保持关闭
no_sandbox = false # 容器里不得不开时,理解代价
[WEB]
host = "127.0.0.1" # 走 SSH 隧道访问
[CLUSTER.status_page]
host = "127.0.0.1"# .env —— 600 权限,不进版本库
IPCLICK_AUTH_TOKEN=<32+ 字节随机>
IPCLICK_WEB_PASSWORD=<32+ 字节随机>
IPCLICK_CLUSTER_SECRET=<32+ 字节随机>生成随机串:
python -c "import secrets; print(secrets.token_urlsafe(32))"最后跑一遍 ipclick config-info 逐项核对——它显示的是什么真生效了,
不是配置文件里写了什么。
发现漏洞请不要开公开 issue,改用 GitHub 私密安全通报。