hev-socks5-tunnel 透明代理网关部署指南

项目概述

hev-socks5-tunnel 是一个基于 TUN 设备的 SOCKS5 隧道工具,可以将系统级流量透明转发到 SOCKS5 代理服务器。本指南介绍如何将其部署为局域网透明代理网关。

系统架构

  • 网关机器:运行 hev-socks5-tunnel,作为局域网的透明代理网关
  • 局域网客户端:将网关设为默认路由,流量自动经过代理
  • 上游 SOCKS5:实际的代理服务器

核心组件

  • TUN 虚拟网卡:捕获需要代理的流量
  • 策略路由:将局域网流量导向 TUN 设备
  • iptables 规则:管理流量转发
  • fwmark 标记:避免代理流量循环

1. 构建二进制文件

使用 Docker 构建

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# ---------- Build stage ----------
FROM debian:bookworm-slim AS builder

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
git ca-certificates build-essential pkg-config \
&& rm -rf /var/lib/apt/lists/*

# 可通过 --build-arg HEV_REF=v2.13.0 指定版本/标签/提交
ARG HEV_REF=master
WORKDIR /src

RUN git clone --recursive https://github.com/heiher/hev-socks5-tunnel . \
&& git checkout "${HEV_REF}" \
&& git submodule update --init --recursive \
&& make -j"$(nproc)"

# ---------- Runtime stage ----------
FROM debian:bookworm-slim

# 运行时常见依赖(可按需删除)
RUN apt-get update && apt-get install -y --no-install-recommends \
iproute2 libcap2-bin ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# 拷贝编译产物
COPY --from=builder /src/bin/hev-socks5-tunnel /usr/local/bin/hev-socks5-tunnel

# 可选:暴露配置/数据目录(如需)
# VOLUME ["/etc/hev-socks5-tunnel"]

# 不设置 ENTRYPOINT,便于按需传参;需要时可启用:
# ENTRYPOINT ["/usr/local/bin/hev-socks5-tunnel"]

构建并提取二进制文件

1
2
3
4
docker run -d --name my_tun2socks_container tun2socks:latest sleep infinity
docker cp my_tun2socks_container:/usr/local/bin/hev-socks5-tunnel ./
docker stop my_tun2socks_container
docker rm my_tun2socks_container

2. 配置文件

/etc/hev-socks5-tunnel/config.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
tunnel:
# 虚拟 TUN 接口
name: tun0
mtu: 8500
multi-queue: true
# TUN 口地址(内网保留网段,用于与宿主区分)
ipv4: 198.18.0.1
ipv6: "fc00::1"
# 可选:启动/停止时的钩子脚本(如自定义路由)
post-up-script: /etc/hev-socks5-tunnel/up.sh
pre-down-script: /etc/hev-socks5-tunnel/down.sh

socks5:
# 上游 SOCKS5 服务器
address: 127.0.0.1
port: 1080
# UDP 中继模式:udp(原生)或 tcp(UDP-over-TCP 兼容模式)
udp: "udp"
# 可选:握手管线化(减少 RTT)
pipeline: true
# 可选:认证
# username: "user"
# password: "pass"
# 将连接打上此 fwmark,便于在系统路由中"旁路上游"避免环路
mark: 438

# 可选:DNS 映射(将域名解析映射到专用网段,降低泄露风险)
#mapdns:
# address: 198.18.0.2 # 本地映射 DNS 监听地址
# port: 53
# network: 240.0.0.0 # 专用映射网段(仅供本机内部映射)
# netmask: 240.0.0.0
# cache-size: 10000

# 可选:其他运行参数与日志
# misc:
# task-stack-size: 86016 # 任务栈大小(字节)
# tcp-buffer-size: 65536 # TCP 缓冲(字节)
# connect-timeout: 5000 # 连接超时(毫秒)
# read-write-timeout: 60000 # 读写超时(毫秒)
# log-file: stderr # stdout / stderr / 文件路径
# log-level: warn # debug / info / warn / error
# pid-file: /run/hev-socks5-tunnel.pid
# limit-nofile: 65535 # (可选)提升文件句柄上限
misc:
task-stack-size: 86016 # = 20480 + 65536
tcp-buffer-size: 65536
connect-timeout: 5000 # ms
read-write-timeout: 180000
log-file: stderr
log-level: warn
# pid-file: /run/hev-socks5-tunnel.pid
limit-nofile: 65535

配置说明

  • tunnel.ipv4/ipv6:TUN 设备的虚拟 IP,选择不与现有网络冲突的地址段
  • socks5.address/port:上游 SOCKS5 服务器地址,通常是本机运行的代理客户端
  • socks5.mark:重要!用于标记代理流量,防止路由循环
  • misc.limit-nofile:提高文件句柄限制,支持更多并发连接

3. 网络配置脚本

/etc/hev-socks5-tunnel/up.sh(启动时配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/bash
# up.sh — 配置 hev-socks5-tunnel 网关(仅 IPv4,幂等合并版)
set -e

# ===== 基本变量(可通过环境变量覆盖)=====
SELF_IP="${SELF_IP:-192.168.2.42}"
LAN_NET="${LAN_NET:-192.168.2.0/24}"
TUN_IF="${TUN_IF:-tun0}"
TBL_NAME="${TBL_NAME:-tun_socks}" # /etc/iproute2/rt_tables 中映射到 100
TBL_NUM="${TBL_NUM:-100}"
MARK="${MARK:-438}"

# 自动识别 LAN 接口(可预先导出 LAN_IF 覆盖)
if [ -z "${LAN_IF:-}" ]; then
LAN_IF="$(ip -o -4 addr show | awk -v ip="${SELF_IP}" '$4 ~ ip"/" {print $2; exit}')"
fi
: "${LAN_IF:?无法识别 LAN 接口,请手动设置 SELF_IP 或 LAN_IF}"
echo "LAN_IF=${LAN_IF} TUN_IF=${TUN_IF} TBL=${TBL_NAME}(${TBL_NUM}) MARK=${MARK}"

# ===== 内核参数 =====
sysctl -w net.ipv4.ip_forward=1 \
net.ipv4.conf.all.rp_filter=0 \
net.ipv4.conf.default.rp_filter=0 \
net.ipv4.conf.all.accept_redirects=0 \
net.ipv4.conf.default.accept_redirects=0 \
net.ipv4.conf.all.send_redirects=0 \
net.ipv4.conf.default.send_redirects=0 >/dev/null

# ===== 路由表映射 =====
grep -qE "^[[:space:]]*${TBL_NUM}[[:space:]]+${TBL_NAME}\$" /etc/iproute2/rt_tables || \
echo "${TBL_NUM} ${TBL_NAME}" >> /etc/iproute2/rt_tables

# ===== 路由与策略规则 =====
# 100 表(tun_socks)→ tun0
ip route replace default dev "$TUN_IF" table "$TBL_NAME"

# main 表不得出现 “default dev tun0”
ip route del default dev "$TUN_IF" 2>/dev/null || true

# 旁路标记:hev-socks5-tunnel 自身连接 mark=438 走 main
ip rule add fwmark "$MARK" lookup main priority 50 2>/dev/null || true

# 旁路 LAN/本机/容器网段(避免被表100吸走)
ip rule add iif "$LAN_IF" to "${SELF_IP}/32" lookup main priority 900 2>/dev/null || true
ip rule add iif "$LAN_IF" to "$LAN_NET" lookup main priority 900 2>/dev/null || true
ip rule add iif "$LAN_IF" to 172.17.0.0/16 lookup main priority 900 2>/dev/null || true
ip rule add iif "$LAN_IF" to 172.18.0.0/16 lookup main priority 900 2>/dev/null || true

# LAN 其余流量 → 表100(经 tun0)
ip rule add iif "$LAN_IF" lookup "$TBL_NAME" priority 1000 2>/dev/null || true

# ===== 防火墙(转发放行)=====
iptables -C FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || \
iptables -I FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -C FORWARD -i "$LAN_IF" -o "$TUN_IF" -j ACCEPT 2>/dev/null || \
iptables -I FORWARD -i "$LAN_IF" -o "$TUN_IF" -j ACCEPT
iptables -C FORWARD -i "$TUN_IF" -o "$LAN_IF" -j ACCEPT 2>/dev/null || \
iptables -I FORWARD -i "$TUN_IF" -o "$LAN_IF" -j ACCEPT

echo "配置完成。"

/etc/hev-socks5-tunnel/down.sh(停止时清理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
# down.sh — 清理 hev-socks5-tunnel 网关配置(仅 IPv4,等幂)
set -e

# ===== 变量(可通过环境变量覆盖)=====
SELF_IP="${SELF_IP:-192.168.2.42}"
LAN_NET="${LAN_NET:-192.168.2.0/24}"
TUN_IF="${TUN_IF:-tun0}"
TBL_NAME="${TBL_NAME:-tun_socks}" # 映射到 100
TBL_NUM="${TBL_NUM:-100}"
MARK="${MARK:-438}"

# 自动识别 LAN 接口(亦可预先导出 LAN_IF 覆盖)
if [ -z "${LAN_IF:-}" ]; then
LAN_IF="$(ip -o -4 addr show | awk -v ip="${SELF_IP}" '$4 ~ ip"/" {print $2; exit}')"
fi
: "${LAN_IF:?无法识别 LAN 接口,请手动设置 SELF_IP 或 LAN_IF}"
echo "清理: LAN_IF=${LAN_IF} TUN_IF=${TUN_IF} TBL=${TBL_NAME}(${TBL_NUM}) MARK=${MARK}"

# ===== 删除策略路由 =====
# 旁路标记
ip rule del fwmark "$MARK" lookup main priority 50 2>/dev/null || true

# 旁路本机/LAN/容器网段
ip rule del iif "$LAN_IF" to "${SELF_IP}/32" lookup main priority 900 2>/dev/null || true
ip rule del iif "$LAN_IF" to "$LAN_NET" lookup main priority 900 2>/dev/null || true
ip rule del iif "$LAN_IF" to 172.17.0.0/16 lookup main priority 900 2>/dev/null || true
ip rule del iif "$LAN_IF" to 172.18.0.0/16 lookup main priority 900 2>/dev/null || true

# LAN → 代理表
ip rule del iif "$LAN_IF" lookup "$TBL_NAME" priority 1000 2>/dev/null || true
ip rule del iif "$LAN_IF" lookup "$TBL_NUM" priority 1000 2>/dev/null || true

# ===== 清空代理表路由并移除误设默认路由 =====
ip route flush table "$TBL_NAME" 2>/dev/null || true
ip route flush table "$TBL_NUM" 2>/dev/null || true
ip route del default dev "$TUN_IF" 2>/dev/null || true # 防止 main 表存在 default dev tun0

# ===== 删除 iptables 规则(与 up.sh 对应)=====
iptables -D FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$LAN_IF" -o "$TUN_IF" -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$TUN_IF" -o "$LAN_IF" -j ACCEPT 2>/dev/null || true

# ===== 可选:恢复内核参数(按需启用)=====
sysctl -w net.ipv4.ip_forward=0 \
net.ipv4.conf.all.rp_filter=1 \
net.ipv4.conf.default.rp_filter=1 >/dev/null || true

echo "已清理 hev-socks5-tunnel 网关配置。"

脚本参数说明

需要根据实际网络环境修改以下参数:

  • LAN_NET:局域网网段(如 192.168.1.0/24)
  • SELF_IP:网关机器的局域网 IP
  • MARK:必须与 config.yml 中的 socks5.mark 一致

4. 部署步骤

4.1 创建目录结构

1
2
3
4
5
6
mkdir /etc/hev-socks5-tunnel
touch /etc/hev-socks5-tunnel/config.yml
cp up.sh down.sh /etc/hev-socks5-tunnel
cp hev-socks5-tunnel /usr/local/bin/hev-socks5-tunnel
chmod 0755 /etc/hev-socks5-tunnel/up.sh
chmod 0755 /etc/hev-socks5-tunnel/down.sh

4.2 创建 systemd 服务

/etc/systemd/system/hev-socks5-tunnel.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=hev-socks5-tunnel (with policy routing scripts)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/hev-socks5-tunnel /etc/hev-socks5-tunnel/config.yml

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

4.3 启动服务

1
2
3
systemctl daemon-reload
systemctl enable --now hev-socks5-tunnel
systemctl status hev-socks5-tunnel

5. 客户端配置

在局域网内的客户端设备上,将默认网关改为网关机器的 IP(如 192.168.2.42)。


6. 故障排查

检查服务状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看服务状态
systemctl status hev-socks5-tunnel

# 查看日志
journalctl -u hev-socks5-tunnel -f

# 检查 TUN 设备
ip link show tun0
ip addr show tun0

# 检查路由规则
ip rule list
ip route show table 100

# 检查 iptables 规则
iptables -t filter -L FORWARD -n -v

# 测试连通性
curl --interface tun0 https://ipinfo.io/ip

# 客户端测试
curl -4s https://ipinfo.io/ip
dig @8.8.8.8 www.google.com +timeout=10 +retry=0

7. 进阶配置

7.3 DNS 防泄露

启用 config.yml 中的 mapdns 功能,或使用独立的 DNS 服务器(如 dnsmasq)。

NUT配置文件

不论是Linux还是Windows系统, 想要顺利获取UPS状态, 需要配置NUT的5个基本文件.

  1. nut.conf
    控制NUT运行模式, 支持standalone, netserver, netclient, master, slave, none.
    如果需要通过网络分享UPS状态信息, 运行模式需要设置为master或者netserver, 否则设置为standalone. 当前 Debian/Ubuntu 软件库中的 NUT 版本master和slave参数. 通常只需要在nut.conf中按照如下填写:

    1
    MODE=netserver
  2. ups.conf
    指定UPS设备名称还有UPS和服务器物理连接方式等.
    此文件只支持纯ASCII内容. 不同的UPS可能需要不同驱动, 在Hardware compatibility list 查找对应UPS驱动类型. 如果厂商推荐使用UPSilon 2000客户端, 驱动类型可以选择”nutdrv_qx”. 部分设备需要设置langid_fix=”0x409”, 固定USB HID 报告中的语言ID. desc用来简单描述ups设备. 通常没必要设置vendorid和productid, 这两个数值用于在连接多个UPS时, 区分不同设备.

    1
    2
    3
    4
    5
    6
    7
    [upsname]
    driver = "nutdrv_qx"
    port = auto
    langid_fix="0x409"
    desc="Constantinople 1453"
    vendorid = "0001"
    productid = "0000"

    最简单的配置:

    1
    2
    3
    4
    [upsname]
    driver = usbhid-ups
    port = auto
    desc = "Simple UPS"
  3. upsd.conf
    设置NUT服务器监听的地址和端口. 端口3493可能被Windows Docker Desktop占用.

    1
    2
    LISTEN 127.0.0.1 3493
    LISTEN 192.168.42.* 3493
  4. upsd.users
    设置访问权限.
    至少要设置一个用户才能顺利启动NUT. 在不需要控制UPS的前提下, 只需要设置一个只读用户即可.

    1
    2
    3
    [user]
    password = userpassword
    upsmon slave
  5. upsmon.conf
    定义了连接UPS服务器方式和UPS监控策略.

    1
    MONITOR upsname@127.0.0.1 1 user userpassword slave

Debian/Ubuntu配置NUT

  1. 安装 NUT 程序
    使用以下命令安装 NUT:

    1
    apt install nut
  2. 配置 UPS
    修改ups.conf 文件, 根据Hardware compatibility list 填写相应配置. 也可以通过以下命令扫描 UPS 设备, 并将 UPS信息填入 /etc/nut/ups.conf:

    1
    nut-scanner -U

    将修改后的ups.conf和其他配置文件保存到 /etc/nut 目录.

  3. 启动服务
    依次启动 NUT 相关服务:

    1
    2
    systemctl enable nut.targe
    systemctl restart nut-server
  4. 测试 UPS 状态

    1
    upsc upsname@127.0.0.1:3493

    若能正确输出 UPS 信息, 则说明配置成功.

Windows配置NUT

  1. 安装NUT主程序

    • 下载并执行安装程序.
      安装程序会尝试安装驱动, 此处忽略安装失败的错误提示. 记录VID和PID数值:
      1
      Found UPS : vendor ID = 1 - Product ID = 0
    • 安装完毕后, 执行程序目录中others文件夹中wdi-simple.exe程序手动安装驱动. 根据提示将UPS数据线连接至电脑.
    • 驱动完成安装后可在设备管理器找到”NUT USB Driver”
  2. 下载DLL文件
    NUT 需要一些缺少的库文件, 必须手动添加到程序目录:

    • 必须文件: libeay32.dll、ssleay32.dll、libgcc_s_dw2-1.dll.
    • libeay32.dll 和 ssleay32.dll 为 OpenSSL 运行库, 确保它们版本相同.
    • 将 libeay32.dll 和 ssleay32.dll 复制到 bin 文件夹.
    • 将 libeay32.dll、ssleay32.dll 和 libgcc_s_dw2-1.dll 复制到 sbin 文件夹.
  3. 配置文件

    • 修改ups.conf 文件, 填写VID和PID数值, 根据Hardware compatibility list 填写相应配置.
    • 将修改后的ups.conf和其他配置文件保存到C:\Program Files (x86)\NUT\etc 目录.
  4. 设置防护墙

    • NUT默认使用3493端口, 此端口可能被docker服务占用. 建议使用NETSTAT.EXE命令确认端口占用情况, 并修改默认端口.
    • 在防火墙设置中添加新规则, 开放upsd.conf中设置的端口.
    • 重启”Network UPS Tools”服务.
  5. 测试

    • 在bin目录下执行命令, 测试 UPS 状态:
      1
      .\upsc.exe ups@127.0.0.1:3493
    • 若终端能够正确输出 UPS 信息, 说明配置成功.
    • 更换USB接口需要重启服务.
  6. 客户端
    Windows 推荐使用WinNUT作为UPS客户端.

参考:

客户端选择

  • I2P
    基于java的I2P客户端,具有一个网页版的控制台。因为是第一个实现I2P协议的客户端,所以网络上很多教程是基于这个版本的。https://geti2p.net/
  • I2P Plus
    和I2P一样基于java,增强了网页版的控制台。https://i2pplus.github.io
  • I2P Daemon
    基于C++ 实现的I2P 客户端。应用本身占用空间很少。缺少网页控制台,只有一个查看网络信息的网页。这个版本有详细的配置文档。 https://i2pd.website/

上述三个版本都支持除了IOS外的主流操作系统(Linux, BSD, Windows, Android)和容器化部署(X86, ARM)。

I2P网络和常规互联网

I2P网络和通常的互联网不互通。为了通过I2P网络访问互联网,需要配置一个代理作为出口。
I2P社区推荐使用I2P Outproxy,目前已知的i2p-outproxy代理有:

如何设置这些出口,可以参考stormycloud的文档。目前没有发现这些服务的开源实现。
如果需要更稳定的隐蔽连接,可以在服务器部署Tor, 并在i2pd.conf中设置socksproxy将流量指向Tor作为出口。
在牺牲隐蔽性的前提下,可以利用隧道转发HTTP代理实现通过I2P网络访问互联网的需求。

使用I2PD配置隧道

I2P协议支持一定的内网穿透功能,因此可以配置简单的转发隧道。这些隧道不需要公网支持,但是延迟高,速度慢,只能作为应急使用。因为I2PD占用系统资源少,配置隧道不依赖于GUI,而且其docker镜像大小不高于20M,所以推荐使用这个版本配置I2P隧道。
I2PD需要两个配置文件i2pd.conf和tunnels.conf.

  1. 配置i2pd.conf
    此配置关闭了中继功能,开启了全部隐蔽选项。该配置适合简单浏览网页,更详细的配置参看I2PD文档示例

    • 如果要建立隧道,[http]和[i2pcontrol]至少要开启一个。

    • 如果不需要浏览i2p网页,可以关闭[httpproxy]和[socksproxy].

    • address是监听地址,如果允许局域网其他服务器访问,需要修改地址为I2P服务器地址。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      log = none

      daemon = true

      ## Port to listen for connections
      ## By default i2pd picks random port. You MUST pick a random number too,
      ## don't just uncomment this
      # port = 4567

      ipv4 = true
      ipv6 = false

      bandwidth = 256
      notransit = true
      floodfill = true

      [ntcp2]
      enabled = true
      published = false
      port = 4567

      [ssu2]
      enabled = true
      published = false
      port = 4567

      [http]
      enabled = false
      address = 127.0.0.1
      port = 7070
      webroot = /

      [httpproxy]
      enabled = true
      address = 127.0.0.1
      port = 4444
      keys = http-proxy-keys.dat
      addresshelper = true
      # outproxy = http://false.i2p

      [socksproxy]
      enabled = false
      address = 127.0.0.1
      port = 4447
      keys = socks-proxy-keys.dat
      outproxy.enabled = false
      outproxy = 127.0.0.1
      outproxyport = 9050

      [sam]
      enabled = false

      [bob]
      enabled = false

      [i2cp]
      enabled = false

      [i2pcontrol]
      enabled = false
      address = 127.0.0.1
      port = 7650
      password = itoopie

      [precomputation]
      elgamal = true

      [upnp]
      enabled = false
      name = I2Pd

      [meshnets]
      yggdrasil = false

      [reseed]
      verify = true
      # proxy = http://127.0.0.1:8118 # or socks://address:port

      [trust]
      hidden = true

      [persist]
      profiles = true
      addressbook = true

      [cpuext]
      aesni = true

  2. 配置tunnels.conf
    这里配置了可以访问服务器的ssh隧道和HTTP代理隧道以及一个可以在I2P网络访问的http服务器。通过I2P地址访问对应端口的流量会转发到服务器的对应端口。服务器需要额外安装支持相关功能的软件(ssh server、http proxy server、http server)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    [ssh-tunnel]
    type = server
    host = 127.0.0.1
    port = 22
    keys = ssh-tunnel-key.dat

    [proxy]
    type = server
    host = 127.0.0.1
    port = 3128
    keys = proxy-key.dat

    [website]
    type = http
    host = 127.0.0.1
    port = 8080
    keys = website-key.dat
  3. 使用docker部署,其他安装方式参考文档

    1
    2
    3
    4
    5
    6
    docker pull purplei2p/i2pd

    docker run -d --name i2pd --network=host --restart always \
    -v "$PWD/i2pd.conf:/home/i2pd/data/i2pd.conf" \
    -v "$PWD/tunnels.conf:/home/i2pd/data/tunnels.conf" \
    i2pd
  4. 获得隧道的I2P地址
    每个隧道会被赋予唯一的b32.i2p地址,记录隧道的地址。

    • 使用I2PC获取地址
      1
      2
      3
      4
      5
      6
      7
      curl -X POST http://127.0.0.1:7650/json -d '{
      "method": "TunnelInfo",
      "params": {
      "Token": "your_access_token"
      },
      "id": 2
      }'
    • 使用http获取地址
      1
      curl http://127.0.0.1:7070/?page=i2p_tunnels|grep "i2p"
  5. 连接隧道的配置
    其他设备使用i2pd连接隧道,需要设置tunnels.conf,并填写对应的b32.i2p地址:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [ssh-tunnel]
    type = client
    destination = <ssh-tunnel b32.i2p>:22
    port = 40022
    address = 0.0.0.0
    keys = ssh-tunnel-client-key.dat
    inbound.length = 1
    outbound.length = 1

    [proxy-client]
    type = client
    destination = <proxy b32.i2p>:3128
    port = 43128
    address = 0.0.0.0
    keys = proxy-client-key.dat
  6. 测试隧道

    1
    2
    ssh 127.0.0.1:40022
    curl -X 127.0.0.1:43128 https://ifconfig.me/

相关连接

i2pd docker-compose 部署 http代理
中文论坛
Reg.i2p registry service
Let’s Decentralize
Wolfram MathWorld

可能存在的问题

1. 使用cloudflare warp进行内网穿透可能无法订阅中继站
2. S3_ALIAS_HOST需要和LOCAL_DOMAIN使用相同域名,将S3资源定义为Mastodon域名下的路径,并做转发
3. S3设置可能不是最优设置,Just works
4. 注释docker-compose.yml的build提高部署效率

提前准备

  • 支持smtp协议的Email账户、密码(应用码)
  • 域名
  • 服务器
  • S3 (可选)

根据配置文件部署

  1. 下载mastodon配置文件和docker-compose.yml

    1
    2
    git clone https://github.com/mastodon/mastodon.git
    git fetch && git checkout v4.2.12
  2. 根据配置文件(.env.production.sample)要求填写信息:

    • 额外添加 SINGLE_USER_MODE=true 设置单用户模式
    • S3_ALIAS_HOST需要和LOCAL_DOMAIN使用相同域名,将S3资源定义为Mastodon域名下的路径,例如:
      1
      2
      LOCAL_DOMAIN=mastodon.example.com
      S3_ALIAS_HOST=mastodon.example.com/s3
    • VAPID生成
      1
      docker compose run --rm web rake mastodon:webpush:generate_vapid_key
    • SECRET_KEY_BASE, OTP_SECRET生成
      1
      docker compose run --rm -- web bundle exec rake secret
    • ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY \ ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT \ ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
      1
      docker compose run --rm -- web bundle exec rails db:encryption:init
  3. 初始化数据库

    1. 初始化Postgres

      1
      2
      3
      4
      docker run --rm --name postgres \
      -v $PWD/postgres14:/var/lib/postgresql/data \
      -e POSTGRES_PASSWORD="098f6bcd4621d37" \
      -d postgres:14-alpine
    2. 进入Postgres 控制台

      1
      docker exec -it postgres psql -U postgres
    3. 创建用户和数据库

      1
      2
      3
      4
      5
      CREATE USER pg_mastodon WITH PASSWORD 'pg_passowrd' CREATEDB;
      CREATE DATABASE db_mastodon;
      \c db_mastodon;
      CREATE extension pg_stat_statements;
      exit
    4. 关闭数据库

      1
      docker stop postgres
  4. S3

    1. 参考Cloudflare说明开启S3_BUCKET,记录信息:
    • BUCKET_NAME
      
    • ACCESS_KEY_ID
      
    • SECRET_ACCESS_KEY
      
    • S3_ENDPOINT
      
    1. 设置CROS策略:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      [
      {
      "AllowedOrigins": [
      "*"
      ],
      "AllowedMethods": [
      "GET",
      "HEAD"
      ]
      }
      ]
    2. 在控制台设置自定义域

    s3.example.com

    1. 设置如下
      1
      2
      3
      4
      5
      6
      7
      S3_ENABLED=true
      S3_BUCKET=BUCKET_NAME
      S3_ENDPOINT=https://your_id.r2.cloudflarestorage.com/mastodon
      AWS_ACCESS_KEY_ID={ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY={SECRET_ACCESS_KEY}
      S3_ALIAS_HOST=mastodon.example.com/s3
      S3_PERMISSION = "private"
  5. 生成容器

    1
    docker compose run --rm -v $(pwd)/.env.production:/opt/mastodon/.env.production web bundle exec rake db:setup
  6. 配置权限

    1
    2
    chown -R 70:70 postgres14
    chown -R 991:991 public
  7. 执行

    1
    docker compose up -d
  8. 生成管理员用户名和密码

    1
    2
    docker exec mastodon-web-1 tootctl accounts create "username" --email "user@email.com" --confirmed --role Owner
    docker exec mastodon-web-1 tootctl accounts modify --approve "username"

交互式部署

  1. 初始化Postgres

    1
    2
    3
    4
    docker run --rm --name postgres \
    -v $PWD/postgres14:/var/lib/postgresql/data \
    -e POSTGRES_PASSWORD="098f6bcd4621d37" \
    -d postgres:14-alpine
  2. 进入Postgres 控制台

    1
    docker exec -it postgres psql -U postgres
  3. 创建用户和数据库

    1
    2
    3
    4
    5
    CREATE USER pg_mastodon WITH PASSWORD 'pg_passowrd' CREATEDB;
    CREATE DATABASE db_mastodon;
    \c db_mastodon;
    CREATE extension pg_stat_statements;
    exit
  4. 关闭数据库

    1
    docker stop postgres
  5. 根据安装向导填写信息

    1
    2
    wget https://raw.githubusercontent.com/mastodon/mastodon/main/docker-compose.yml
    docker compose run --rm web bundle exec rake mastodon:setup
  6. 配置权限

    1
    2
    chown -R 70:70 postgres14
    chown -R 991:991 public
  7. 执行

    1
    docker compose up -d

Caddy2 反向代理

  • Mastodon仅支持引用与其相同域名下的资源。为了能够在Mastodon中引用存储在S3上的资源,通过设置S3_ALIAS_HOST将S3资源定义为Mastodon域名下的路径。这需要使用URL重写技术,以便请求能够被转发到S3服务器。
  • 注意将root * /home/user/mastodon/public修改为合适的位置。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    mastodon.powernut.rocks {
    @local {
    file
    not path /
    }

    log {
    output file /var/log/caddy/mastodon.log
    }

    @local_media {
    path_regexp /system/(.*)
    }
    @streaming {
    path /api/v1/streaming/*
    }
    @cache_control {
    path_regexp ^/(emoji|packs|/system/accounts/avatars|/system/media_attachments/files)
    }

    root * /home/user/mastodon/public

    encode zstd gzip

    handle_errors {
    rewrite 500.html
    file_server
    }

    header {
    Strict-Transport-Security "max-age=31536000"
    }
    header /sw.js Cache-Control "public, max-age=0"
    header @cache_control Cache-Control "public, max-age=31536000, immutable"

    handle @local {
    file_server
    }

    # If you've been migrated media from local to object storage, this navigate old URL to new one.
    # redir @local_media https://yourobjectstorage.example.com/{http.regexp.1} permanent

    reverse_proxy @streaming {
    to http://127.0.0.1:4000

    transport http {
    keepalive 5s
    keepalive_idle_conns 10
    }
    }

    reverse_proxy {
    to http://127.0.0.1:3000

    header_up X-Forwarded-Port 443
    header_up X-Forwarded-Proto https

    transport http {
    keepalive 5s
    keepalive_idle_conns 10
    }
    }

    handle_path /s3/* {
    rewrite * /mastodon-buckets_name{path}
    reverse_proxy https://s3.server.com {
    header_up Host {http.reverse_proxy.upstream.hostport}
    }
    }
    }

注意要点

  • 配置Cloudflare中部署指令不要使用 hexo generate,需要使用
    npx hexo generate
  • 环境变量需要指定YARN版本
    YARN_VERSION=1.22.10

详细步骤

1. 创建私有仓库

  • 登录GitHub:确保您已登录GitHub。
  • 创建新仓库:点击右上角的“New repository”按钮。
  • 设置仓库为私有:选择“Private”选项,以保护您的内容。
  • 填写仓库详情:为仓库命名(例如your-hexo-blog),可选地添加描述,然后点击“Create repository”。

2. Clone到本地

打开您的计算机上的命令行工具,然后使用以下命令克隆仓库:

1
git clone https://github.com/yourusername/your-hexo-blog.git

3. 初始化Hexo

在克隆的仓库所在的父目录中,运行以下命令来初始化您的Hexo博客:

1
hexo init your-hexo-blog

这将在your-hexo-blog目录下创建一个新的Hexo项目。

4. 安装依赖

进入新创建的your-hexo-blog目录,然后安装Hexo及其依赖:

1
2
cd your-hexo-blog
npm install

5. 对Hexo进行配置

your-hexo-blog目录中,找到_config.yml文件并使用文本编辑器进行必要的配置。

6. 上传至仓库

将您的Hexo项目上传到GitHub:

1
2
3
git add .
git commit -m "Initial Hexo site"
git push origin main

7. 在Cloudflare中部署

  • 登录Cloudflare:访问Cloudflare Dashboard并登录。
  • 创建应用程序:找到“Workers 和 Pages”,选择创建“Page”应用程序。
  • 关联GitHub仓库:按指示选择GitHub账户和仓库,授权Cloudflare访问。
  • 配置构建和发布设置
    • 构建命令npx hexo generate
    • 发布目录public

8. 设置环境变量

在Cloudflare Pages的设置中,添加以下环境变量:

  • NODE_VERSION=18.12.0
  • YARN_VERSION=1.22.10