Skip to content
Advanced / 4 min read

如何在 Linux 上使用 Privoxy 和 HAProxy 构建共享的 ProxyScrape 代理网关

将一个 ProxyScrape SOCKS5 端点转变为一个可供整个团队使用的 HTTP 代理地址,支持跨多个上游的轮询轮换、健康检查和私有网络访问控制。

Linux SOCKS5 HTTP(S) 其他

概述

每台机器单独配置代理的方式无法扩展。当多个人、几个 CI 运行器和一些容器都需要同一个代理池时,凭据最终会被复制到每个启动器和脚本中,并且没有人能分辨出哪些流量使用了哪个上游。

本教程构建的是相反的方式:一台 Linux 主机,它只保存一次你的 ProxyScrape 凭据,并向你的私有网络暴露一个单一的 HTTP 代理地址。

客户端应用 -> HAProxy (TCP :3128) -> Privoxy 实例 (127.0.0.1:8118-8120) -> ProxyScrape SOCKS5 -> 互联网

本指南使用 ProxyScrape SOCKS5 上游,因为本地前端必须向上游传递用户名和密码,而 Privoxy 支持 SOCKS5 上游的凭据,但不支持 HTTP 上游。如果你特别需要通过经过身份验证的 HTTP 上游进行链式连接,请改用支持 HTTP 父级认证的链式工具。

层级 软件 作用
客户端边缘 HAProxy (TCP 模式) 为客户端提供一个主机和端口,将新连接分散到本地代理实例,移除停止响应的实例
本地代理 Privoxy 接受 HTTP 代理请求,并将每个请求转发到单个 ProxyScrape SOCKS5 端点
上游 ProxyScrape SOCKS5 使用代理池中的代理 IP 解析并获取目标 URL

这种设计的一个实际后果:轮换是基于每个新连接发生的,而不是基于每个请求。重用 keep-alive 连接的客户端会一直停留在同一个上游实例上,直到重新连接。

先决条件

  • 一台 Linux 主机(Debian/Ubuntu 或 RHEL/Fedora 系列),在私有网络、VPN 或 tailnet 上具有稳定地址。不要将网关端口暴露到公共互联网。
  • 来自仪表板的 ProxyScrape SOCKS5 端点详细信息:主机、端口、用户名和密码。
  • sudo 访问权限,以及在私有网络上打开一个端口(下面示例中的 3128)的权限。
  • 基本熟悉 systemd 单元和编辑配置文件。

步骤

步骤 1:在构建任何东西之前验证 ProxyScrape SOCKS5 端点

将你的凭据放入 shell 变量中,以便后续测试保持一致:

export PS_HOST="socks5-host-from-your-dashboard"
export PS_PORT="1080"
export PS_USER="your-username"
export PS_PASS="your-password"

直接通过端点发起一个请求:

curl --silent --show-error \
  --proxy "socks5h://${PS_USER}:${PS_PASS}@${PS_HOST}:${PS_PORT}" \
  https://api.ipify.org
printf '\n'

socks5h:// 方案使 curl 通过代理发送 DNS 查询,这正是网关所需要的。运行该命令两到三次,并记下返回的地址。如果第一个命令失败,请在继续之前修复凭据或主机和端口,因为此后的每一层都依赖于它。

步骤 2:安装 Privoxy 和 HAProxy

Debian 和 Ubuntu:

sudo apt update
sudo apt install --yes privoxy haproxy

RHEL、Fedora 和兼容系统:

sudo dnf install --yes privoxy haproxy

确认二进制文件存在并记录其路径,因为下面的 systemd 单元直接引用了 Privoxy 二进制文件:

command -v privoxy
command -v haproxy

步骤 3:为每个上游创建一个 Privoxy 实例

Privoxy 读取单个平面配置文件,这使得运行多个独立实例(每个上游端点一个)变得容易。首先为它们创建一个目录:

sudo mkdir -p /etc/privoxy/instances

创建 /etc/privoxy/instances/instance-1.conf:

confdir /etc/privoxy
logdir /var/log/privoxy
logfile instance-1.log

toggle 0
listen-address 127.0.0.1:8118

forward-socks5t / USERNAME:PASSWORD@HOST:PORT .

将 USERNAME、PASSWORD、HOST 和 PORT 替换为你的 ProxyScrape SOCKS5 值。

  • toggle 0 禁用 Privoxy 内容过滤。网关应原样转发流量,而不是重写或阻止它。
  • listen-address 127.0.0.1:8118 使实例仅对主机私有。HAProxy 将是唯一暴露的表面。
  • forward-socks5t 将请求发送到 SOCKS5 上游,并远程解析 DNS,因此主机名不会从你自己的网络解析。
  • 前导 / 匹配每个 URL,尾随 . 是 Privoxy 文档中用于 Tor 和类似设置的 SOCKS 转发规则形式的一部分。

锁定文件,因为它包含凭据:

sudo chown root:privoxy /etc/privoxy/instances/instance-1.conf
sudo chmod 640 /etc/privoxy/instances/instance-1.conf

如果发行版软件包已经在端口 8118 上启动了 Privoxy 服务,请先停止它,以便释放端口:

sudo systemctl disable --now privoxy

在前台测试实例:

sudo privoxy --no-daemon /etc/privoxy/instances/instance-1.conf

在第二个终端中,通过它发送请求:

curl --silent --proxy http://127.0.0.1:8118 https://api.ipify.org
printf '\n'

如果返回一个地址,最难的部分就完成了。用 Ctrl+C 停止前台进程,并为你想要使用的每个额外的 ProxyScrape 端点重复配置文件,只更改日志文件名和监听端口。

步骤 4:将实例作为 systemd 模板服务运行

创建 /etc/systemd/system/[email protected]:

[Unit]
Description=Privoxy proxy gateway instance %i
Documentation=man:privoxy(8)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=privoxy
Group=privoxy
ExecStart=/usr/sbin/privoxy --no-daemon /etc/privoxy/instances/instance-%i.conf
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

如果 command -v privoxy 打印了不同的路径,请调整 ExecStart 以匹配。然后启用实例:

sudo systemctl daemon-reload
sudo systemctl enable --now privoxy-gateway@1 privoxy-gateway@2 privoxy-gateway@3
systemctl list-units 'privoxy-gateway@*' --no-pager

确认每个实例都在自己的回环端口上监听:

for port in 8118 8119 8120; do
  printf '%s -> ' "$port"
  curl --silent --proxy "http://127.0.0.1:${port}" https://api.ipify.org
  printf '\n'
done

每一行都应返回一个地址,而且它们不一定是相同的地址,因为每个实例都与自己的上游通信。

步骤 5:将 HAProxy 置于前端作为 TCP 负载均衡器

前端不需要理解 HTTP。它只需要中继 TCP 连接,这使得 HAProxy 的 TCP 模式非常适合位于 SOCKS5 上游前面的 HTTP 代理。

备份打包的配置,并将 /etc/haproxy/haproxy.cfg 替换为以下内容。将 10.8.0.1 更改为此主机的私有地址。

global
    log /dev/log local0
    maxconn 4000
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s

defaults
    mode tcp
    log global
    option tcplog
    retries 2
    timeout connect 10s
    timeout client 5m
    timeout server 5m

frontend proxy_gateway
    bind 10.8.0.1:3128
    default_backend privoxy_instances

frontend proxy_gateway_sticky
    bind 10.8.0.1:3129
    default_backend privoxy_instance_1

backend privoxy_instances
    balance roundrobin
    server privoxy1 127.0.0.1:8118 check
    server privoxy2 127.0.0.1:8119 check
    server privoxy3 127.0.0.1:8120 check

backend privoxy_instance_1
    server privoxy1 127.0.0.1:8118 check

listen stats
    bind 127.0.0.1:8404
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s

关于这里的选择的几点说明:

  • 端口 3128 是轮换入口点。每个新的 TCP 连接都被发送到下一个健康的 Privoxy 实例。
  • 端口 3129 固定到单个实例,适用于你希望每次运行期间每个请求都通过同一上游离开的任务。
  • 普通的 check 关键字执行 TCP 连接检查,因此停止接受连接的实例会自动从轮换中移除。
  • 统计页面仅绑定到回环。通过 SSH 隧道或内部跳板机访问它,而不是将端口 8404 开放给网络。

验证并重新加载:

sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl restart haproxy
systemctl --no-pager --lines=20 status haproxy

步骤 6:限制对私有网络的访问

将 HAProxy 绑定到私有、VPN 或 tailnet 地址意味着网关只能从该网络内部访问。如果主机还有一个公共接口,请添加防火墙规则,以便该接口上的任何内容都无法访问代理端口。

以下 nftables 示例是可选的,仅用于说明。从第二个终端会话应用它,以免错误将你锁在机器外,并根据你自己的网络调整源范围:

sudo nft add table inet proxygw
sudo nft add chain inet proxygw input '{ type filter hook input priority 0; }'
sudo nft add rule inet proxygw input tcp dport { 3128, 3129 } ip saddr != 10.8.0.0/24 drop

此堆栈不验证客户端;访问控制来自网关绑定到的网络。如果你需要个人凭据,请在主机前的 VPN 或 tailnet 上终止用户访问。

步骤 7:端到端验证轮换

向轮换端口发送一系列单独的请求:

for i in $(seq 1 6); do
  curl --silent --proxy http://10.8.0.1:3128 https://api.ipify.org
  printf '\n'
done

每次 curl 调用都会打开一个新连接,因此你应该看到地址的分布,而不是单个重复的地址。然后打开统计页面(如果需要,通过隧道),并确认每个后端都标记为 UP:

ssh -L 8404:127.0.0.1:8404 user@gateway-host
# in the browser on your workstation: http://127.0.0.1:8404/stats

最后,将真实客户端指向网关,例如使用代理进行命令行工具的 shell 会话:

export http_proxy="http://10.8.0.1:3128"
export https_proxy="http://10.8.0.1:3128"
curl --silent https://api.ipify.org
printf '\n'
unset http_proxy https_proxy

轮换与粘性行为

选择与任务匹配的前端端口。两个选项都已在上方配置。

  • 轮换(3128):新连接分布到所有健康的实例。对于浏览器、一般浏览以及频繁打开新连接的任务来说,是一个好的默认选择。
  • 粘性(3129):所有流量都转到一个实例。适用于必须在会话期间呈现一致的源地址的工作流。
  • 按组隔离:创建一个包含实例子集的第二个后端,并将新的前端端口指向它,这样一个团队永远不会消耗整个池。

故障排除

症状 可能原因 修复
Privoxy 返回 403 响应 内容过滤已启用,并且过滤规则匹配了请求 确认实例配置中存在 toggle 0,并重启该实例
Privoxy 返回 502 或上游连接错误 SOCKS5 主机、端口或凭据错误 重新运行步骤 1 中的直接 curl --proxy "socks5h://..." 测试
所有后端在统计页面中显示为 DOWN Privoxy 实例未运行或未在 HAProxy 期望的位置监听 运行 systemctl list-units 'privoxy-gateway@*' 和 `ss -ltnp
每个请求都从同一地址退出 客户端正在重用 keep-alive 连接,或者只有一个实例在运行 强制每个请求使用新连接,并确认所有后端报告 UP
主机名从你自己的网络解析 实例使用 forward-socks5 而不是 forward-socks5t 切换到 forward-socks5t,以便通过代理解析 DNS
HAProxy 启动失败,出现 /dev/log 错误 主机上没有 syslog 套接字 删除 log /dev/log local0 行,或安装并启用 syslog 服务
大文件下载在传输中途超时 默认 HAProxy 超时对于工作负载来说太低 在 defaults 部分增加 timeout client 和 timeout server

总结

你现在拥有一个单一的 HTTP 代理地址,它前端有多个 ProxyScrape SOCKS5 端点,HAProxy 侧具有自动健康检查,并且端口 3128 上具有按连接轮换。凭据存放在一组 root 拥有的配置文件中,而不是复制到每台机器上的每个工具中。

自然的下一步:每当你想增加上游容量时,添加一个实例和一行 server,将 Privoxy 配置文件排除在版本控制之外,并通过编辑实例配置并一次一个地重启模板单元来轮换凭据,以便网关永远不会完全离线。