test
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -49,4 +49,8 @@ resources/Pure_Component/
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
cmd/api/__debug_bin*
|
cmd/api/__debug_bin*
|
||||||
|
|
||||||
|
# 戎行 OpenVPN 机密(仅保留 secrets/openvpn/.gitkeep)
|
||||||
|
secrets/openvpn/*
|
||||||
|
!secrets/openvpn/.gitkeep
|
||||||
@@ -860,6 +860,8 @@ rongxing:
|
|||||||
app_id: "hykj"
|
app_id: "hykj"
|
||||||
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
# 仅戎行请求走 VPN SOCKS;本机已连 OpenVPN GUI 时可留空。生产 Docker:socks5://rongxing-vpn:1080
|
||||||
|
proxy: ""
|
||||||
logging:
|
logging:
|
||||||
enabled: true
|
enabled: true
|
||||||
log_dir: "logs/external_services"
|
log_dir: "logs/external_services"
|
||||||
|
|||||||
@@ -279,6 +279,8 @@ rongxing:
|
|||||||
app_id: "hykj"
|
app_id: "hykj"
|
||||||
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
# 开发机用 OpenVPN GUI 直连时可留空;若走本地 Docker VPN 可填 socks5://rongxing-vpn:1080
|
||||||
|
proxy: ""
|
||||||
logging:
|
logging:
|
||||||
enabled: true
|
enabled: true
|
||||||
log_dir: "logs/external_services"
|
log_dir: "logs/external_services"
|
||||||
|
|||||||
@@ -245,6 +245,8 @@ rongxing:
|
|||||||
app_id: "hykj"
|
app_id: "hykj"
|
||||||
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
private_key: "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI1eWDx325U/DCpaw+394555voDNwKyfg6rAIBYGSmJDmUyCi6c8LeqVtYnmtUMJrTDrfUWFG7jQ+mQy65rXMY7zrZ0Cc+orP7uRgrvCBsH1775KuSji8TkVbEBw+Upro7FuNkutBItNxZCvcpFpNqNBwCCkCK1xscVN8gErxc7/AgMBAAECgYAL/ZVHWU7Ni5TyrLmTBwAjWD+RX9V4iGkb3QLiGCayZS05NGNq/ytrCvqxMSY6HIKAZ6Du+hmXvr+JXll/slvyGs1ETOgDi7563RAT/2TVicZF16IM2d7nhK6eTJffmiG2ZZC8n043F0QNposleEIMhM251iT1xiwZfg+QqHB0EQJBAI+BFcbYl+Vxpxdouvuwq11gYMTNepcGdY3OaPZzW4sQA41s+6aZhZ7tAk65Tk7PgLfdU2yimBKNkJstlSUT1w8CQQD8MKjAi5wngnDO+04n5mt5EotpNy6xpvkQP2izL0FWKmvwZtc0ihJpxa64vfo4+1jE8YXx1/qGe16A15fmywkRAkAbImVjvAC8ucjGfF8eyEEe3uJtVA0iEW6Y6bafIyDkIpsJWtoanlzNuDL/f7p23HWSTp8/o17t4ya8sNnKsP2xAkEArIQc7JqUl/KDeRQwwtq9anVlKPS23JB8kMDPvsP0zhz2+d1gGnDZZ8HzZC2RnqlScGdIWciFeLmsTDcvkpISAQJANaQU0uKBPvgMx+uedbCbn9MfYmpEiONHblrINH8WAa3Z1pr7R0wBUog1fEWimnxOX5wGwsGmDOMw5B+4xJ4Zfw=="
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
# 生产:戎行请求经 Docker 内 rongxing-vpn 的 SOCKS5 进企业内网
|
||||||
|
proxy: "socks5://rongxing-vpn:1080"
|
||||||
logging:
|
logging:
|
||||||
enabled: true
|
enabled: true
|
||||||
log_dir: "logs/external_services"
|
log_dir: "logs/external_services"
|
||||||
|
|||||||
25
deployments/openvpn/Dockerfile
Normal file
25
deployments/openvpn/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 戎行专用:OpenVPN 客户端 + SOCKS5(仅内网代理,不接管默认路由)
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
openvpn \
|
||||||
|
bash \
|
||||||
|
iproute2 \
|
||||||
|
microsocks \
|
||||||
|
curl
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
ENV VPN_DIR=/vpn \
|
||||||
|
SOCKS_HOST=0.0.0.0 \
|
||||||
|
SOCKS_PORT=1080 \
|
||||||
|
RONGXING_HOST=192.168.3.43
|
||||||
|
|
||||||
|
VOLUME ["/vpn"]
|
||||||
|
EXPOSE 1080
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=5 \
|
||||||
|
CMD ip link show tun0 >/dev/null 2>&1 || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
13
deployments/openvpn/README.md
Normal file
13
deployments/openvpn/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Rongxing OpenVPN(部署附件)
|
||||||
|
|
||||||
|
完整方案见:[docs/戎行OpenVPN接入方案.md](../../docs/戎行OpenVPN接入方案.md)
|
||||||
|
|
||||||
|
| 文件 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `Dockerfile` | OpenVPN + microsocks 自建镜像 |
|
||||||
|
| `entrypoint.sh` | 启动 VPN(仅路由戎行 IP)并监听 SOCKS5 `:1080` |
|
||||||
|
| `tiany0721.ovpn.example` | ovpn 模板 |
|
||||||
|
| `docker-compose.rongxing-vpn.snippet.yml` | 独立编排参考(prod 已内置) |
|
||||||
|
| `upload-checklist.txt` | 上传清单 |
|
||||||
|
|
||||||
|
**真实证书放到服务器 secrets 目录或 `./secrets/openvpn/`,不要提交 Git。**
|
||||||
31
deployments/openvpn/docker-compose.rongxing-vpn.snippet.yml
Normal file
31
deployments/openvpn/docker-compose.rongxing-vpn.snippet.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 已并入 docker-compose.prod.yml 的 rongxing-vpn 服务。
|
||||||
|
# 保留本文件便于 1Panel 单独编排时参考。
|
||||||
|
#
|
||||||
|
# 1Panel 机密目录示例:
|
||||||
|
# RONGXING_VPN_CONFIG=/opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
#
|
||||||
|
# 独立栈时需 external 网络(名称以 docker network ls 为准):
|
||||||
|
# networks:
|
||||||
|
# hyapi-network:
|
||||||
|
# external: true
|
||||||
|
# name: hyapi-server_hyapi-network
|
||||||
|
|
||||||
|
services:
|
||||||
|
rongxing-vpn:
|
||||||
|
build:
|
||||||
|
context: ./deployments/openvpn
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: hyapi-rongxing-vpn:local
|
||||||
|
container_name: hyapi-rongxing-vpn
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
RONGXING_HOST: "192.168.3.43"
|
||||||
|
volumes:
|
||||||
|
- ${RONGXING_VPN_CONFIG:-./secrets/openvpn}:/vpn:ro
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
networks:
|
||||||
|
- hyapi-network
|
||||||
|
restart: unless-stopped
|
||||||
69
deployments/openvpn/entrypoint.sh
Normal file
69
deployments/openvpn/entrypoint.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# OpenVPN 连通后拉起 SOCKS5;仅服务戎行内网代理。
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VPN_DIR="${VPN_DIR:-/vpn}"
|
||||||
|
SOCKS_HOST="${SOCKS_HOST:-0.0.0.0}"
|
||||||
|
SOCKS_PORT="${SOCKS_PORT:-1080}"
|
||||||
|
RONGXING_HOST="${RONGXING_HOST:-192.168.3.43}"
|
||||||
|
|
||||||
|
cd "$VPN_DIR"
|
||||||
|
|
||||||
|
CONF=""
|
||||||
|
for f in *.conf *.ovpn; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
CONF="$f"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$CONF" ]; then
|
||||||
|
echo "[rongxing-vpn] 未在 ${VPN_DIR} 找到 .ovpn/.conf,请挂载证书目录"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[rongxing-vpn] 使用配置: ${CONF}"
|
||||||
|
echo "[rongxing-vpn] 仅路由 ${RONGXING_HOST}/32,SOCKS5 ${SOCKS_HOST}:${SOCKS_PORT}"
|
||||||
|
|
||||||
|
openvpn \
|
||||||
|
--config "$CONF" \
|
||||||
|
--cd "$VPN_DIR" \
|
||||||
|
--route-nopull \
|
||||||
|
--route "$RONGXING_HOST" 255.255.255.255 \
|
||||||
|
--daemon \
|
||||||
|
--writepid /var/run/openvpn.pid \
|
||||||
|
--log /var/log/openvpn.log \
|
||||||
|
--verb 3
|
||||||
|
|
||||||
|
for i in $(seq 1 90); do
|
||||||
|
if ip link show tun0 >/dev/null 2>&1; then
|
||||||
|
echo "[rongxing-vpn] tun0 已就绪 (${i}s)"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ "$i" -eq 90 ]; then
|
||||||
|
echo "[rongxing-vpn] 等待 tun0 超时,OpenVPN 日志:"
|
||||||
|
cat /var/log/openvpn.log || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# 前台跑 microsocks,进程退出则容器退出;同时监控 openvpn
|
||||||
|
microsocks -i "$SOCKS_HOST" -p "$SOCKS_PORT" &
|
||||||
|
SOCKS_PID=$!
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
kill "$SOCKS_PID" 2>/dev/null || true
|
||||||
|
if [ -f /var/run/openvpn.pid ]; then
|
||||||
|
kill "$(cat /var/run/openvpn.pid)" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
while kill -0 "$(cat /var/run/openvpn.pid)" 2>/dev/null && kill -0 "$SOCKS_PID" 2>/dev/null; do
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[rongxing-vpn] OpenVPN 或 SOCKS 已退出"
|
||||||
|
cat /var/log/openvpn.log || true
|
||||||
|
exit 1
|
||||||
6
deployments/openvpn/env.example
Normal file
6
deployments/openvpn/env.example
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 可选:复制为 .env 供 docker compose 读取
|
||||||
|
# 1Panel 推荐:
|
||||||
|
RONGXING_VPN_CONFIG=/opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
|
||||||
|
# 本地默认(不设则用 compose 内 ./secrets/openvpn):
|
||||||
|
# RONGXING_VPN_CONFIG=./secrets/openvpn
|
||||||
23
deployments/openvpn/tiany0721.ovpn.example
Normal file
23
deployments/openvpn/tiany0721.ovpn.example
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 复制为服务器上的 tiany0721.ovpn,并与 ca.crt / 证书 / 密钥同目录放置。
|
||||||
|
# 勿将真实证书提交到 Git。
|
||||||
|
|
||||||
|
client
|
||||||
|
dev tun
|
||||||
|
proto tcp
|
||||||
|
remote 111.31.14.226 32199
|
||||||
|
resolv-retry infinite
|
||||||
|
nobind
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
||||||
|
ca ca.crt
|
||||||
|
cert tiany0721.crt
|
||||||
|
key tiany0721.key
|
||||||
|
remote-cert-tls server
|
||||||
|
tls-auth ta.key 1
|
||||||
|
cipher AES-256-CBC
|
||||||
|
compress lz4-v2
|
||||||
|
verb 3
|
||||||
|
|
||||||
|
# 仅路由戎行主机,不接管默认网关(其它流量仍走容器原出口)
|
||||||
|
route-nopull
|
||||||
|
route 192.168.3.43 255.255.255.255
|
||||||
17
deployments/openvpn/upload-checklist.txt
Normal file
17
deployments/openvpn/upload-checklist.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 从本机 C:\Users\a1726\Desktop\11\OpenVPN\config 上传到服务器:
|
||||||
|
# /opt/1panel/apps/hyapi-secrets/openvpn/
|
||||||
|
#
|
||||||
|
# 需要:
|
||||||
|
# [ ] tiany0721.ovpn (上传后按 example 加上 route-nopull + route)
|
||||||
|
# [ ] ca.crt
|
||||||
|
# [ ] tiany0721.crt
|
||||||
|
# [ ] tiany0721.key
|
||||||
|
# [ ] ta.key
|
||||||
|
#
|
||||||
|
# 不需要:
|
||||||
|
# [x] bin/、doc/、Uninstall.exe、openvpn-gui.exe 等 Windows 安装文件
|
||||||
|
#
|
||||||
|
# 权限:
|
||||||
|
# chmod 700 /opt/1panel/apps/hyapi-secrets /opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
# chmod 600 *.key
|
||||||
|
# chmod 644 *.crt *.ovpn
|
||||||
@@ -38,6 +38,45 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "25010:5432"
|
- "25010:5432"
|
||||||
|
|
||||||
|
# 戎行 OpenVPN 客户端 + SOCKS5(仅 192.168.3.43 走隧道)
|
||||||
|
# 证书目录:默认 ./secrets/openvpn;1Panel 建议:
|
||||||
|
# export RONGXING_VPN_CONFIG=/opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
rongxing-vpn:
|
||||||
|
build:
|
||||||
|
context: ./deployments/openvpn
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: hyapi-rongxing-vpn:local
|
||||||
|
container_name: hyapi-rongxing-vpn
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
RONGXING_HOST: "192.168.3.43"
|
||||||
|
SOCKS_HOST: "0.0.0.0"
|
||||||
|
SOCKS_PORT: "1080"
|
||||||
|
volumes:
|
||||||
|
- ${RONGXING_VPN_CONFIG:-./secrets/openvpn}:/vpn:ro
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
networks:
|
||||||
|
- hyapi-network
|
||||||
|
# 禁止把 1080 暴露到宿主机公网
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "ip link show tun0 >/dev/null 2>&1 || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 256M
|
||||||
|
cpus: "0.3"
|
||||||
|
reservations:
|
||||||
|
memory: 64M
|
||||||
|
cpus: "0.1"
|
||||||
|
|
||||||
# Redis 缓存 (生产环境)
|
# Redis 缓存 (生产环境)
|
||||||
redis:
|
redis:
|
||||||
image: redis:8.0.2
|
image: redis:8.0.2
|
||||||
@@ -101,6 +140,8 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
rongxing-vpn:
|
||||||
|
condition: service_started
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -134,6 +175,8 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
rongxing-vpn:
|
||||||
|
condition: service_started
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "ps", "aux", "|", "grep", "worker"]
|
test: ["CMD", "ps", "aux", "|", "grep", "worker"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|||||||
262
docs/戎行OpenVPN接入方案.md
Normal file
262
docs/戎行OpenVPN接入方案.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# 戎行数据源 OpenVPN 接入方案(1Panel)
|
||||||
|
|
||||||
|
> 目标:**仅** `rongxing`(`http://192.168.3.43:7007`)走企业 VPN;Postgres / Redis / 其它外部 API 仍走原出口。
|
||||||
|
> 方案:**OpenVPN 容器 + SOCKS5 代理 + 戎行 HTTP 客户端单独走代理**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
hyapi-network(现有 bridge)
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ hyapi-app / hyapi-worker │
|
||||||
|
│ ├─ DB / Redis / 其它 API ──► 默认出口(不经 VPN) │
|
||||||
|
│ └─ 戎行 Client ──► socks5://rongxing-vpn:1080 │
|
||||||
|
│ │ │
|
||||||
|
│ rongxing-vpn(openvpn + microsocks)│ │
|
||||||
|
│ TUN ──► OpenVPN Server 111.31.14.226:32199 │
|
||||||
|
│ ──► 企业内网 192.168.3.43:7007 │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
要点:
|
||||||
|
|
||||||
|
- 业务容器网络身份不变(端口、健康检查、服务发现保持现状)。
|
||||||
|
- VPN 只负责进企业网;对外暴露的是 Docker 内网 SOCKS5,不映射到公网。
|
||||||
|
- OpenVPN 使用 `route-nopull`,只加 `192.168.3.43`,避免抢走默认路由。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. VPN 材料要不要上传?
|
||||||
|
|
||||||
|
**要上传,但只上传「客户端配置与证书」,不要上传整个 Windows OpenVPN 安装目录。**
|
||||||
|
|
||||||
|
| 本地路径(你机器) | 是否上传 | 说明 |
|
||||||
|
|--------------------|----------|------|
|
||||||
|
| `OpenVPN\config\tiany0721.ovpn` | ✅ | 连接配置(服务器上会再加路由控制指令) |
|
||||||
|
| `OpenVPN\config\ca.crt` | ✅ | CA |
|
||||||
|
| `OpenVPN\config\tiany0721.crt` | ✅ | 客户端证书 |
|
||||||
|
| `OpenVPN\config\tiany0721.key` | ✅ | 客户端私钥(敏感) |
|
||||||
|
| `OpenVPN\config\ta.key` | ✅ | tls-auth |
|
||||||
|
| `OpenVPN\bin\`、`doc\`、`Uninstall.exe` 等 | ❌ | Windows 安装包,容器不用 |
|
||||||
|
|
||||||
|
服务器上使用 **Linux 版 OpenVPN 镜像**,复用上述 5 个文件即可。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 1Panel 推荐目录(最优落点)
|
||||||
|
|
||||||
|
假设 1Panel 数据盘常用根为 `/opt/1panel`(若你改过安装目录,把前缀换成实际路径)。
|
||||||
|
|
||||||
|
**推荐:密钥与业务代码分离。**
|
||||||
|
|
||||||
|
```text
|
||||||
|
/opt/1panel/apps/hyapi/ # 业务 Compose / 代码(可随发布更新)
|
||||||
|
├── docker-compose.prod.yml
|
||||||
|
├── config.yaml / configs/...
|
||||||
|
├── ...
|
||||||
|
└── (不要把 .key 放进 git 仓库目录)
|
||||||
|
|
||||||
|
/opt/1panel/apps/hyapi-secrets/openvpn/ # ★ VPN 机密目录(推荐)
|
||||||
|
├── tiany0721.ovpn # 服务器侧改过的版本(见下文)
|
||||||
|
├── ca.crt
|
||||||
|
├── tiany0721.crt
|
||||||
|
├── tiany0721.key # 权限 600
|
||||||
|
└── ta.key # 权限 600
|
||||||
|
```
|
||||||
|
|
||||||
|
备选(同样可以):
|
||||||
|
|
||||||
|
```text
|
||||||
|
/data/hyapi/secrets/openvpn/ # 若 1Panel 数据盘挂在 /data
|
||||||
|
```
|
||||||
|
|
||||||
|
**不要放:**
|
||||||
|
|
||||||
|
- 网站静态目录 `/opt/1panel/www/...`(易被误暴露)
|
||||||
|
- 仓库内 `hyapi-server/config/` 或会随 CI 同步的路径(防泄漏)
|
||||||
|
|
||||||
|
**权限建议(SSH 上执行):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
sudo chmod 700 /opt/1panel/apps/hyapi-secrets
|
||||||
|
sudo chmod 700 /opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
# 上传文件后:
|
||||||
|
sudo chmod 600 /opt/1panel/apps/hyapi-secrets/openvpn/*.key
|
||||||
|
sudo chmod 644 /opt/1panel/apps/hyapi-secrets/openvpn/*.{crt,ovpn}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 服务器侧 OpenVPN 配置(准备好再挂载)
|
||||||
|
|
||||||
|
把本地 `tiany0721.ovpn` 拷到服务器后,**追加**以下行(保留原有 `remote` / 证书引用):
|
||||||
|
|
||||||
|
```conf
|
||||||
|
# --- 仅路由戎行内网,不接管默认网关 ---
|
||||||
|
route-nopull
|
||||||
|
route 192.168.3.43 255.255.255.255
|
||||||
|
```
|
||||||
|
|
||||||
|
完整示例见仓库:`deployments/openvpn/tiany0721.ovpn.example`。
|
||||||
|
|
||||||
|
证书相对路径保持与 `.ovpn` 同目录即可(`ca ca.crt` 等),挂载整个目录到容器 `/vpn`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Compose 片段(接入现有 prod)
|
||||||
|
|
||||||
|
参考文件:`deployments/openvpn/docker-compose.rongxing-vpn.snippet.yml`
|
||||||
|
|
||||||
|
核心服务示意:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rongxing-vpn:
|
||||||
|
image: ghcr.io/linuxserver/openvpn-as:latest # 不推荐 AS;见下方推荐镜像
|
||||||
|
```
|
||||||
|
|
||||||
|
**推荐用轻量客户端镜像**(文档配套 snippet 已按此写):
|
||||||
|
|
||||||
|
- 镜像:`dperson/openvpn-client` **或** 自建 `alpine + openvpn + microsocks`
|
||||||
|
- 能力:`cap_add: [NET_ADMIN]`,`devices: [/dev/net/tun]`
|
||||||
|
- 挂载:`/opt/1panel/apps/hyapi-secrets/openvpn:/vpn:ro`
|
||||||
|
- 网络:加入现有 `hyapi-network`
|
||||||
|
- **不要**把 `1080` 映射到宿主机公网端口
|
||||||
|
|
||||||
|
业务侧:
|
||||||
|
|
||||||
|
- `hyapi-app` /(若异步也会调戎行)`hyapi-worker` 保持原 `networks: [hyapi-network]`
|
||||||
|
- 配置增加代理地址:`socks5://rongxing-vpn:1080`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 应用配置(仅戎行)
|
||||||
|
|
||||||
|
在 `rongxing` 段增加代理字段(实现代码时再接线;配置先预留):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rongxing:
|
||||||
|
url: "http://192.168.3.43:7007"
|
||||||
|
account: "..."
|
||||||
|
password: "..."
|
||||||
|
app_id: "..."
|
||||||
|
private_key: "..."
|
||||||
|
timeout: 10s
|
||||||
|
# 仅戎行走 VPN 旁路代理;留空则直连(开发机已连 VPN 时可留空)
|
||||||
|
proxy: "socks5://rongxing-vpn:1080"
|
||||||
|
logging:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
开发机若已用 GUI 连上 VPN,`proxy` 留空即可,无需起 Docker VPN。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 代码改动清单(后续开发)
|
||||||
|
|
||||||
|
当前 `RongxingService` 使用裸 `http.Client`,需:
|
||||||
|
|
||||||
|
1. `RongxingConfig` / `serviceConfig` 增加 `Proxy string`
|
||||||
|
2. `NewRongxingServiceWithConfig` 传入 `cfg.Rongxing.Proxy`
|
||||||
|
3. 创建 `http.Client` 时:若 `Proxy` 非空,用 `http.ProxyURL` 或 SOCKS5 dialer(`golang.org/x/net/proxy`)
|
||||||
|
4. **只改戎行包**,其它外部服务不动
|
||||||
|
|
||||||
|
验证点:未配 `proxy` 时行为与现在一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 1Panel 上操作清单
|
||||||
|
|
||||||
|
### 8.1 一次性准备
|
||||||
|
|
||||||
|
1. SSH 登录服务器,创建 `/opt/1panel/apps/hyapi-secrets/openvpn/`
|
||||||
|
2. 用 SFTP / `scp` 上传 5 个文件(见第 2 节)
|
||||||
|
3. 编辑服务器上的 `tiany0721.ovpn`,加上 `route-nopull` + `route 192.168.3.43 ...`
|
||||||
|
4. 设好目录/文件权限(第 3 节)
|
||||||
|
5. 确认内核允许 TUN:多数 1Panel/Linux 默认有 `/dev/net/tun`;若容器报错再装 `tun` 模块
|
||||||
|
|
||||||
|
### 8.2 接入 Compose
|
||||||
|
|
||||||
|
任选其一:
|
||||||
|
|
||||||
|
- **A. 1Panel「编排」**:打开现有 hyapi 的 Compose,粘贴 `rongxing-vpn` 服务片段,挂载 secrets 目录,重新部署
|
||||||
|
- **B. 独立编排**:单独建一个 `rongxing-vpn` 栈,但必须加入与 hyapi **同一个** Docker network(`hyapi-network` 的外部网络名以 `docker network ls` 为准)
|
||||||
|
|
||||||
|
查现有网络名:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network ls | grep hyapi
|
||||||
|
docker inspect hyapi-app-prod --format '{{json .NetworkSettings.Networks}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
若网络是 compose 项目前缀(如 `hyapi-server_hyapi-network`),独立栈里要:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
hyapi-network:
|
||||||
|
external: true
|
||||||
|
name: hyapi-server_hyapi-network # 以实际名为准
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 发布后验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1) VPN 容器是否拿到 tun / 路由
|
||||||
|
docker exec -it <rongxing-vpn容器名> ip route
|
||||||
|
# 应能看到 192.168.3.43 经 tun0
|
||||||
|
|
||||||
|
# 2) 在 VPN 容器内测内网(镜像若有 curl)
|
||||||
|
docker exec -it <rongxing-vpn容器名> wget -qO- --timeout=5 http://192.168.3.43:7007/ || true
|
||||||
|
|
||||||
|
# 3) 从业务容器经 SOCKS5 测(需业务镜像有 curl,或临时 alpine)
|
||||||
|
docker run --rm --network <hyapi网络名> curlimages/curl:8.5.0 \
|
||||||
|
-x socks5h://rongxing-vpn:1080 -m 10 -v http://192.168.3.43:7007/
|
||||||
|
|
||||||
|
# 4) 确认「不走代理」时业务仍能访问公网/库(对照现有 health)
|
||||||
|
curl -f http://127.0.0.1:25000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
业务侧再打一笔戎行真实查询,看 `logs/external_services` 下戎行日志是否成功。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 安全与运维
|
||||||
|
|
||||||
|
- `.key` / `ta.key` / 账号密码:**不进 Git**;secrets 目录不挂到可下载的 Web 根。
|
||||||
|
- SOCKS5 **仅**监听容器网络,不 `-p 1080:1080` 到宿主机。
|
||||||
|
- VPN 证书到期前找对方续期,替换 secrets 目录文件后 `docker compose restart rongxing-vpn`。
|
||||||
|
- 断线策略:容器 `restart: unless-stopped`;OpenVPN 侧已有 `resolv-retry infinite` / `persist-tun`。
|
||||||
|
- 监控:可对 `rongxing-vpn` 做 1Panel 容器存活告警;业务侧戎行错误率告警。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 本地文件 → 服务器对照表
|
||||||
|
|
||||||
|
| 本地 | 服务器推荐路径 |
|
||||||
|
|------|----------------|
|
||||||
|
| `...\OpenVPN\config\tiany0721.ovpn` | `/opt/1panel/apps/hyapi-secrets/openvpn/tiany0721.ovpn`(改过路由) |
|
||||||
|
| `...\OpenVPN\config\ca.crt` | 同目录 |
|
||||||
|
| `...\OpenVPN\config\tiany0721.crt` | 同目录 |
|
||||||
|
| `...\OpenVPN\config\tiany0721.key` | 同目录(600) |
|
||||||
|
| `...\OpenVPN\config\ta.key` | 同目录(600) |
|
||||||
|
| Windows `bin/` 整包 | **不上传** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 实施状态与顺序
|
||||||
|
|
||||||
|
**代码与 Compose 已落地**(见仓库当前变更):
|
||||||
|
|
||||||
|
- `rongxing` 支持 `proxy`(SOCKS5/HTTP)
|
||||||
|
- `docker-compose.prod.yml` 已加入 `rongxing-vpn` 服务
|
||||||
|
- `deployments/openvpn/` 自建镜像(OpenVPN + microsocks)
|
||||||
|
- 生产配置 `configs/env.production.yaml`:`proxy: "socks5://rongxing-vpn:1080"`
|
||||||
|
|
||||||
|
**1Panel 服务器仍需人工完成:**
|
||||||
|
|
||||||
|
1. 创建 `/opt/1panel/apps/hyapi-secrets/openvpn/`,上传 5 个证书/配置文件(可用本机已备好的 `secrets/openvpn/` 内容)
|
||||||
|
2. 设置环境变量:`RONGXING_VPN_CONFIG=/opt/1panel/apps/hyapi-secrets/openvpn`
|
||||||
|
3. 拉取/构建并重新部署 Compose(确保服务器能访问 Docker Hub / 镜像源以下载 `alpine`)
|
||||||
|
4. 验证:`docker exec hyapi-rongxing-vpn ip route` 含 `192.168.3.43`;业务侧打一笔戎行查询
|
||||||
@@ -846,6 +846,7 @@ type RongxingConfig struct {
|
|||||||
AppID string `mapstructure:"app_id"` // 应用 ID
|
AppID string `mapstructure:"app_id"` // 应用 ID
|
||||||
PrivateKey string `mapstructure:"private_key"` // RSA 私钥(PEM 或 PKCS#8 Base64)
|
PrivateKey string `mapstructure:"private_key"` // RSA 私钥(PEM 或 PKCS#8 Base64)
|
||||||
Timeout time.Duration `mapstructure:"timeout"`
|
Timeout time.Duration `mapstructure:"timeout"`
|
||||||
|
Proxy string `mapstructure:"proxy"` // 可选,仅戎行请求走此代理,如 socks5://rongxing-vpn:1080
|
||||||
|
|
||||||
Logging RongxingLoggingConfig `mapstructure:"logging"`
|
Logging RongxingLoggingConfig `mapstructure:"logging"`
|
||||||
}
|
}
|
||||||
|
|||||||
47
internal/infrastructure/external/rongxing/http_client_test.go
vendored
Normal file
47
internal/infrastructure/external/rongxing/http_client_test.go
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package rongxing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewHTTPClient_Direct(t *testing.T) {
|
||||||
|
client, err := newHTTPClient(5*time.Second, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if client.Timeout != 5*time.Second {
|
||||||
|
t.Fatalf("timeout = %v", client.Timeout)
|
||||||
|
}
|
||||||
|
if client.Transport != nil && client.Transport != http.DefaultTransport {
|
||||||
|
// 直连允许 Transport 为 nil(使用 DefaultTransport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHTTPClient_Socks5(t *testing.T) {
|
||||||
|
client, err := newHTTPClient(3*time.Second, "socks5://rongxing-vpn:1080")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if client.Transport == nil {
|
||||||
|
t.Fatal("expected custom transport for socks5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHTTPClient_HTTPProxy(t *testing.T) {
|
||||||
|
client, err := newHTTPClient(3*time.Second, "http://127.0.0.1:8888")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if client.Transport == nil {
|
||||||
|
t.Fatal("expected custom transport for http proxy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHTTPClient_UnsupportedScheme(t *testing.T) {
|
||||||
|
_, err := newHTTPClient(time.Second, "ftp://x")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for unsupported scheme")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,5 +46,6 @@ func NewRongxingServiceWithConfig(cfg *config.Config) (*RongxingService, error)
|
|||||||
AppID: cfg.Rongxing.AppID,
|
AppID: cfg.Rongxing.AppID,
|
||||||
PrivateKey: cfg.Rongxing.PrivateKey,
|
PrivateKey: cfg.Rongxing.PrivateKey,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
}, logger), nil
|
Proxy: cfg.Rongxing.Proxy,
|
||||||
|
}, logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,6 +17,7 @@ import (
|
|||||||
"hyapi-server/internal/shared/external_logger"
|
"hyapi-server/internal/shared/external_logger"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -32,12 +35,14 @@ type serviceConfig struct {
|
|||||||
AppID string
|
AppID string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
Proxy string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RongxingService 戎行数据源服务
|
// RongxingService 戎行数据源服务
|
||||||
type RongxingService struct {
|
type RongxingService struct {
|
||||||
config serviceConfig
|
config serviceConfig
|
||||||
logger *external_logger.ExternalServiceLogger
|
logger *external_logger.ExternalServiceLogger
|
||||||
|
client *http.Client
|
||||||
|
|
||||||
tokenMu sync.RWMutex
|
tokenMu sync.RWMutex
|
||||||
cachedToken string
|
cachedToken string
|
||||||
@@ -65,12 +70,52 @@ func (r apiResponse) code() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRongxingService 创建戎行服务实例
|
// NewRongxingService 创建戎行服务实例
|
||||||
func NewRongxingService(cfg serviceConfig, logger *external_logger.ExternalServiceLogger) *RongxingService {
|
func NewRongxingService(cfg serviceConfig, logger *external_logger.ExternalServiceLogger) (*RongxingService, error) {
|
||||||
if cfg.Timeout <= 0 {
|
if cfg.Timeout <= 0 {
|
||||||
cfg.Timeout = defaultRequestTimeout
|
cfg.Timeout = defaultRequestTimeout
|
||||||
}
|
}
|
||||||
cfg.BaseURL = strings.TrimRight(strings.TrimSpace(cfg.BaseURL), "/")
|
cfg.BaseURL = strings.TrimRight(strings.TrimSpace(cfg.BaseURL), "/")
|
||||||
return &RongxingService{config: cfg, logger: logger}
|
client, err := newHTTPClient(cfg.Timeout, cfg.Proxy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &RongxingService{config: cfg, logger: logger, client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHTTPClient 构建 HTTP 客户端;proxyURL 支持 socks5/socks5h/http/https,空则直连。
|
||||||
|
func newHTTPClient(timeout time.Duration, proxyURL string) (*http.Client, error) {
|
||||||
|
client := &http.Client{Timeout: timeout}
|
||||||
|
proxyURL = strings.TrimSpace(proxyURL)
|
||||||
|
if proxyURL == "" {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(proxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析 proxy 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(u.Scheme) {
|
||||||
|
case "socks5", "socks5h":
|
||||||
|
dialer, err := proxy.FromURL(u, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建 SOCKS 代理失败: %w", err)
|
||||||
|
}
|
||||||
|
transport := &http.Transport{}
|
||||||
|
if cd, ok := dialer.(proxy.ContextDialer); ok {
|
||||||
|
transport.DialContext = cd.DialContext
|
||||||
|
} else {
|
||||||
|
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return dialer.Dial(network, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.Transport = transport
|
||||||
|
case "http", "https":
|
||||||
|
client.Transport = &http.Transport{Proxy: http.ProxyURL(u)}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("不支持的 proxy scheme: %s(支持 socks5/socks5h/http/https)", u.Scheme)
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig 获取运行时配置
|
// GetConfig 获取运行时配置
|
||||||
@@ -309,8 +354,7 @@ func (s *RongxingService) login(ctx context.Context, transactionID string) (stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *RongxingService) doHTTP(req *http.Request) ([]byte, int, error) {
|
func (s *RongxingService) doHTTP(req *http.Request) ([]byte, int, error) {
|
||||||
client := &http.Client{Timeout: s.config.Timeout}
|
resp, err := s.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
3
secrets/openvpn/.gitkeep
Normal file
3
secrets/openvpn/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# 本地/仓库内占位:真实证书请放到此目录(勿提交 *.key / *.crt / *.ovpn)
|
||||||
|
# 1Panel 推荐改用绝对路径:/opt/1panel/apps/hyapi-secrets/openvpn
|
||||||
|
# Compose 通过环境变量 RONGXING_VPN_CONFIG 覆盖挂载源目录。
|
||||||
Reference in New Issue
Block a user