`TestHandleConnFallsBackAfterSowerAuthFailure` is the negative arm (a wrong password falls back to the website path), but nothing exercised the positive one end to end, so a green run could not distinguish "the frame was relayed" from "the transport branch never engaged" — only the frame-level Wrap/Unwrap unit tests covered the authenticated path. Add TestHandleConnRelaysAfterSowerAuth: a frame authenticated with the configured password is relayed to the target named inside the frame, asserted against a fallback body that differs from the target's. Verified by mutation: a server/client password mismatch flips the response to the fallback body and the test fails.
sower
Sower 是一个跨平台的智能分流代理工具。
它可以同时提供:
- DNS 分流:客户端先问 Sower 域名怎么解析,Sower 根据规则决定直连还是代理。
- HTTP/HTTPS 透明代理:DNS 模式下,Sower 可以接住被导向自己的
80/443流量,再按规则转发。 - SOCKS5 代理:把 Sower 当成一个普通的
socks5h代理使用,也能享受同一套分流规则。
如果你已经有机场、VPS、Clash、V2Ray、SSR 或其它 SOCKS5 上游,推荐先把 Sower 当成“内网智能分流节点”使用,不一定要部署 sowerd。
安装
一键安装(Linux / macOS / OpenWrt)
curl -fsSL https://raw.githubusercontent.com/sower-proxy/sower/master/install.sh | sudo sh
脚本会识别平台与 init 系统,下载对应版本的 release、安装 sower 二进制、生成初始配置并注册系统服务:
| 平台 | 服务方式 | 服务文件 |
|---|---|---|
| Linux(systemd) | systemd | /etc/systemd/system/sower.service |
| OpenWrt / iStoreOS | procd | /etc/init.d/sower |
| macOS | launchd | /Library/LaunchDaemons/sower.plist |
安装完成后编辑 /etc/sower/sower.toml(macOS 是 /usr/local/etc/sower/sower.toml),填好 remote.addr 与 remote.password,再重启服务。重复执行脚本等于原地升级,已有配置不会被覆盖。
常用参数:
sudo sh install.sh --version v1.6.0 # 安装/回退到指定版本
sudo sh install.sh --mirror https://<你的反代>/ # 给 GitHub 下载地址加前缀
sudo sh install.sh --dry-run # 只打印将要执行的操作
sudo sh install.sh --service none \
--prefix "$HOME/.local/bin" --config-dir "$HOME/.config/sower" # 只装二进制
远程安装默认会校验 release 里的 SHA256SUMS;老版本没有该文件时,需要显式加 --insecure-skip-verify 才会继续。install.sh --help 可以查看全部参数。
手动安装
从 Releases 下载对应平台的 sower-<平台>.tar.gz,解压后得到 sower、sowerd、示例配置和服务文件:
tar -xzf sower-linux-amd64.tar.gz
sudo install -m 0755 sower /usr/local/bin/sower
sudo install -d -m 0755 /etc/sower
sudo install -m 0600 sower.toml /etc/sower/sower.toml
sudo install -m 0644 sower.service /etc/systemd/system/sower.service
sudo systemctl enable --now sower
Windows 目前没有一键脚本:解压后运行 sower.exe -c sower.toml,或参考 sower_run.vbs 自行建立开机启动项。
推荐用法一:Tailscale + Sower 节点
对新手来说,最省事的方式是:选一台常开的设备运行 sower,让它加入 Tailscale,其它手机、电脑、平板也加入同一个 Tailscale 网络。
这个方案的核心思路很简单:
- Tailscale 负责组网、身份认证和加密通道。
- Sower 节点负责 DNS、SOCKS5 和智能分流。
- 普通客户端不用暴露在公网里,只需要连上同一个 Tailscale 网络。
- 国内网站按规则直连,国外网站按规则走上游代理。
适合谁
- 你有多台设备,不想在每台设备上单独维护复杂代理规则。
- 你已经有一个可用的上游代理,只想让 Sower 统一分流。
- 你不想把代理入口直接暴露到公网,只想在自己的 Tailscale 网络里使用。
工作方式
推荐同时使用两种入口:
- DNS 入口:客户端 DNS 指向 Tailscale 的 MagicDNS 地址
100.100.100.100,Tailscale 再把 DNS 请求转给 Sower 节点。 - SOCKS5 入口:需要手动设置代理的软件,直接使用
socks5h://<Sower 节点的 Tailscale IP>:1080。
DNS 模式下,Sower 会这样处理域名:
- 命中直连规则:返回真实 IP,客户端直接访问目标网站。
- 命中代理规则:返回 Sower 节点自己的 IP,客户端访问该 IP 的
80/443,Sower 再根据域名或 TLS SNI 转发到上游代理。 - 命中阻断规则:直接返回 DNS 错误。
SOCKS5 模式下,客户端把请求交给 Sower,Sower 再根据同一套路由规则决定直连或代理。
Sower 节点配置
先复制示例配置:
cp config/sower.toml sower.toml
然后重点修改这几处:
[remote]
type = "socks5"
addr = "127.0.0.1:7890"
password = ""
[dns]
disable = false
serve = "<Sower 节点的 Tailscale IP>"
fallback = "223.5.5.5"
[socks_5]
disable = false
addr = "<Sower 节点的 Tailscale IP>:1080"
说明:
- 如果上游代理就在同一台机器上,例如 Clash 本地 SOCKS5 是
127.0.0.1:7890,就按上面这样写。 - 如果上游代理在另一台机器上,把
remote.addr改成对应的host:port。 - 如果你使用的是
sowerd上游,把remote.type改成sower,并按你的服务端信息填写remote.addr和remote.password。 remote.password与 sowerd 的password按字面值传输,不做任何编解码(旧版本会把恰好是合法 base64 的密码当 base64 解码,导致认证失败;该行为已移除)。不要在配置文件里用编码“隐藏”密码,文件权限才是正确的手段。dns.serve会同时决定 Sower 的 DNS 入口,以及 DNS 模式下 HTTP/HTTPS 透明代理监听的 IP。
启动:
sudo sower -c sower.toml
sower 通常需要 root 权限,因为 DNS 模式会监听 53/udp、80/tcp 和 443/tcp。SOCKS5 入口会监听你在 [socks_5] 中配置的地址。
如果你把监听地址写成 0.0.0.0,必须用系统防火墙限制只允许 Tailscale 网络访问。更简单的做法是直接绑定 Sower 节点的 Tailscale IP。
Tailscale DNS 设置
在 Tailscale 的 DNS 设置里,把 Sower 节点的 Tailscale IP 配成自定义 DNS 服务器,并开启“覆盖本机 DNS”。
客户端侧保持使用 Tailscale 的 MagicDNS 地址:
100.100.100.100
这样客户端发出的 DNS 请求会先进 Tailscale,再被转发到 Sower 节点。
客户端代理设置
如果软件支持 SOCKS5,推荐写成:
socks5h://<Sower 节点的 Tailscale IP>:1080
这里的 socks5h 很重要:它表示域名解析也交给代理端处理。这样 Sower 才能根据域名做分流,客户端也不会把需要代理的域名提前解析掉。
推荐用法二:家庭或办公子网透明分流
如果你的设备都在同一个局域网里,例如家庭网络、小型办公室、实验室网络,也可以把 Sower 放在子网内,然后在路由器上修改 DNS。
这种方式的目标是:手机、电脑、电视等设备不单独配置代理,只要连上这个 Wi-Fi 或有线网络,就自动使用 Sower 做 DNS 分流和 HTTP/HTTPS 透明代理。
网络结构
假设你的局域网是:
- 路由器网关:
192.168.1.1 - Sower 节点:
192.168.1.2 - 普通客户端:
192.168.1.x
工作流程是:
- 路由器通过 DHCP 把 DNS 下发为
192.168.1.2。 - 客户端访问网站前,先向
192.168.1.2查询 DNS。 - Sower 根据规则判断这个域名应该直连、代理还是阻断。
- 直连域名返回真实 IP,客户端直接访问。
- 代理域名返回 Sower 节点 IP,客户端后续访问
192.168.1.2:80/443。 - Sower 接住 HTTP/HTTPS 流量,再转发到上游代理。
Sower 节点配置
复制示例配置:
cp config/sower.toml sower.toml
重点修改:
[remote]
type = "socks5"
addr = "127.0.0.1:7890"
password = ""
[dns]
disable = false
serve = "192.168.1.2"
fallback = "223.5.5.5"
[socks_5]
disable = false
addr = "192.168.1.2:1080"
把 192.168.1.2 换成你的 Sower 节点局域网 IP。
启动:
sudo sower -c sower.toml
Sower 节点需要能监听 53/udp、80/tcp、443/tcp 和 1080/tcp。如果系统防火墙开启了,需要允许局域网设备访问这些端口。
路由器设置
在路由器的 DHCP 设置里,把下发给客户端的 DNS 服务器改成:
192.168.1.2
不同路由器界面名称不一样,常见名称包括:
- DHCP DNS
- LAN DNS
- 自定义 DNS 服务器
- DNS 服务器 1
不要只改路由器自己的“上游 DNS”。要改的是“通过 DHCP 发给局域网客户端的 DNS”。否则客户端可能仍然拿到路由器 IP 作为 DNS,Sower 不会接到查询。
修改后,让客户端重新连接 Wi-Fi,或者更新 DHCP 租约。确认客户端 DNS 已经变成 Sower 节点 IP。
建议的路由器限制
如果你想让全子网都稳定走这套透明分流,路由器上还应该限制几类绕过路径:
- 拦截或阻断客户端直接访问外部
53/udp和53/tcp,只允许访问 Sower 节点的 DNS。 - 阻断常见 DoH/DoT,或者在客户端浏览器里关闭“安全 DNS”。
- 阻断
443/udp,避免 QUIC 绕过 Sower 的 TCP443透明代理;浏览器通常会回退到 TCP HTTPS。
这些限制不是 Sower 自己完成的,需要路由器、防火墙或网关设备支持。
适合和不适合的场景
适合:
- 家庭网络里想让大多数设备自动分流。
- 小型办公网络里想集中维护规则。
- 电视、游戏机、IoT 设备不方便单独配置 SOCKS5。
不适合:
- 客户端强制使用内置 DoH/DoT,且你无法关闭或阻断。
- 应用直接访问固定 IP,不经过 DNS。
- 网络里已经有复杂网关策略,且不能修改 DHCP DNS 或防火墙规则。
这个方案的前提是客户端愿意使用路由器下发的 DNS。只改 DNS 不是万能透明代理;它依赖域名解析把需要代理的 HTTP/HTTPS 流量导向 Sower。
传统用法:部署 sowerd
如果你想自己搭一台 Sower 服务端,可以部署 sowerd。
sowerd 运行在服务端,像一个 Web 代理服务,会监听:
80/tcp443/tcp
它会在启动时校验配置,日志中会脱敏敏感字段。如果 80/443 监听失败,进程会直接退出,不会假装启动成功。
sowerd 可以使用你自己的证书,也可以通过 Let's Encrypt 自动申请证书。
如果 sowerd 作为 systemd 服务运行,且环境里没有 HOME 或 XDG_CACHE_HOME,证书缓存会退回到 /var/cache/sower。
当 fake_site 指向本地目录时,sowerd 只会通过 127.0.0.1:80 的回退流量服务这个目录;公网 HTTP 流量仍会重定向到 HTTPS。未命中 site_routes 的 HTTP/1.x 浏览请求会反代到 fake_site(目录模式为 loopback :80,或配置的 host:port),并覆盖写入 X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Host、X-Real-IP;非 HTTP 流量仍按原字节 TCP relay。
站点路由的访问认证
site_routes 的域名会随 Let's Encrypt 证书进入 Certificate Transparency 日志,任何人都能查到,因此上游站点如果自身没有登录功能,就等于公开。可以给单条路由加一个边缘认证门(HTTP Basic):
[[site_routes]]
domains = ["nas.example.com"]
upstream = "http://127.0.0.1:5000"
[site_routes.auth] # 两个字段都填才生效,留空即不启用
username = "admin"
password = "your_long_random_password"
- 只作用于这条路由的反代流量(路径分流和 WebSocket 升级都包含在内)。
sower客户端的传输流量在 SNI 路由之前就已分流,只用传输密码认证,不受影响。 - 浏览器 WebSocket 是已知的边界情况:门禁会拦到
Upgrade请求,但脚本无法给 WebSocket 握手设置Authorization,浏览器只能靠复用该 origin 已缓存的 Basic 凭证,能否复用因浏览器而异(未验证);门禁按路由生效,无法只放行/ws。每次失败握手还会计入按来源 IP 的锁定计数,所以会自动重连的客户端(socket.io 之类)可能把自己这个 IP 锁在整条路由之外(连普通页面也一起锁)15 分钟。把浏览器的 WS 路由放在门禁后面之前请先自测,或让该客户端走sower传输。 - 认证通过后客户端的
Authorization头会被丢弃,不会转发给上游,也不会写进上游日志。 - 同一来源 IP 累计 5 次失败(中间没有成功请求)会被拒绝 15 分钟(返回 429),所以弱口令不会被直接暴力枚举。但它是边缘门禁,不是上游自身认证的替代品,也不是账号系统(一条路由一组账号,没有会话和登出)。
- 上游需要认证时把凭证写在 upstream 的 userinfo 里(
upstream = "http://user:pass@127.0.0.1:5000"),sowerd会把它作为 Basic 凭证发给上游;错误日志中该密码会被脱敏。
启动方式有两种。
直接运行:
sudo sowerd -password XXX -fake_site 127.0.0.1:8080
交互式安装为 systemd 服务:
sudo sowerd -i
安装器可以复制当前二进制到 /usr/local/bin/sowerd,写入 /etc/systemd/system/sowerd.service,在缺少配置时创建 /etc/sower/sowerd.toml,并按提示决定是否立即启动服务。新建配置会写入一个随机生成的客户端口令并在安装过程中打印一次(不会沿用模板里的 change_me 占位值);已有配置不会被覆盖,需要自己把 password 改成强随机值——它是传输路径上唯一的凭据。
管理控制台(Admin)
sower 内置一个本地管理控制台,用于运行期管理路由规则和监控流量:
-
规则管理:实时查看、添加、删除 block / direct / proxy 三类规则,立即生效;变更以增量形式持久化到
admin.state_file,重启后自动重放(不会改写配置文件)。 -
流量监控:DNS 查询数、各入口连接数、上下行字节数、按域名聚合的流量,以及每条规则的命中统计和未命中规则的域名访问统计。
-
配置页:展示生效配置(并按来源标注为配置文件值或「覆盖」值),可在线调整白名单字段。覆盖以增量持久化到
admin.state_file,重启后自动重放;把某字段清空会恢复配置文件里的值。可编辑字段按生效方式分为三类:
- 立即生效(无需重启):
log_level、dns.upstream、dns.fallback。 - 重启后生效:远程代理(
remote.type/addr、remote.tls.*)、监听地址(dns.serve/serve6、socks5.addr)、admin 自身(admin.session_file、admin.disable_session_persistence、admin.cookie_secure、admin.state_file)、规则来源(各router.*.file/prefix/skip_rules/rules、router.country.*)。 - 只读:版本、构建时间,以及
remote.password、admin.password等密钥字段(只显示「是否已配置」,绝不回显值)。
规则类列表字段(如内联规则、
file_skip_rules)在控制台内按每行一条编辑,展示时折叠为数量。 - 立即生效(无需重启):
-
重启服务:一键触发就地重启——先关闭所有监听(释放 80/443/53 等端口),再
exec替换当前进程,PID 保持不变,因此 systemd 服务的Restart=on-failure不会被误触发。
默认关闭。在 sower.toml 中启用:
[admin]
disable = false # 默认 true
addr = "127.0.0.1:19090" # 默认仅监听回环
password = "your_admin_password" # 示例值;留空则启动时自动生成临时密码
admin.password 接受明文(按字面值)或显式带 b64: 前缀的 canonical base64(如 echo -n "your_password" | base64 后写成 password = "b64:c2VjcmV0...")。明文密码永远不会被解码,可以放心写。
启动后打开 http://127.0.0.1:19090,输入 admin 密码即可。未配置 password 时,登录页会提示使用启动日志中打印的临时密码(每次重启更换;已登录的浏览器通过会话持久化免重新登录)。密码只用于换取会话 cookie,不会保存在浏览器里。
Admin 配置项
| 字段 | 默认值 | 说明 |
|---|---|---|
disable |
true |
默认关闭管理台 |
addr |
127.0.0.1:19090 |
监听地址;设为 dns.serve:80 时与 HTTP 代理共享端口 |
password |
空 | 未配置时启动自动生成一次性随机密码(打印到启动日志);只用于换取会话 cookie |
state_file |
/etc/sower/admin-state.json |
规则增量与配置覆盖的持久化文件;空字符串禁用持久化 |
session_file |
/etc/sower/sessions.json |
会话持久化文件,重启后浏览器免重新登录;仅限单进程使用 |
disable_session_persistence |
false |
设为 true 关闭会话持久化(重启后需重新登录) |
cookie_secure |
false |
仅当管理台位于 TLS 终止代理(如 nginx/caddy)之后时设为 true,给会话 cookie 加 Secure 标记 |
与 HTTP 代理共享端口
如果希望管理台与 HTTP 代理复用同一个 IP:80,把 admin.addr 设为 dns.serve:80:
[dns]
disable = false
serve = "192.168.1.10"
[admin]
disable = false
addr = "192.168.1.10:80" # 与 dns.serve:80 相同,进入共享模式
password = "your_admin_password"
共享模式下,同一个监听器按请求头区分流量:
Host等于监听 IP 的普通请求(如浏览器访问http://192.168.1.10/)→ 管理台CONNECT、absolute-form 请求、以及其他Host的请求 → HTTP 代理
注意:
- 只支持共享
80端口;443(HTTPS 透明代理)和53(DNS)无法共享,配置冲突会启动失败。 - 代理一个
Host恰好等于监听 IP 的目标时无法区分,会进入管理台。 - 管理台仍只提供明文 HTTP,共享
80时请勿在公网直接暴露。 - 独立监听模式(默认
127.0.0.1:19090)不受影响。
规则和配置补充
sower.toml 是默认示例配置。完整示例见 config/sower.toml。
配置格式仅支持 TOML(sower.toml / sowerd.toml)。旧版本支持的同名 YAML 配置文件已不再被读取,升级前请将 YAML 配置迁移为 TOML。
几个容易踩坑的点:
- 远程规则文件会通过上游代理下载,不会直接出网。
- 远程规则加载失败时,
sower会启动失败,不会带着不完整规则继续运行。 router.country.mmdb是可选项,留空表示关闭 GeoIP,只使用配置里的 CIDR 规则。- HTTPS 透明代理只读取 TLS ClientHello 里的 SNI,不会在本机解密或终止 TLS。
- HTTP/HTTPS 可达性探测结果会缓存 1 小时,减少重复探测,同时避免长期固定错误状态。
sower上游的remote.addr可以写host,也可以写host:port。remote.tls可以设置 SNI、跳过证书校验,或使用chrome、firefox等 uTLS 指纹。- 规则文件可以用
file_skip_rules跳过第三方列表中的个别条目。例如在[router.block]中写file_skip_rules = ["t.co"]。
架构
更详细的架构、边界和数据流说明见 ARCHITECTURE.md。
