用 Docker + redsocks 实现「一容器一出口 IP」:透明 SOCKS5 代理网关

2025 年 10 月 8 日 星期三
19

用 Docker + redsocks 实现「一容器一出口 IP」:透明 SOCKS5 代理网关

很多时候,给容器设置 HTTP_PROXY/ALL_PROXY 并不可靠:有的程序不读环境变量,有的仅支持 HTTP 代理不支持 SOCKS5,还有的会把一部分流量绕过代理。更稳的做法是系统级透明代理——把容器里的所有 TCP 流量统一“劫持”并转发到指定的 SOCKS5,由此实现一个容器一个出口 IP

思路与架构

我们做一个单独的“网关容器”,在这个容器里运行:

  • redsocks:把透明转发的 TCP 连接封装成 SOCKS5 请求;
  • iptables (NAT/OUTPUT):在该网络命名空间内重定向所有 TCP 出站流量到 redsocks 监听口。

随后,让业务容器通过 --network container:<网关容器名> 复用网关容器的网络栈。这样业务容器无感知地全量走代理。

业务容器 → (共享网络栈) → iptables(OUTPUT) → redsocks:12345 → SOCKS5 → 目标网站

优点:

  • 应用容器无需更改镜像或配置;
  • 真正“全局”代理(TCP 层),不依赖应用是否支持代理环境变量;
  • 多个网关容器 = 多个出口 IP;业务容器按需绑定对应网关。

环境准备

  • 宿主机:Debian/Ubuntu(其他 Linux 发行版亦可)
  • 已安装 Docker,能拉取镜像、启动容器
  • 具备一个或多个可用 SOCKS5 代理(可匿名或带用户名/密码)

实操步骤(逐步 + 每步目的)

以网关容器名 proxy1、示例 SOCKS5 60.188.121.252:20002 为例。

1. 创建“网关容器”

docker run -d --name proxy1 --cap-add=NET_ADMIN --restart unless-stopped debian:bookworm-slim sleep infinity

目的

  • --cap-add=NET_ADMIN 让容器内能设置 iptables;
  • 独立网络命名空间,用作“透明代理网关”;
  • sleep infinity 占位常驻;重启策略保证容器随宿主开机恢复。

2. 安装工具(redsocks / iptables / curl 等)

docker exec -it proxy1 bash -lc 'apt update && apt install -y redsocks iptables curl procps net-tools ca-certificates'

目的

  • redsocks 做透明转发至 SOCKS5;
  • iptables 写 NAT/OUTPUT 规则;
  • curl 用于探测代理可用与验证出口 IP;
  • procps/net-tools 用于排错(ps/netstat);
  • ca-certificates 避免 HTTPS 验证问题。

3. 先直接用 curl 测 SOCKS5 是否可用(绕过 redsocks)

docker exec -it proxy1 bash -lc 'curl -v --socks5 60.188.121.252:20002 https://ipinfo.io/ip'

目的

  • 在启用透明代理前,确认 SOCKS5 本身能正常出外网;
  • 如果这里就超时/失败,后续即使配置再完美也没用。

4. 写入 redsocks.conf

docker exec -i proxy1 bash <<'EOF'
cat > /etc/redsocks.conf <<'EOC'
base {
  log_debug = off;
  log_info  = on;
  log       = "stderr";
  daemon    = on;
  redirector = iptables;
}
redsocks {
  local_ip   = 127.0.0.1;
  local_port = 12345;
  ip   = 60.188.121.252;   # SOCKS5 服务器 IP
  port = 20002;            # SOCKS5 端口
  type = socks5;
  # 若需认证,取消注释:
  # login = "username";
  # password = "password";
}
EOC
EOF

目的与要点

  • 配置 redsocks 监听 127.0.0.1:12345
  • 指向你的 SOCKS5;
  • 配置文件必须是纯 ASCII,避免中文/奇怪字符导致解析失败。

5. 启动 redsocks

docker exec -d proxy1 bash -lc 'redsocks -c /etc/redsocks.conf'

目的

  • 让 redsocks 常驻监听;如需诊断可用 -t 前台模式查看日志。

6. 下发 iptables 规则(透明转发)

docker exec -it proxy1 bash -lc '
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 -j RETURN
iptables -t nat -A OUTPUT -p tcp -d 60.188.121.252 -j RETURN
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 12345
'

目的与要点

  • 清空旧规则,避免重复叠加;
  • 放行本机回环与代理服务器本身(否则会套娃死循环);
  • 其余 TCP 全部重定向到 12345 → 由 redsocks 送往 SOCKS5。

说明:这是在 proxy1 的网络命名空间 内生效,不影响宿主机和其他容器。


7. 验证出口 IP(确认透明代理生效)

docker exec -it proxy1 bash -lc 'curl -s https://ipinfo.io/ip'

期望:输出为 SOCKS5 的公网 IP(例如 114.86.2.30)。 意义:证明透明代理链路 容器 → iptables → redsocks → SOCKS5 正常。


8. 让业务容器复用网关网络(“一容器一出口 IP”)

docker run -d \
  --restart unless-stopped \
  --network container:proxy1 \
  packetshare/packetshare \
  -accept-tos \
  -email=<你的邮箱> \
  -password=<你的密码>

目的

  • 业务容器与 proxy1 共用网络栈,因此自动走上一步配置好的透明代理;
  • 适用于任何镜像,不需要在应用层配置代理变量。

切换/替换 SOCKS5 的正确姿势

当旧代理失效,需要切换新代理时:

# 1) 恢复直连(避免“断网时无法 apt/curl”)
docker exec -it proxy1 bash -lc 'iptables -t nat -F'

# 2) 重启或重启 redsocks(如需)
docker restart proxy1   # 简洁做法,顺带清理僵尸进程

# 3) 先直接用 curl 测新 SOCKS5 是否通
docker exec -it proxy1 bash -lc 'curl -v --socks5 <NEW_IP:PORT> https://ipinfo.io/ip'

# 4) 改 /etc/redsocks.conf,启动 redsocks
# 5) 重新下发三条 iptables 规则(放行 127.0.0.1 / 新代理 IP / 其余全转发)

常见问题 & 排错建议

  • redsocks 报“unexpected char / unclosed section” → 配置文件含中文或 ${VAR} 这类 shell 变量占位符(redsocks 不会展开),改为硬编码纯 ASCII

  • curl -s https://ipinfo.io/ip 无输出 → 常见于 SOCKS5 仅“能连上端口”但不能完整转发 HTTPS(公开代理很多这种)。先用 curl -v --socks5 <IP:PORT> https://ipinfo.io/ip 直连测试;查代理质量。

  • [redsocks] <defunct> 僵尸进程 → 代理或配置异常导致短启即死,重启容器最干净:docker restart proxy1。 重新按「先清规则 → 测代理 → 写配置 → 启动 → 下规则」顺序来。

  • DNS/UDP 泄漏 → 透明代理本方案主要拦截 TCP。对 DNS 建议:应用侧启用 DoH/DoT 或使用 redudp/dns2socks 进一步处理。 (如果你的业务全是 HTTPS/TLS,通常已足够。)

  • IPv6 → 如需代理 IPv6,另配一组 ip6tables -t nat ... 规则(思路同 IPv4)。


一键脚本(先测 SOCKS5,再自动建“网关容器”)

把下面内容保存为 create_proxy_container.sh,赋予执行权限 chmod +x create_proxy_container.sh。 用法:./create_proxy_container.sh <容器名> <SOCKS_IP> <SOCKS_PORT>

#!/bin/bash
# ===============================
# 一键创建 SOCKS5 透明代理“网关容器”
# 用法示例:
#   ./create_proxy_container.sh proxy1 60.188.121.252 20002
# ===============================

set -e

NAME=$1
SOCKS_IP=$2
SOCKS_PORT=$3
TARGET_URL="https://ipinfo.io/ip"

if [[ -z "$NAME" || -z "$SOCKS_IP" || -z "$SOCKS_PORT" ]]; then
  echo "用法: $0 <容器名> <SOCKS_IP> <SOCKS_PORT>"
  exit 1
fi

# 1) 准备容器
if docker ps -a --format '{{.Names}}' | grep -q "^${NAME}$"; then
  echo "容器 ${NAME} 已存在,直接使用。"
else
  echo "创建容器 ${NAME}..."
  docker run -d --name "$NAME" --cap-add=NET_ADMIN --restart unless-stopped debian:bookworm-slim sleep infinity
fi

# 2) 安装依赖
echo "安装 redsocks 和依赖..."
docker exec "$NAME" bash -lc 'apt update -qq && apt install -y -qq redsocks iptables curl procps net-tools ca-certificates >/dev/null'

# 3) 预检 SOCKS5 可用性(绕过透明代理)
echo "测试 SOCKS5 代理是否可用 (${SOCKS_IP}:${SOCKS_PORT}) ..."
if docker exec "$NAME" bash -lc "curl -s --max-time 6 --socks5 ${SOCKS_IP}:${SOCKS_PORT} ${TARGET_URL}" >/dev/null 2>&1; then
  echo "✅ SOCKS5 测试通过"
else
  echo "❌ SOCKS5 无法连接或超时,请检查地址/端口/可用性"
  exit 1
fi

# 4) 写入 redsocks 配置
echo "写入 /etc/redsocks.conf..."
docker exec -i "$NAME" bash <<EOF
cat > /etc/redsocks.conf <<EOC
base {
  log_debug = off;
  log_info  = on;
  log       = "stderr";
  daemon    = on;
  redirector = iptables;
}
redsocks {
  local_ip   = 127.0.0.1;
  local_port = 12345;
  ip   = ${SOCKS_IP};
  port = ${SOCKS_PORT};
  type = socks5;
}
EOC
EOF

# 5) 启动 redsocks
echo "启动 redsocks..."
docker exec -d "$NAME" bash -lc 'redsocks -c /etc/redsocks.conf'

# 6) 配置 iptables(先清空再下发)
echo "配置 iptables 规则..."
docker exec "$NAME" bash -lc "
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 -j RETURN
iptables -t nat -A OUTPUT -p tcp -d ${SOCKS_IP} -j RETURN
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 12345
"

# 7) 验证出口 IP(透明代理是否生效)
echo "验证透明代理出口 IP..."
IP_OUT=$(docker exec "$NAME" bash -lc "curl -s --max-time 8 ${TARGET_URL}" || true)
if [[ -n "$IP_OUT" ]]; then
  echo "✅ 代理工作正常,出口 IP: $IP_OUT"
  echo "👉 可通过 '--network container:${NAME}' 让其他容器共用此代理。"
  exit 0
else
  echo "⚠️ 未能获取出口 IP,请手动检查 redsocks/iptables/代理可用性。"
  exit 2
fi

使用例子:

./create_proxy_container.sh proxy1 60.188.121.252 20002

# 让业务容器共享代理网络
docker run -d --network container:proxy1 packetshare/packetshare \
  -accept-tos -email=<你的邮箱> -password=<你的密码>

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...