Kiln 用两层配置管理出站流量:[[proxies]] 定义可复用的代理,[egress] 决定哪些请求使用哪个出口。节目源、节目单和台标共用全局路由,但频道与节目单源可以单独覆盖。
模型总览
每个待请求的地址都会独立走一次判定:
按优先级匹配规则
规则按 priority 升序遍历,命中的第一条决定出口,后面的规则不再参与。
使用默认出口
没有任何规则命中时使用 egress.default。
解析线路
出口是 direct 就直连,是某条线路的 id 就走该线路。启动配置引用不存在或已停用的线路时进程会拒绝启动;管理端保存同类无效设置时会拒绝更新并保留当前运行配置。
资源级设置优先于全局规则:
| 资源 | auto |
direct |
具体线路 |
|---|---|---|---|
| 频道 | 按 channel_id 和 URL 走全局规则 |
始终直连 | 固定走该线路 |
| EPG 源 | 按 URL 走全局规则 | 始终直连(默认) | 固定走该线路 |
| 台标 | 继承所属频道的出口上下文 | 继承频道 | 继承频道 |
频道编辑页的「出站线路」与 EPG 源编辑页的 proxy 都提供 auto、direct 和具体线路;只有选 auto 才继续走下面的全局规则。
[[proxies]]
id = "proxy-http"
name = "HTTP 线路"
url = "http://127.0.0.1:7890"
[[proxies]]
id = "proxy-socks"
name = "SOCKS5 线路"
url = "socks5h://127.0.0.1:7891"
[egress]
default = "direct"
playlist_policy = "rewrite"
# docker_proxy_host = "host.docker.internal"
[[egress.rules]]
id = "example-cdn"
priority = 10
kind = "host_suffix"
pattern = "example-cdn.com"
proxy = "proxy-http"定义线路
id唯一,被egress.default、路由规则和节目单源引用;name只用于界面显示。url支持http、https、socks5、socks5h四种 scheme,例如http://127.0.0.1:7890或socks5h://127.0.0.1:7891。- 需要认证时把凭据放进 URL,例如
http://user:password@127.0.0.1:7890,控制台会显示该线路已配置凭据而不回显密码。 - 所有线路都先由 Kiln 本机解析并校验目标,再把固定 IP 交给直连或代理,同时保留原始 HTTP Host 与目标 TLS SNI。
socks5h继续作为兼容 scheme 接受,但目标同样使用本机解析出的固定 IP,不再让代理二次解析。 - HTTPS 上游经 HTTP 或 HTTPS 转发代理时,CONNECT 目标是校验后的固定 IP。明文 HTTP 上游无法用 Go 的标准转发代理同时表达固定拨号 IP 与原始 Host,因此这个组合会在发起连接前被拒绝;请改用 HTTPS 上游或 SOCKS5 线路。
- 线路可以停用而不删除。被
egress.default或启用中的规则引用的线路不允许处于停用状态。
宿主机整机启用 TUN 或 fake-IP、任何进程都拿不到真实地址时,本机解析这条前提不成立。这种部署可以设 egress.trust_proxy_dns = true,把域名原样交给代理解析,恢复 v1.1.0 之前的行为:
[egress]
default = "proxy-http"
trust_proxy_dns = true默认出口与规则
要让全部回源默认走代理,先定义 [[proxies]],再把 egress.default 设为这条线路的 ID。Kiln 不读取 HTTP_PROXY、HTTPS_PROXY 或 ALL_PROXY 作为出站配置。
egress.default 取 direct 或某条线路的 ID,所有未命中规则的请求都走它。
规则由 id、priority、kind、pattern、proxy 组成,可以单独停用。priority 数字越小越优先,proxy 也可以填 direct,用来在默认走代理的部署里为个别域名开一个直连口子。
kind |
匹配对象 |
|---|---|
host_suffix |
主机名后缀。example.com 同时命中 example.com 与 cdn.example.com,不会命中 notexample.com |
host_exact |
完整主机名,需要完全相等 |
host_regex |
对主机名做正则匹配 |
channel_id |
频道 ID 完全相等 |
url_regex |
对完整地址做正则匹配 |
省略 kind 时按 host_suffix 处理。正则类规则在保存前会做编译校验,编译失败的规则不会生效。
播放列表处理
playlist_policy 决定 Kiln 转发上游 HLS 播放列表时,里面的源站绝对地址如何呈现给播放器。
| 取值 | 行为 |
|---|---|
rewrite(默认) |
全部改写回 Kiln,播放器只与 Kiln 通信 |
passthrough |
原样保留源站地址,播放器直连源站 |
auto |
改写需要走代理的地址;频道配置了自定义 User-Agent 或有效上游请求头时,全部地址都改写回 Kiln |
rewrite 的具体行为是:Kiln 把播放列表里的每条媒体地址,以及标签中的 URI="...",先解析成绝对地址,再替换成自身的 /v1/play/<频道 ID>/u/<编码后的地址>?sig=...;通过播放密钥分发时,前缀相应变成 /p/<密钥>/play/<频道 ID>/u/。地址用 URL-safe Base64 编码并保留可识别的扩展名,签名是按频道 ID 与目标地址计算的 HMAC,密钥在进程启动时随机生成,因此重启后旧地址立即失效。播放器取到这些地址后仍然只连 Kiln,回源由 Kiln 按出站路由完成。嵌套的变体播放列表在被取回时会再走一次同样的改写。
passthrough 让播放器直接访问源站,这些请求不会经过 Kiln 的代理线路,也不会带上频道自定义的请求头,只有在播放器自身能够直连源站时才适用。auto 通常只把需要走代理的地址改写回 Kiln;如果频道配置了自定义 User-Agent 或任何有效上游请求头,则所有地址都会改写,否则播放器直连的请求会丢失这些配置。
Kiln 的出站主机校验只应用于改写为 /u/、随后由 Kiln 回源的目标。passthrough 与 auto 中保留的源站地址由播放器直接访问,不会经过 Kiln 的 SSRF 校验。这是直连策略本身的边界,使用不受信任的上游播放列表时应选择 rewrite。
在容器里访问代理
Kiln 不会改写线路里的地址。在容器里运行时,url 必须是从 Kiln 容器内部连得上的地址:宿主机上好用的 127.0.0.1 在容器里指向容器自己,直接照抄会连不通。
代理跑在宿主机上
用 host.docker.internal 回连宿主机。这个名字在 Docker Desktop 上自带,Linux 上需要显式映射到网关:
services:
kiln:
image: ghcr.io/babywbx/kiln:core
ports:
- "8080:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./kiln.toml:/etc/kiln/kiln.toml:ro
- kiln-data:/var/lib/kiln/data
restart: unless-stopped
volumes:
kiln-data:线路填这个主机名,端口用代理在宿主机上监听的端口:
[[proxies]]
id = "proxy-host"
name = "宿主机代理"
url = "http://host.docker.internal:7890"代理是另一个容器
两个容器接在同一个 Docker 网络上时,直接用服务名寻址,代理不需要把端口映射到宿主机:
services:
proxy:
image: your-proxy-image
container_name: proxy
volumes:
- ./proxy-config:/etc/proxy:ro
restart: unless-stopped
networks: [kiln-net]
kiln:
image: ghcr.io/babywbx/kiln:core
depends_on: [proxy]
ports:
- "8080:8080"
volumes:
- ./kiln.toml:/etc/kiln/kiln.toml:ro
- kiln-data:/var/lib/kiln/data
restart: unless-stopped
networks: [kiln-net]
networks:
kiln-net:
volumes:
kiln-data:线路用服务名加代理在容器内监听的端口,这里不是它映射到宿主机的那个端口:
[[proxies]]
id = "proxy-sidecar"
name = "代理容器"
url = "http://proxy:7890"
[[proxies]]
id = "proxy-sidecar-socks"
name = "代理容器 SOCKS"
url = "socks5h://proxy:7891"同一个网络内的流量不出宿主机,所以代理容器不必对外暴露端口,也不需要 extra_hosts。
代理在局域网另一台机器上
这种情况不需要任何 Docker 侧的特殊配置。容器默认的 bridge 网络本来就能访问宿主机所在的局域网,把线路填成那台机器的地址即可:
[[proxies]]
id = "proxy-lan"
name = "局域网代理"
url = "http://192.168.1.100:7890"既不需要 extra_hosts,也不需要和谁共享网络,写法与 Kiln 直接跑在宿主机上时完全一致。要求同样是那台机器上的代理必须监听 0.0.0.0 或对应网卡,并且防火墙放行该端口。
docker_proxy_host 不参与这件事
docker_proxy_host 只在 ffmpeg.mode = "docker" 时生效,作用是告诉 Kiln 拉起的 ffmpeg 子容器怎么回连 Kiln 进程自己,与线路地址无关。上面两种拓扑都不需要动它。
验证
控制台的「出站代理」页要求线路先通过一次连通性测试才允许应用,这次测试正是从 Kiln 容器内部发起的,通过即说明地址在容器视角可达。测试失败时按这个顺序排查:代理是否允许非本机连接、容器是否解析得到那个主机名或服务名、端口填的是不是容器视角的端口。
受路由控制的流量
| 流量 | 说明 |
|---|---|
| 频道回源 | 播放列表与分片的拉取,原生引擎与 ffmpeg 引擎都会应用路由结果 |
| 节目单抓取 | 仅当该源的出口设为 auto 时按规则路由,设为 direct 或具体线路时按源的设置执行,详见 EPG 节目单 |
| 频道台标 | /v1/logo/{id} 的回源抓取,按频道 ID 参与规则匹配 |
| 管理端探测 | 频道探测、预览与出站连通性测试 |
规则里的 channel_id 匹配只在请求带有频道上下文时才可能命中,节目单抓取没有频道上下文,因此不会被这类规则选中。
控制台与连通性测试
「出站代理」页采用草稿式编辑:新增或修改线路与规则只会改动草稿,必须先运行一次通过的连通性测试,「应用」按钮才会可用;任何后续改动都会重置这个状态,需要重新测试。这样可以避免把一条填错的代理地址直接推送到运行中的服务。
测试对应 POST /v1/admin/egress/test,需要 refresh 权限,可选参数包括:
target:省略时使用内置的公共探测目标;填source或custom时必须给出url,且目标必须是公网地址。channel_id:带上频道后按该频道解析路由,用于验证channel_id类规则。proxy_id或proxy_url:只测某条线路或某个尚未保存的代理地址。draft:直接用草稿配置解析路由,不影响正在运行的配置。
响应会给出实际选中的出口 proxy_id、命中原因 reason、是否会改写播放列表 rewrite、耗时 dur_ms,以及是否可达、HTTP 状态与最终地址。代理要求认证时会单独标记出来。
常见拓扑
默认直连,个别域走代理
default = "direct",为需要代理的域名加一条 host_suffix 规则指向线路。适合大部分源可直连、只有个别源需要绕行的部署。
默认代理,个别域直连
default = "proxy-http",再为内网或本地源加一条 proxy = "direct" 的规则。局域网上游不必绕出去,也避免代理看到内网地址。
Kiln 在容器里,代理在宿主机
线路填 http://host.docker.internal:7890,并给 Kiln 容器加上 host.docker.internal:host-gateway 映射。见 在容器里访问代理。
Kiln 与代理各在一个容器
两者接同一个 Docker 网络,线路填 http://<服务名>:<容器内端口>,代理无需对外映射端口。见 在容器里访问代理。
配置改动的排查思路与日志字段见 故障排查。