feat: add environment variable support for proxy authentication and geo-blocking

- Introduced `.env.example` for environment variable configuration.
- Updated `docker-compose.yml` to utilize environment variables for proxy settings.
- Implemented proxy authentication with Basic Auth and geo-blocking based on country codes.
- Added `GEO_FILTER.md` for documentation on geo-filtering configuration.
- Enhanced logging to indicate authentication status and blocked countries during proxy server startup.
This commit is contained in:
isboyjc
2026-03-29 04:11:38 +08:00
parent 7a5061b101
commit a70df0d505
14 changed files with 1088 additions and 120 deletions

35
.env.example Normal file
View File

@@ -0,0 +1,35 @@
# Docker 容器配置
CONTAINER_NAME=goproxy
# 端口绑定配置
# PROXY_HOST: 代理服务绑定地址
# - 127.0.0.1 = 仅本地访问(推荐,最安全)
# - 0.0.0.0 = 对外开放(如启用需配合认证或防火墙)
PROXY_HOST=127.0.0.1
STABLE_PORT=7776 # 最低延迟代理端口
RANDOM_PORT=7777 # 随机轮换代理端口
# 地理过滤配置
# 屏蔽指定国家代码的出口代理(逗号分隔,如 CN,RU,KP
# 留空 = 不屏蔽任何国家
# 默认屏蔽中国大陆CN香港HK、澳门MO、台湾TW不受影响
BLOCKED_COUNTRIES=CN
# 代理服务认证配置
# ⚠️ 当 PROXY_HOST=0.0.0.0(对外开放)时,强烈建议启用认证!
# 使用方式curl -x http://username:password@host:port https://example.com
PROXY_AUTH_ENABLED=false # 是否启用代理认证true/false
PROXY_AUTH_USERNAME=proxy # 代理认证用户名
PROXY_AUTH_PASSWORD= # 代理认证密码(留空=不启用认证)
# 示例PROXY_AUTH_PASSWORD=my_secure_password_123
# WebUI 配置
WEBUI_HOST=0.0.0.0 # WebUI 可对外访问(有登录认证保护)
WEBUI_PORT=7778
WEBUI_PASSWORD=goproxy # ⚠️ 生产环境请修改为强密码
# 数据存储
DATA_DIR=./data
# 时区配置
TZ=Asia/Shanghai

127
.github/DOCKER_SETUP.md vendored Normal file
View File

@@ -0,0 +1,127 @@
# GitHub Actions Docker 镜像自动构建配置
本项目使用 GitHub Actions 自动构建并推送 Docker 镜像到多个镜像仓库。
## 📦 自动构建触发条件
- **Push to main 分支**:自动构建 `latest` 标签
- **推送版本标签**(如 `v1.0.0`):自动构建版本标签(`1.0.0`, `1.0`, `1`, `latest`
- **手动触发**:在 GitHub Actions 页面手动运行
## 🔧 配置 GitHub Secrets
### 1. Docker Hub 配置(可选)
如果要推送到 Docker Hub需要配置以下 Secrets
1. 登录 [Docker Hub](https://hub.docker.com/)
2.**Account Settings > Security** 创建 Access Token
3. 在 GitHub 仓库的 **Settings > Secrets and variables > Actions** 添加:
- `DOCKERHUB_USERNAME`: 你的 Docker Hub 用户名
- `DOCKERHUB_TOKEN`: 刚才创建的 Access Token
### 2. GitHub Container Registry默认已配置
GitHub Container Registry (ghcr.io) 无需额外配置workflow 使用自动提供的 `GITHUB_TOKEN`
**镜像地址**`ghcr.io/isboyjc/goproxy`
## 🏗️ 构建特性
-**多架构支持**`linux/amd64``linux/arm64`
-**多标签发布**`latest`、版本号(如 `1.0.0``1.0``1`
-**双仓库推送**Docker Hub + GitHub Container Registry
-**构建缓存**:使用 GitHub Actions 缓存加速构建
## 📋 使用镜像
### 从 Docker Hub 拉取
```bash
docker pull isboyjc/goproxy:latest
docker run -d \
--name proxygo \
-p 127.0.0.1:7776:7776 \
-p 127.0.0.1:7777:7777 \
-p 7778:7778 \
-e WEBUI_PASSWORD=your_password \
-v "$(pwd)/data:/app/data" \
isboyjc/goproxy:latest
```
### 从 GitHub Container Registry 拉取
```bash
docker pull ghcr.io/isboyjc/goproxy:latest
docker run -d \
--name proxygo \
-p 127.0.0.1:7776:7776 \
-p 127.0.0.1:7777:7777 \
-p 7778:7778 \
-e WEBUI_PASSWORD=your_password \
-v "$(pwd)/data:/app/data" \
ghcr.io/isboyjc/goproxy:latest
```
### 使用特定版本
```bash
# 使用特定版本号
docker pull ghcr.io/isboyjc/goproxy:1.0.0
# 使用主版本号(自动获取最新的 1.x.x
docker pull ghcr.io/isboyjc/goproxy:1
```
## 🚀 发布新版本
创建并推送版本标签即可触发自动构建:
```bash
# 创建版本标签
git tag -a v1.0.0 -m "Release version 1.0.0"
# 推送标签到 GitHub
git push origin v1.0.0
```
GitHub Actions 会自动:
1. 构建多架构镜像
2. 推送到 Docker Hub 和 GHCR
3. 创建多个标签(`1.0.0`, `1.0`, `1`, `latest`
## 🌐 端口绑定说明
默认示例使用 `127.0.0.1:7776``127.0.0.1:7777`(仅本地访问):
- **场景**:在部署机器上本地使用代理
- **安全**:最安全,代理服务不对外暴露
- **限制**:其他机器无法使用该代理
**如需对外开放代理服务**(局域网/VPS 多机共享),修改端口绑定:
```bash
docker run -d \
--name proxygo \
-p 7776:7776 \ # ⚠️ 改为对外开放(无认证)
-p 7777:7777 \ # ⚠️ 改为对外开放(无认证)
-p 7778:7778 \
-e WEBUI_PASSWORD=strong_password \
-v "$(pwd)/data:/app/data" \
ghcr.io/isboyjc/goproxy:latest
```
**⚠️ 安全警告**
- 代理服务本身**无认证机制**,对外开放后任何人都可使用
- 建议配合**防火墙白名单**或**云安全组**限制访问 IP
- WebUI 有登录认证,可以安全对外开放
更多部署场景说明,请查看 [README - 部署场景配置](../README.md#部署场景配置)
---
## 📝 注意事项
- **权限配置**:首次推送到 GHCR 可能需要在仓库 **Settings > Actions > General** 中设置 **Workflow permissions**`Read and write permissions`
- **GHCR 可见性**:默认镜像为私有,可在 **Packages** 页面修改为公开
- **Docker Hub 可选**:如果不需要推送到 Docker Hub可在 workflow 文件中删除相关配置,或不配置 DOCKERHUB_* secretsworkflow 会跳过 Docker Hub 登录失败的步骤)

72
.github/workflows/docker-image.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Docker Image CI
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
env:
REGISTRY_DOCKERHUB: docker.io
REGISTRY_GHCR: ghcr.io
IMAGE_NAME: isboyjc/goproxy
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_DOCKERHUB }}/${{ env.IMAGE_NAME }}
${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_DOCKERHUB }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_GHCR }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

3
.gitignore vendored
View File

@@ -25,6 +25,9 @@ config.json
data/ data/
!data/.gitkeep !data/.gitkeep
# Environment variables
.env
# Temporary files # Temporary files
*.tmp *.tmp
*.bak *.bak

View File

@@ -20,6 +20,6 @@ ENV TZ=Asia/Shanghai
WORKDIR /app WORKDIR /app
COPY --from=builder /app/proxy-pool . COPY --from=builder /app/proxy-pool .
EXPOSE 7777 7778 EXPOSE 7776 7777 7778
CMD ["./proxy-pool"] CMD ["./proxy-pool"]

242
GEO_FILTER.md Normal file
View File

@@ -0,0 +1,242 @@
# 地理过滤配置指南
GoProxy 支持通过国家代码过滤代理的出口位置,让你可以灵活控制代理池的地理分布。
## 🌍 配置方式
### 环境变量配置
通过 `BLOCKED_COUNTRIES` 环境变量设置需要屏蔽的国家代码:
```bash
# 默认屏蔽中国大陆CN
BLOCKED_COUNTRIES=CN
# 屏蔽多个国家(逗号分隔)
BLOCKED_COUNTRIES=CN,RU,KP,IR
# 不屏蔽任何国家(留空)
BLOCKED_COUNTRIES=
```
### Docker Compose 配置
编辑 `.env` 文件:
```bash
# 屏蔽中国大陆和俄罗斯
BLOCKED_COUNTRIES=CN,RU
```
启动服务:
```bash
docker compose up -d
```
### Docker Run 配置
```bash
docker run -d --name proxygo \
-p 127.0.0.1:7776:7776 -p 127.0.0.1:7777:7777 -p 7778:7778 \
-e BLOCKED_COUNTRIES=CN,RU \
-e WEBUI_PASSWORD=your_password \
-v "$(pwd)/data:/app/data" \
ghcr.io/isboyjc/goproxy:latest
```
### 本地运行配置
```bash
export BLOCKED_COUNTRIES=CN,RU,KP
go run .
```
## 🗺️ 工作机制
### 双重过滤
地理过滤在两个阶段生效:
**1. 启动清理阶段**
- 程序启动时自动扫描数据库
- 删除所有屏蔽国家出口的代理
- 日志输出:`🧹 已清理 X 个屏蔽国家出口代理 (屏蔽: [CN RU])`
**2. 验证阶段**
- 新抓取的代理在验证时检查出口位置
- 如果出口国家在屏蔽列表中,直接拒绝入池
- 不会占用池子容量
### 国家代码识别
系统使用 **ISO 3166-1 alpha-2** 标准的两位国家代码:
```
出口位置格式CC City
示例:
CN Beijing → 国家代码 CN中国大陆
HK Hong Kong → 国家代码 HK香港
US New York → 国家代码 US美国
RU Moscow → 国家代码 RU俄罗斯
```
匹配规则:`exit_location LIKE 'CC %'`(国家代码 + 空格 + 城市)
## 📋 常用国家代码
### 亚洲
| 代码 | 国家/地区 | 代码 | 国家/地区 |
|------|----------|------|----------|
| `CN` | 中国大陆 | `HK` | 香港 |
| `TW` | 台湾 | `MO` | 澳门 |
| `JP` | 日本 | `KR` | 韩国 |
| `SG` | 新加坡 | `IN` | 印度 |
| `TH` | 泰国 | `VN` | 越南 |
| `KP` | 朝鲜 | `IR` | 伊朗 |
### 欧洲
| 代码 | 国家 | 代码 | 国家 |
|------|------|------|------|
| `RU` | 俄罗斯 | `GB` | 英国 |
| `DE` | 德国 | `FR` | 法国 |
| `NL` | 荷兰 | `SE` | 瑞典 |
| `UA` | 乌克兰 | `PL` | 波兰 |
### 美洲
| 代码 | 国家 | 代码 | 国家 |
|------|------|------|------|
| `US` | 美国 | `CA` | 加拿大 |
| `BR` | 巴西 | `MX` | 墨西哥 |
| `AR` | 阿根廷 | `CL` | 智利 |
完整国家代码列表:[ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
## 🎯 使用场景
### 场景 1屏蔽中国大陆默认
```bash
BLOCKED_COUNTRIES=CN
```
**适用**
- 需要海外 IP 代理
- 避免被识别为中国大陆流量
- 保留香港、澳门、台湾代理
### 场景 2屏蔽多个敏感地区
```bash
BLOCKED_COUNTRIES=CN,RU,KP,IR,SY
```
**适用**
- 合规要求(避免某些国家的 IP
- 地缘政治考虑
- 防止特定地区的代理质量问题
### 场景 3仅使用欧美代理
```bash
# 屏蔽亚洲、非洲、中东等地区(示例,需根据实际需求调整)
BLOCKED_COUNTRIES=CN,IN,TH,VN,ID,PH,BD,PK,IR,IQ,SA,EG,NG,ZA
```
### 场景 4不做地理限制
```bash
BLOCKED_COUNTRIES=
```
**适用**
- 需要最大化代理池容量
- 对地理位置无特殊要求
- 测试和开发环境
## 📊 实时查看
### 查看当前屏蔽配置
启动日志会显示:
```
[main] 🧹 已清理 15 个屏蔽国家出口代理 (屏蔽: [CN RU KP])
```
### 查看池中国家分布
通过 WebUI 的**出口国家筛选器**可以看到当前池中所有国家的代理分布。
### 数据库查询
```bash
# 查看所有代理的国家分布
sqlite3 data/proxy.db "
SELECT SUBSTR(exit_location, 1, 2) AS country, COUNT(*) AS count
FROM proxies
GROUP BY country
ORDER BY count DESC;
"
# 查看特定国家的代理
sqlite3 data/proxy.db "
SELECT address, exit_ip, exit_location, latency
FROM proxies
WHERE exit_location LIKE 'US %';
"
```
## ⚠️ 注意事项
1. **大小写不敏感**:国家代码会自动转为大写(`cn``CN`
2. **空格自动处理**:前后空格会自动去除
3. **重启生效**:修改配置后需要重启服务
4. **已有代理清理**:启动时会清理数据库中的屏蔽国家代理
5. **香港独立识别**
- 中国大陆代码:`CN`
- 香港代码:`HK`(独立的国家代码)
- 设置 `BLOCKED_COUNTRIES=CN` 不会影响香港代理
## 🧪 测试验证
### 测试 1屏蔽中国大陆
```bash
# 启动服务
export BLOCKED_COUNTRIES=CN
go run .
# 查看日志(应该显示清理信息)
# [main] 🧹 已清理 X 个屏蔽国家出口代理 (屏蔽: [CN])
# 查看 WebUI 的代理列表(不应该有 CN 开头的出口位置)
```
### 测试 2屏蔽多个国家
```bash
export BLOCKED_COUNTRIES=CN,RU,KP
go run .
# 使用测试脚本验证
./test/test_proxy.sh
# 观察输出的国旗 emoji不应该有 🇨🇳 🇷🇺 🇰🇵)
```
### 测试 3不屏蔽任何国家
```bash
export BLOCKED_COUNTRIES=
go run .
# 查看日志(不应该有清理信息)
# 代理列表中可能出现各种国家的代理
```
## 💡 最佳实践
1. **默认配置**:保持默认 `BLOCKED_COUNTRIES=CN`,适合大多数场景
2. **生产环境**:根据业务合规要求设置屏蔽国家
3. **测试环境**:可以设置为空(`BLOCKED_COUNTRIES=`)以获取更多代理
4. **定期调整**:根据代理质量和可用性调整屏蔽列表
5. **配合筛选**:利用 WebUI 的国家筛选器查看各国代理分布,辅助决策

385
README.md
View File

@@ -2,10 +2,39 @@
> **智能代理池系统** — 基于 Go 的轻量级、低资源消耗、自适应的代理池服务 > **智能代理池系统** — 基于 Go 的轻量级、低资源消耗、自适应的代理池服务
[![Docker Hub](https://img.shields.io/docker/v/isboyjc/goproxy?label=Docker%20Hub&logo=docker)](https://hub.docker.com/r/isboyjc/goproxy)
[![GitHub Container Registry](https://img.shields.io/badge/GHCR-latest-blue?logo=github)](https://github.com/isboyjc/GoProxy/pkgs/container/goproxy)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Go Version](https://img.shields.io/badge/Go-1.25-00ADD8?logo=go)](https://go.dev/)
GoProxy 从多个公开代理源自动抓取 HTTP/SOCKS5 代理,通过严格验证(出口 IP + 位置 + 延迟)后加入智能代理池,对外提供统一的 HTTP 代理服务。系统采用质量分级、智能补充、自动优化等机制,确保代理池始终保持高质量和稳定性。 GoProxy 从多个公开代理源自动抓取 HTTP/SOCKS5 代理,通过严格验证(出口 IP + 位置 + 延迟)后加入智能代理池,对外提供统一的 HTTP 代理服务。系统采用质量分级、智能补充、自动优化等机制,确保代理池始终保持高质量和稳定性。
**GitHub**[github.com/isboyjc/GoProxy](https://github.com/isboyjc/GoProxy)
![](https://cdn.isboyjc.com/img/Xnip2026-03-29_03-35-06.png) ![](https://cdn.isboyjc.com/img/Xnip2026-03-29_03-35-06.png)
## 📑 目录
- [核心特性](#-核心特性)
- [项目结构](#-项目结构)
- [快速开始](#-快速开始)
- [Docker 部署](#-docker-部署) - 推荐使用 docker-compose
- [配置说明](#-配置说明) - 环境变量、池子参数、认证、地理过滤
- [WebUI 使用指南](#-webui-使用指南) - 双角色权限、操作功能
- [核心架构](#-核心架构) - 智能池子、状态转换、质量分级
- [数据库 Schema](#-数据库-schema)
- [代理源](#-代理源)
- [测试代理服务](#-测试代理服务)
- [常见问题](#-常见问题)
- [致谢与声明](#-致谢与声明)
**扩展文档**
- [Docker 镜像发布配置](.github/DOCKER_SETUP.md) - GitHub Actions CI/CD 设置
- [地理过滤配置指南](GEO_FILTER.md) - 国家代码、使用场景、测试方法
- [认证功能测试](test/AUTH_TEST.md) - 代理认证测试和多语言客户端示例
- [基础功能测试](test/TEST_GUIDE.md) - 测试脚本使用指南
- [架构设计文档](POOL_DESIGN.md) - 完整的系统设计和实现细节
## ✨ 核心特性 ## ✨ 核心特性
### 🎯 智能池子机制 ### 🎯 智能池子机制
@@ -39,6 +68,7 @@ GoProxy 从多个公开代理源自动抓取 HTTP/SOCKS5 代理,通过严格
- **7776 端口(最低延迟)**:固定使用延迟最低的代理,除非失败才切换,适合长连接和流媒体 - **7776 端口(最低延迟)**:固定使用延迟最低的代理,除非失败才切换,适合长连接和流媒体
- **自动切换**两个端口都支持失败自动重试7776 失败后切换到次优代理 - **自动切换**两个端口都支持失败自动重试7776 失败后切换到次优代理
- **共享池子**:两个端口使用同一个代理池,统一管理和优化 - **共享池子**:两个端口使用同一个代理池,统一管理和优化
- **可选认证**:支持 Basic Auth 认证保护,对外开放时可启用
### 🎨 黑客风格 WebUI ### 🎨 黑客风格 WebUI
- **Matrix 美学**:荧光绿 + 纯黑背景CRT 扫描线效果JetBrains Mono 等宽字体 - **Matrix 美学**:荧光绿 + 纯黑背景CRT 扫描线效果JetBrains Mono 等宽字体
@@ -58,26 +88,35 @@ GoProxy 从多个公开代理源自动抓取 HTTP/SOCKS5 代理,通过严格
```text ```text
. .
├── main.go # 程序入口,协调所有模块 ├── main.go # 程序入口,协调所有模块
├── config/ # 配置系统(池子容量、延迟标准、验证参数等) ├── config/ # 配置系统(池子容量、延迟标准、验证参数等)
├── pool/ # 🆕 池子管理器(入池判断、替换逻辑、状态计算) ├── pool/ # 🆕 池子管理器(入池判断、替换逻辑、状态计算)
├── fetcher/ # 🆕 智能抓取器(源分组、断路器、按需抓取) ├── fetcher/ # 🆕 智能抓取器(源分组、断路器、按需抓取)
│ ├── fetcher.go # 多模式抓取逻辑 │ ├── fetcher.go # 多模式抓取逻辑
│ ├── source_manager.go # 源状态管理和断路器 │ ├── source_manager.go # 源状态管理和断路器
│ └── ip_query.go # IP查询限流和多源降级 │ └── ip_query.go # IP查询限流和多源降级
├── validator/ # 代理验证(连接测试 + 出口IP检测 ├── validator/ # 代理验证(连接测试 + 出口IP检测 + 地理过滤
├── checker/ # 🆕 分批健康检查器 ├── checker/ # 🆕 分批健康检查器
├── optimizer/ # 🆕 优化轮换器(定时优化池子质量) ├── optimizer/ # 🆕 优化轮换器(定时优化池子质量)
├── storage/ # 🆕 扩展存储层(质量等级、使用统计、源状态表) ├── storage/ # 🆕 扩展存储层(质量等级、使用统计、源状态表)
├── proxy/ # 对外 HTTP 代理服务 ├── proxy/ # 🆕 对外 HTTP 代理服务(双端口 + 可选认证)
├── webui/ # 🆕 黑客风格 WebUI健康仪表盘、配置界面 ├── webui/ # 🆕 黑客风格 WebUI健康仪表盘、配置界面、RBAC
├── logger/ # 内存日志收集 ├── logger/ # 内存日志收集
├── test/ # 🧪 测试脚本Bash/Go/Python ├── test/ # 🧪 测试脚本与文档
│ ├── test_proxy.sh # Bash 测试脚本 │ ├── test_proxy.sh # Bash 测试脚本
│ ├── test_proxy.go # Go 测试脚本 │ ├── test_proxy.go # Go 测试脚本
│ ├── test_proxy.py # Python 测试脚本 │ ├── test_proxy.py # Python 测试脚本
── TEST_GUIDE.md # 测试指南 ── TEST_GUIDE.md # 基础功能测试指南
└── POOL_DESIGN.md # 🆕 完整架构设计文档 │ └── AUTH_TEST.md # 🆕 认证功能测试指南
├── .github/ # 🆕 GitHub Actions CI/CD
│ ├── workflows/docker-image.yml # 自动构建多架构镜像
│ └── DOCKER_SETUP.md # Docker 镜像发布配置指南
├── .env.example # 🆕 环境变量配置模板
├── docker-compose.yml # 🆕 Docker Compose 配置(自动更新镜像)
├── Dockerfile # Docker 构建文件
├── GEO_FILTER.md # 🆕 地理过滤配置指南
├── POOL_DESIGN.md # 完整架构设计文档
└── README.md # 本文件
``` ```
## 🚀 快速开始 ## 🚀 快速开始
@@ -100,15 +139,16 @@ go build -o proxygo .
``` ```
程序启动后会: 程序启动后会:
1. 加载配置(优先 `config.json` 1. 加载配置(环境变量 + `config.json`
2. 初始化数据库和限流器 2. 初始化数据库和限流器
3. 启动 WebUI`:7778` 3. 清理不符合条件的代理(屏蔽国家出口、无地理信息
4. 立即执行智能填充(按需抓取 + 严格验证 4. 启动 WebUI`:7778`
5. 启动后台协程: 5. 立即执行智能填充(按需抓取 + 严格验证)
- 状态监控(每分钟) 6. 启动后台协程:
- 状态监控(每 30 秒)
- 健康检查(默认 5 分钟) - 健康检查(默认 5 分钟)
- 优化轮换(默认 30 分钟) - 优化轮换(默认 30 分钟)
6. 启动两个代理服务: 7. 启动两个代理服务(支持可选认证)
- `:7776` - 最低延迟模式(稳定连接) - `:7776` - 最低延迟模式(稳定连接)
- `:7777` - 随机轮换模式IP 多样性) - `:7777` - 随机轮换模式IP 多样性)
@@ -151,6 +191,7 @@ curl -x http://127.0.0.1:7776 https://httpbin.org/ip
#### 环境变量配置 #### 环境变量配置
**本地使用(无认证)**
```bash ```bash
# 使用随机模式 # 使用随机模式
export http_proxy=http://127.0.0.1:7777 export http_proxy=http://127.0.0.1:7777
@@ -161,6 +202,13 @@ export http_proxy=http://127.0.0.1:7776
export https_proxy=http://127.0.0.1:7776 export https_proxy=http://127.0.0.1:7776
``` ```
**远程使用(带认证)**
```bash
# 格式http://username:password@host:port
export http_proxy=http://proxy:your_password@192.168.1.100:7777
export https_proxy=http://proxy:your_password@192.168.1.100:7777
```
#### 端口对比 #### 端口对比
| 特性 | 7777随机轮换 | 7776最低延迟 | | 特性 | 7777随机轮换 | 7776最低延迟 |
@@ -190,50 +238,130 @@ export https_proxy=http://127.0.0.1:7776
## 🐳 Docker 部署 ## 🐳 Docker 部署
### 使用 Dockerfile > 💡 **自动构建**GitHub Actions 自动构建多架构镜像amd64/arm64支持 Docker Hub 和 GHCR 双仓库推送。
> 详细配置说明请查看 [`.github/DOCKER_SETUP.md`](.github/DOCKER_SETUP.md)
### 快速启动(推荐)
使用 docker-compose支持环境变量配置自动拉取最新镜像
```bash ```bash
docker build -t proxygo . # 1. 复制环境变量模板
docker run -d \ cp .env.example .env
--name proxygo \
-p 127.0.0.1:7776:7776 \ # 2. 根据需要编辑 .env可选默认配置即可使用
-p 127.0.0.1:7777:7777 \ # vim .env
-p 7778:7778 \
-e TZ=Asia/Shanghai \ # 3. 启动服务(自动拉取最新镜像)
-e WEBUI_PASSWORD=your_password \ docker compose up -d
-e DATA_DIR=/app/data \
-v "$(pwd)/data:/app/data" \ # 4. 访问 WebUI
proxygo # http://localhost:7778默认密码goproxy
``` ```
### 使用 docker-compose ### 使用 Docker Hub 镜像
```bash ```bash
docker compose up -d --build docker pull isboyjc/goproxy:latest
docker run -d --name proxygo \
-p 127.0.0.1:7776:7776 -p 127.0.0.1:7777:7777 -p 7778:7778 \
-e WEBUI_PASSWORD=your_password -v "$(pwd)/data:/app/data" \
isboyjc/goproxy:latest
``` ```
### WebUI 公网访问配置 ### 使用 GitHub Container Registry 镜像
得益于**双角色权限系统**WebUI 可以安全地对外开放:
```bash ```bash
docker run -d \ docker pull ghcr.io/isboyjc/goproxy:latest
--name proxygo \ docker run -d --name proxygo \
-p 127.0.0.1:7776:7776 \ -p 127.0.0.1:7776:7776 -p 127.0.0.1:7777:7777 -p 7778:7778 \
-p 127.0.0.1:7777:7777 \ -e WEBUI_PASSWORD=your_password -v "$(pwd)/data:/app/data" \
-p 0.0.0.0:7778:7778 \ # 公网访问 WebUI ghcr.io/isboyjc/goproxy:latest
-e TZ=Asia/Shanghai \
-e WEBUI_PASSWORD=strong_password \
-e DATA_DIR=/app/data \
-v "$(pwd)/data:/app/data" \
proxygo
``` ```
**安全说明** ### 环境变量配置
- 访客(未登录)只能查看数据,无法执行任何操作
- 所有写操作(抓取、删除、配置修改)都需要管理员密码 **核心配置**`.env` 文件):
- 建议设置强密码(通过 `WEBUI_PASSWORD` 环境变量)
- 代理服务端口7776、7777建议仅绑定内网`127.0.0.1` | 变量 | 默认值 | 说明 |
|------|--------|------|
| `PROXY_HOST` | `127.0.0.1` | 代理服务绑定地址(`0.0.0.0` = 对外开放) |
| `BLOCKED_COUNTRIES` | `CN` | 屏蔽的国家代码(逗号分隔,如 `CN,RU`,留空=不屏蔽) |
| `PROXY_AUTH_ENABLED` | `false` | 是否启用代理认证 |
| `PROXY_AUTH_USERNAME` | `proxy` | 代理认证用户名 |
| `PROXY_AUTH_PASSWORD` | 空 | 代理认证密码 |
| `WEBUI_PASSWORD` | `goproxy` | WebUI 登录密码 |
完整环境变量列表请查看 `.env.example` 文件。
**常用配置示例**
```bash
# 场景 1本地使用默认最安全
# 直接运行 docker compose up -d 即可
# 场景 2对外开放 + 启用认证(推荐)
cat > .env << EOF
PROXY_HOST=0.0.0.0
PROXY_AUTH_ENABLED=true
PROXY_AUTH_USERNAME=myuser
PROXY_AUTH_PASSWORD=secure_pass_123
WEBUI_PASSWORD=admin_pass_456
BLOCKED_COUNTRIES=CN
EOF
docker compose up -d
# 场景 3屏蔽多个国家
cat > .env << EOF
BLOCKED_COUNTRIES=CN,RU,KP,IR
WEBUI_PASSWORD=admin_pass
EOF
docker compose up -d
# 场景 4不屏蔽任何国家
cat > .env << EOF
BLOCKED_COUNTRIES=
WEBUI_PASSWORD=admin_pass
EOF
docker compose up -d
```
### 安全部署配置
**默认配置**:代理服务仅本地访问(`127.0.0.1`WebUI 可对外访问。
**如需对外开放代理服务**,强烈建议启用认证:
| 使用场景 | 配置建议 | 安全级别 |
|---------|---------|---------|
| **个人 VPS + 本地使用** | `PROXY_HOST=127.0.0.1`(默认) | ⭐⭐⭐⭐⭐ 最安全 |
| **团队共享 + 认证** | `PROXY_HOST=0.0.0.0` + 启用认证 | ⭐⭐⭐⭐ 安全 |
| **内网 + 防火墙** | `PROXY_HOST=0.0.0.0` + iptables 白名单 | ⭐⭐⭐ 中等 |
**启用代理认证**(编辑 `.env`
```bash
PROXY_HOST=0.0.0.0
PROXY_AUTH_ENABLED=true
PROXY_AUTH_USERNAME=myuser
PROXY_AUTH_PASSWORD=secure_pass_123
```
**客户端使用(带认证)**
```bash
# 环境变量方式
export http_proxy=http://myuser:secure_pass_123@server-ip:7777
export https_proxy=http://myuser:secure_pass_123@server-ip:7777
# curl 直接指定
curl -x http://myuser:secure_pass_123@server-ip:7777 https://httpbin.org/ip
# Python requests
proxies = {'http': 'http://myuser:secure_pass_123@server-ip:7777', 'https': '...'}
```
> 💡 **认证测试指南**:详细的认证功能测试和多语言客户端示例,请查看 [`test/AUTH_TEST.md`](test/AUTH_TEST.md)
## ⚙️ 配置说明 ## ⚙️ 配置说明
@@ -268,6 +396,16 @@ docker run -d \
| `stable_proxy_port` | `:7776` | 最低延迟代理端口 | | `stable_proxy_port` | `:7776` | 最低延迟代理端口 |
| `webui_port` | `:7778` | WebUI 端口 | | `webui_port` | `:7778` | WebUI 端口 |
**代理认证配置**
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `proxy_auth_enabled` | `false` | 是否启用代理认证(对外开放时建议启用) |
| `proxy_auth_username` | `proxy` | 代理认证用户名 |
| `proxy_auth_password_hash` | 空 | 代理认证密码 SHA256 哈希(通过环境变量 `PROXY_AUTH_PASSWORD` 设置原始密码) |
> 💡 **注意**:代理认证配置通过**环境变量**设置,不在 `config.json` 中。启动时从 `PROXY_AUTH_ENABLED`、`PROXY_AUTH_USERNAME`、`PROXY_AUTH_PASSWORD` 环境变量读取。
**池子容量配置** **池子容量配置**
| 参数 | 默认值 | 说明 | 推荐范围 | | 参数 | 默认值 | 说明 | 推荐范围 |
@@ -349,17 +487,28 @@ docker run -d \
### 固定配置 ### 固定配置
以下配置在代码中固定,无需调整: 以下配置在代码中固定或通过环境变量设置,无需在 `config.json`调整:
| 配置项 | 值 | 说明 | | 配置项 | 值 | 说明 |
| --- | --- | --- | | --- | --- | --- |
| `WebUIPort` | `:7778` | Web 管理后台端口 | | `WebUIPort` | `:7778` | WebUI 端口 |
| `ProxyPort` | `:7777` | 对外统一代理端口 | | `ProxyPort` | `:7777` | 随机轮换代理端口 |
| `StableProxyPort` | `:7776` | 最低延迟代理端口 |
| `ValidateURL` | `http://www.gstatic.com/generate_204` | 验证目标地址 | | `ValidateURL` | `http://www.gstatic.com/generate_204` | 验证目标地址 |
| `IPQueryRateLimit` | `10` | IP 查询限流(次/秒) | | `IPQueryRateLimit` | `10 次/秒` | IP 查询限流 |
| `SourceFailThreshold` | `3` | 源降级阈值 | | `SourceFailThreshold` | `3` | 源降级阈值(连续失败) |
| `SourceDisableThreshold` | `5` | 源禁用阈值 | | `SourceDisableThreshold` | `5` | 源禁用阈值(连续失败) |
| `SourceCooldownMinutes` | `30` | 源禁用冷却时间(分钟) | | `SourceCooldownMinutes` | `30` | 源禁用冷却时间 |
| `MaxRetry` | `3` | 代理请求失败重试次数 |
**环境变量配置**(启动时读取):
| 环境变量 | 默认值 | 说明 |
| --- | --- | --- |
| `PROXY_AUTH_ENABLED` | `false` | 是否启用代理认证 |
| `PROXY_AUTH_USERNAME` | `proxy` | 代理认证用户名 |
| `PROXY_AUTH_PASSWORD` | 空 | 代理认证密码(原始密码,自动哈希) |
| `BLOCKED_COUNTRIES` | `CN` | 屏蔽的国家代码(逗号分隔,如 `CN,RU,KP`,留空=不屏蔽) |
## 🎨 WebUI 使用指南 ## 🎨 WebUI 使用指南
@@ -415,23 +564,29 @@ GoProxy WebUI 支持**访客模式**和**管理员模式**
### 代理注册表 ### 代理注册表
**表格字段** **表格字段**
- **Grade**质量等级S/A/B/C - **Grade**质量等级S/A/B/C,基于延迟计算
- **Protocol**协议类型HTTP/SOCKS5 - **Protocol**协议类型HTTP/SOCKS5
- **Address**代理地址host:port - **Address**代理地址host:port,点击可复制
- **Exit IP**:出口 IP - **Exit IP**代理的出口 IP 地址
- **Location**:出口地理位置(国旗 + 国家代码 + 城市) - **Location**:出口地理位置(国旗 emoji + 国家代码 + 城市)
- **Latency**:延迟(毫秒,颜色编码) - **Latency**连接延迟(毫秒,动态颜色编码:绿/黄/橙/红
- **Usage**:使用次数 / 成功次数 - **Usage**:使用统计(使用总次数 / 成功次数,成功率指标)
- **Action**删除按钮 - **Action**操作按钮(刷新单个代理、删除代理,管理员可见)
**操作功能** **操作功能**
- **筛选**All / HTTP / SOCKS5所有用户可用
- **点击复制地址**:点击代理地址单元格复制到剪贴板(所有用户可用) **所有用户可用**
- **Fetch Proxies**:手动触发智能抓取(⚡ 管理员专属 - **协议筛选**:下拉选择协议类型(全部/HTTP/SOCKS5
- **Refresh Latency**:重新验证所有代理并更新延迟(⚡ 管理员专属 - **国家筛选**:下拉选择出口国家(全部/动态国家列表,带国旗 emoji
- **刷新单个代理**:点击行内刷新按钮验证单个代理(⚡ 管理员专属) - **点击复制地址**:点击代理地址单元格直接复制到剪贴板
- **删除代理**:点击行内删除按钮移除指定代理(⚡ 管理员专属) - **查看数据**:池子状态、质量分布、系统日志
- **Configure Pool**:打开配置界面(⚡ 管理员专属)
**管理员专属**(需登录):
- **Fetch Proxies**:手动触发智能抓取
- **Refresh Latency**:重新验证所有代理并更新延迟
- **刷新单个代理**:点击行内刷新按钮验证单个代理
- **删除代理**:点击行内删除按钮移除指定代理
- **Configure Pool**:打开配置界面修改池子参数
### 配置界面(⚡ 管理员专属) ### 配置界面(⚡ 管理员专属)
@@ -488,9 +643,9 @@ GoProxy WebUI 支持**访客模式**和**管理员模式**
### 状态转换机制 ### 状态转换机制
```text ```text
Healthy (总数≥80% 且 各协议≥80%槽位) Healthy (总数≥95% 且 各协议≥80%槽位)
↓ 代理失效 ↓ 代理失效
Warning (总数<80% 或 任一协议<80%) Warning (总数<95% 或 任一协议<80%)
↓ 继续失效 ↓ 继续失效
Critical (总数<50% 或 任一协议<20%槽位) Critical (总数<50% 或 任一协议<20%槽位)
↓ 继续失效 ↓ 继续失效
@@ -499,12 +654,14 @@ Emergency (总数<10% 或 单协议缺失)
└─ 自动触发紧急抓取 ─┘ └─ 自动触发紧急抓取 ─┘
``` ```
> 💡 **自动补充阈值**:当总数低于 95% 时进入 Warning 状态并触发自动补充,确保池子始终接近满容量运行。
### 抓取模式选择 ### 抓取模式选择
| 池子状态 | 抓取模式 | 使用源 | 触发条件 | | 池子状态 | 抓取模式 | 使用源 | 触发条件 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Emergency | 紧急模式 | 所有可用源 | 单协议缺失或总数<10% | | Emergency | 紧急模式 | 所有可用源 | 单协议缺失或总数<10% |
| Critical/Warning | 补充模式 | 快更新源 | 总数<80%或协议不均 | | Critical/Warning | 补充模式 | 快更新源 | 总数<95%或协议不均 |
| Healthy | 优化模式 | 慢更新源随机2-3个 | 定时触发30分钟 | | Healthy | 优化模式 | 慢更新源随机2-3个 | 定时触发30分钟 |
### 质量分级标准 ### 质量分级标准
@@ -648,8 +805,8 @@ A:
### Q: 池子状态如何计算? ### Q: 池子状态如何计算?
A: A:
- **Healthy**:总数 ≥80% 且各协议 ≥80% 槽位 - **Healthy**:总数 ≥95% 且各协议 ≥80% 槽位
- **Warning**:总数 <80% 或任一协议 <80% 槽位 - **Warning**:总数 <95% 或任一协议 <80% 槽位
- **Critical**:总数 <50% 或任一协议 <20% 槽位 - **Critical**:总数 <50% 或任一协议 <20% 槽位
- **Emergency**:总数 <10% 或单协议缺失 - **Emergency**:总数 <10% 或单协议缺失
@@ -665,6 +822,31 @@ A:
- IP 查询有限流10 次/秒) - IP 查询有限流10 次/秒)
- 部分代理可能不支持 IP 查询 - 部分代理可能不支持 IP 查询
- 系统会在后续健康检查中补全信息 - 系统会在后续健康检查中补全信息
- 没有出口信息的代理会在启动时被自动清理
### Q: 如何配置地理过滤?
A:
通过 `BLOCKED_COUNTRIES` 环境变量配置需要屏蔽的国家:
```bash
# 默认屏蔽中国大陆CN
BLOCKED_COUNTRIES=CN
# 屏蔽多个国家(逗号分隔)
BLOCKED_COUNTRIES=CN,RU,KP
# 不屏蔽任何国家(留空)
BLOCKED_COUNTRIES=
```
**工作机制**
- **验证阶段**:检测到屏蔽国家出口直接拒绝入池
- **启动清理**:自动删除数据库中屏蔽国家的代理
- **精确匹配**:使用 ISO 3166-1 alpha-2 国家代码CN、HK、US 等)
**常用国家代码**`CN`=中国大陆 | `HK`=香港 | `RU`=俄罗斯 | `US`=美国 | `JP`=日本 | `SG`=新加坡
> 📖 **详细配置指南**:更多国家代码、使用场景、测试方法,请查看 [`GEO_FILTER.md`](./GEO_FILTER.md)
### Q: 资源消耗如何? ### Q: 资源消耗如何?
A: A:
@@ -675,11 +857,24 @@ A:
- 按需抓取,避免无效流量 - 按需抓取,避免无效流量
- 健康检查批次小20 个) - 健康检查批次小20 个)
## 📚 详细设计文档 ### Q: 代理服务如何启用认证?
A:
1. 编辑 `.env` 文件:
```bash
PROXY_AUTH_ENABLED=true
PROXY_AUTH_USERNAME=myuser
PROXY_AUTH_PASSWORD=mypass
```
2. 重启服务:`docker compose up -d`
3. 客户端使用:`http://myuser:mypass@server-ip:7777`
完整的架构设计、模块说明、配置策略、资源优化方案,请查看: 详细说明和测试方法请查看 [`test/AUTH_TEST.md`](./test/AUTH_TEST.md)
👉 [POOL_DESIGN.md](./POOL_DESIGN.md) ### Q: 代理认证和 WebUI 认证有什么区别?
A:
- **代理认证**:保护 7776/7777 代理服务端口,防止代理被滥用
- **WebUI 认证**:保护 7778 管理后台,区分访客和管理员权限
- 两者独立配置,互不影响
## 🛠️ 开发与调试 ## 🛠️ 开发与调试
@@ -759,7 +954,18 @@ proxy from 🇯🇵 198.51.100.12: seq=5 time=890ms
50 requests transmitted, 47 received, 3 failed, 6.0% packet loss 50 requests transmitted, 47 received, 3 failed, 6.0% packet loss
``` ```
详细测试指南请查看:👉 [test/TEST_GUIDE.md](./test/TEST_GUIDE.md) **测试认证功能**
```bash
# 启用认证后测试
curl -x http://myuser:mypass@127.0.0.1:7777 https://httpbin.org/ip
# 无认证请求(应该返回 407 错误)
curl -x http://127.0.0.1:7777 https://httpbin.org/ip
```
**更多测试指南**
- 基础功能测试:[`test/TEST_GUIDE.md`](./test/TEST_GUIDE.md)
- 认证功能测试:[`test/AUTH_TEST.md`](./test/AUTH_TEST.md)
## 🙏 致谢与声明 ## 🙏 致谢与声明
@@ -778,10 +984,13 @@ proxy from 🇯🇵 198.51.100.12: seq=5 time=890ms
- 🆕 **分层健康管理**:批次检查、智能跳过 S 级、定时优化轮换 - 🆕 **分层健康管理**:批次检查、智能跳过 S 级、定时优化轮换
- 🆕 **智能重试机制**:自动故障切换、失败即删除、防重复尝试 - 🆕 **智能重试机制**:自动故障切换、失败即删除、防重复尝试
- 🆕 **双端口服务**7777 随机轮换IP 多样性)+ 7776 最低延迟(稳定连接) - 🆕 **双端口服务**7777 随机轮换IP 多样性)+ 7776 最低延迟(稳定连接)
- 🆕 **代理认证保护**:可选 Basic Auth 认证,对外开放时保护代理服务不被滥用
- 🆕 **黑客风格 WebUI**Matrix 美学、实时仪表盘、完整配置界面、中英文切换 - 🆕 **黑客风格 WebUI**Matrix 美学、实时仪表盘、完整配置界面、中英文切换
- 🆕 **双角色权限**:访客模式(只读)+ 管理员模式(完全控制),可安全公网开放 - 🆕 **双角色权限**:访客模式(只读)+ 管理员模式(完全控制),可安全公网开放
- 🆕 **扩展存储层**:质量等级、使用统计、源状态管理 - 🆕 **扩展存储层**:质量等级、使用统计、源状态管理
- 🆕 **测试套件**Bash/Go/Python 三种测试脚本,持续运行模式,显示国旗 emoji - 🆕 **测试套件**Bash/Go/Python 三种测试脚本,持续运行模式,显示国旗 emoji
- 🆕 **CI/CD 自动化**GitHub Actions 自动构建多架构镜像amd64/arm64双仓库发布
- 🆕 **环境变量配置**docker-compose + .env 文件,灵活配置各种部署场景
感谢原作者提供的基础实现,让我们能够在此之上构建更强大的代理池系统。 感谢原作者提供的基础实现,让我们能够在此之上构建更强大的代理池系统。

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"strings"
"sync" "sync"
) )
@@ -33,6 +34,14 @@ type Config struct {
// 稳定代理端口(最低延迟模式) // 稳定代理端口(最低延迟模式)
StableProxyPort string StableProxyPort string
// 代理服务认证配置
ProxyAuthEnabled bool // 是否启用代理认证(默认 false
ProxyAuthUsername string // 代理认证用户名(默认 "proxy"
ProxyAuthPasswordHash string // 代理认证密码 SHA256 哈希
// 地理过滤配置
BlockedCountries []string // 屏蔽的国家代码列表(如 ["CN", "RU"],默认 ["CN"]
// SQLite 数据库路径 // SQLite 数据库路径
DBPath string DBPath string
@@ -97,6 +106,33 @@ func DefaultConfig() *Config {
if password == "" { if password == "" {
password = DefaultPassword password = DefaultPassword
} }
// 读取代理认证配置
proxyAuthEnabled := os.Getenv("PROXY_AUTH_ENABLED") == "true"
proxyAuthUsername := os.Getenv("PROXY_AUTH_USERNAME")
if proxyAuthUsername == "" {
proxyAuthUsername = "proxy"
}
proxyAuthPassword := os.Getenv("PROXY_AUTH_PASSWORD")
proxyAuthHash := ""
if proxyAuthPassword != "" {
proxyAuthHash = passwordHash(proxyAuthPassword)
}
// 读取地理过滤配置
blockedCountries := []string{"CN"} // 默认屏蔽中国大陆
if blockedEnv := os.Getenv("BLOCKED_COUNTRIES"); blockedEnv != "" {
// 支持逗号分隔的国家代码,如 "CN,RU,KP"
countries := strings.Split(blockedEnv, ",")
blockedCountries = make([]string, 0, len(countries))
for _, c := range countries {
c = strings.TrimSpace(strings.ToUpper(c))
if c != "" {
blockedCountries = append(blockedCountries, c)
}
}
}
return &Config{ return &Config{
// 基础服务配置 // 基础服务配置
WebUIPort: ":7778", WebUIPort: ":7778",
@@ -104,6 +140,14 @@ func DefaultConfig() *Config {
ProxyPort: ":7777", ProxyPort: ":7777",
StableProxyPort: ":7776", StableProxyPort: ":7776",
DBPath: dataDir() + "proxy.db", DBPath: dataDir() + "proxy.db",
// 代理认证配置
ProxyAuthEnabled: proxyAuthEnabled,
ProxyAuthUsername: proxyAuthUsername,
ProxyAuthPasswordHash: proxyAuthHash,
// 地理过滤配置
BlockedCountries: blockedCountries,
// 池子容量配置 // 池子容量配置
PoolMaxSize: 100, // 总容量 PoolMaxSize: 100, // 总容量

View File

@@ -1,26 +1,32 @@
services: services:
proxygo: proxygo:
build: . # 使用预构建镜像(每次部署自动拉取最新版本)
container_name: proxygo image: ghcr.io/isboyjc/goproxy:latest
pull_policy: always
# 或使用本地构建(取消下面注释,注释上面的 image 和 pull_policy 行)
# build: .
container_name: ${CONTAINER_NAME:-proxygo}
restart: unless-stopped restart: unless-stopped
ports: ports:
- "127.0.0.1:7776:7776" # 稳定代理端口(最低延迟 # 代理端口:默认仅本地访问(最安全
- "127.0.0.1:7777:7777" # 随机代理端口(轮换模式 # 如需对外开放,在 .env 中设置 PROXY_HOST=0.0.0.0(⚠️ 无认证,需配合防火墙
- "7778:7778" # WebUI 端口(外网可访问 - "${PROXY_HOST:-127.0.0.1}:${STABLE_PORT:-7776}:7776" # 稳定代理(最低延迟
- "${PROXY_HOST:-127.0.0.1}:${RANDOM_PORT:-7777}:7777" # 随机轮换
- "${WEBUI_HOST:-0.0.0.0}:${WEBUI_PORT:-7778}:7778" # WebUI有登录认证
volumes: volumes:
- ./data:/app/data - ${DATA_DIR:-./data}:/app/data
environment: environment:
- TZ=Asia/Shanghai - TZ=${TZ:-Asia/Shanghai}
- DATA_DIR=/app/data - DATA_DIR=/app/data
# - WEBUI_PASSWORD=your-password # 自定义 WebUI 登录密码,默认: proxygo - WEBUI_PASSWORD=${WEBUI_PASSWORD:-goproxy}
- PROXY_AUTH_ENABLED=${PROXY_AUTH_ENABLED:-false}
- PROXY_AUTH_USERNAME=${PROXY_AUTH_USERNAME:-proxy}
- PROXY_AUTH_PASSWORD=${PROXY_AUTH_PASSWORD}
- BLOCKED_COUNTRIES=${BLOCKED_COUNTRIES:-CN}
healthcheck: healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:7778/"] test: ["CMD", "wget", "-qO-", "http://localhost:7778/"]
interval: 30s interval: 30s
timeout: 5s timeout: 5s
retries: 3 retries: 3
networks:
- cursor2api_default
networks:
cursor2api_default:
external: true

View File

@@ -59,9 +59,11 @@ func main() {
// 清理无效代理 // 清理无效代理
totalDeleted := 0 totalDeleted := 0
if deleted, err := store.DeleteChinaMainland(); err == nil && deleted > 0 { if len(cfg.BlockedCountries) > 0 {
log.Printf("[main] 🧹 已清理 %d 个中国大陆出口代理", deleted) if deleted, err := store.DeleteBlockedCountries(cfg.BlockedCountries); err == nil && deleted > 0 {
totalDeleted += int(deleted) log.Printf("[main] 🧹 已清理 %d 个屏蔽国家出口代理 (屏蔽: %v)", deleted, cfg.BlockedCountries)
totalDeleted += int(deleted)
}
} }
if deleted, err := store.DeleteWithoutExitInfo(); err == nil && deleted > 0 { if deleted, err := store.DeleteWithoutExitInfo(); err == nil && deleted > 0 {
log.Printf("[main] 🧹 已清理 %d 个无出口信息的代理", deleted) log.Printf("[main] 🧹 已清理 %d 个无出口信息的代理", deleted)

View File

@@ -1,12 +1,16 @@
package proxy package proxy
import ( import (
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt" "fmt"
"io" "io"
"log" "log"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
@@ -35,11 +39,24 @@ func (s *Server) Start() error {
if s.mode == "lowest-latency" { if s.mode == "lowest-latency" {
modeDesc = "最低延迟" modeDesc = "最低延迟"
} }
log.Printf("proxy server listening on %s [%s]", s.port, modeDesc) authStatus := "无认证"
if s.cfg.ProxyAuthEnabled {
authStatus = fmt.Sprintf("需认证 (用户: %s)", s.cfg.ProxyAuthUsername)
}
log.Printf("proxy server listening on %s [%s] [%s]", s.port, modeDesc, authStatus)
return http.ListenAndServe(s.port, s) return http.ListenAndServe(s.port, s)
} }
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 认证检查(如果启用)
if s.cfg.ProxyAuthEnabled {
if !s.checkAuth(r) {
w.Header().Set("Proxy-Authenticate", `Basic realm="GoProxy"`)
http.Error(w, "Proxy Authentication Required", http.StatusProxyAuthRequired)
return
}
}
if r.Method == http.MethodConnect { if r.Method == http.MethodConnect {
s.handleTunnel(w, r) s.handleTunnel(w, r)
} else { } else {
@@ -47,6 +64,40 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
// checkAuth 验证代理 Basic Auth
func (s *Server) checkAuth(r *http.Request) bool {
auth := r.Header.Get("Proxy-Authorization")
if auth == "" {
return false
}
// 解析 Basic Auth
const prefix = "Basic "
if !strings.HasPrefix(auth, prefix) {
return false
}
decoded, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
if err != nil {
return false
}
credentials := strings.SplitN(string(decoded), ":", 2)
if len(credentials) != 2 {
return false
}
username := credentials[0]
password := credentials[1]
// 验证用户名和密码
usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(s.cfg.ProxyAuthUsername)) == 1
passwordHash := fmt.Sprintf("%x", sha256.Sum256([]byte(password)))
passwordMatch := subtle.ConstantTimeCompare([]byte(passwordHash), []byte(s.cfg.ProxyAuthPasswordHash)) == 1
return usernameMatch && passwordMatch
}
// handleHTTP 处理普通 HTTP 请求(带自动重试) // handleHTTP 处理普通 HTTP 请求(带自动重试)
func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) {
var tried []string var tried []string

View File

@@ -556,15 +556,24 @@ func (s *Storage) DeleteInvalid(maxFailCount int) (int64, error) {
return res.RowsAffected() return res.RowsAffected()
} }
// DeleteChinaMainland 删除中国大陆出口的代理(保留香港) // DeleteBlockedCountries 删除指定国家代码出口的代理
func (s *Storage) DeleteChinaMainland() (int64, error) { func (s *Storage) DeleteBlockedCountries(countryCodes []string) (int64, error) {
// 删除 exit_location 以 "CN " 开头的代理CN后面有空格表示是城市 if len(countryCodes) == 0 {
// 香港是 "HK Hong Kong" 或 "HK" 开头,不会被删除 return 0, nil
res, err := s.db.Exec(`DELETE FROM proxies WHERE exit_location LIKE 'CN %'`)
if err != nil {
return 0, err
} }
return res.RowsAffected()
var totalDeleted int64
for _, code := range countryCodes {
// exit_location 格式:如 "CN Beijing" 或 "HK Hong Kong"
// 使用 LIKE 'CODE %' 来匹配国家代码(后面有空格表示有城市信息)
res, err := s.db.Exec(`DELETE FROM proxies WHERE exit_location LIKE ?`, code+" %")
if err != nil {
return totalDeleted, err
}
affected, _ := res.RowsAffected()
totalDeleted += affected
}
return totalDeleted, nil
} }
// DeleteWithoutExitInfo 删除没有出口信息的代理 // DeleteWithoutExitInfo 删除没有出口信息的代理

165
test/AUTH_TEST.md Normal file
View File

@@ -0,0 +1,165 @@
# 代理认证功能测试指南
本文档说明如何测试 GoProxy 的代理认证功能。
## 🔒 启用认证
### 使用 docker-compose
编辑 `.env` 文件:
```bash
PROXY_HOST=0.0.0.0
PROXY_AUTH_ENABLED=true
PROXY_AUTH_USERNAME=testuser
PROXY_AUTH_PASSWORD=testpass123
```
启动服务:
```bash
docker compose up -d
```
### 直接运行
设置环境变量后启动:
```bash
export PROXY_AUTH_ENABLED=true
export PROXY_AUTH_USERNAME=testuser
export PROXY_AUTH_PASSWORD=testpass123
go run .
```
## 🧪 测试认证功能
### 测试 1无认证请求应该失败
```bash
curl -x http://127.0.0.1:7777 https://httpbin.org/ip -v
```
**预期结果**
- HTTP 状态码:`407 Proxy Authentication Required`
- 响应头:`Proxy-Authenticate: Basic realm="GoProxy"`
### 测试 2错误的用户名/密码(应该失败)
```bash
curl -x http://wronguser:wrongpass@127.0.0.1:7777 https://httpbin.org/ip -v
```
**预期结果**
- HTTP 状态码:`407 Proxy Authentication Required`
### 测试 3正确的认证应该成功
```bash
curl -x http://testuser:testpass123@127.0.0.1:7777 https://httpbin.org/ip
```
**预期结果**
- HTTP 状态码:`200 OK`
- 返回代理的出口 IP 信息
### 测试 4环境变量方式应该成功
```bash
export http_proxy=http://testuser:testpass123@127.0.0.1:7777
export https_proxy=http://testuser:testpass123@127.0.0.1:7777
curl https://httpbin.org/ip
```
### 测试 5HTTPS CONNECT 隧道(应该成功)
```bash
curl -x http://testuser:testpass123@127.0.0.1:7776 https://www.google.com -I
```
## 🧩 多语言客户端示例
### Python (requests)
```python
import requests
proxies = {
'http': 'http://testuser:testpass123@127.0.0.1:7777',
'https': 'http://testuser:testpass123@127.0.0.1:7777',
}
response = requests.get('https://httpbin.org/ip', proxies=proxies)
print(response.text)
```
### Go
```go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://testuser:testpass123@127.0.0.1:7777")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
resp, err := client.Get("https://httpbin.org/ip")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
### Node.js
```javascript
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
const proxyUrl = 'http://testuser:testpass123@127.0.0.1:7777';
const agent = new HttpsProxyAgent(proxyUrl);
axios.get('https://httpbin.org/ip', {
httpAgent: agent,
httpsAgent: agent
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
```
## 🔍 日志验证
启动时会输出认证状态:
```
proxy server listening on :7777 [随机轮换] [需认证 (用户: testuser)]
proxy server listening on :7776 [最低延迟] [需认证 (用户: testuser)]
```
无认证模式:
```
proxy server listening on :7777 [随机轮换] [无认证]
proxy server listening on :7776 [最低延迟] [无认证]
```
## 📝 注意事项
- 认证采用 **HTTP Basic Auth** 标准,兼容绝大多数客户端
- 密码存储为 **SHA256 哈希**,安全性高
- 认证失败不会删除代理或影响池子状态
- 两个端口7776、7777共享相同的认证配置
- 认证仅保护代理服务WebUI 有独立的登录系统

View File

@@ -20,6 +20,7 @@ type Validator struct {
timeout time.Duration timeout time.Duration
validateURL string validateURL string
maxResponseMs int maxResponseMs int
cfg *config.Config
} }
func concurrencyBuffer(total, concurrency int) int { func concurrencyBuffer(total, concurrency int) int {
@@ -40,6 +41,7 @@ func New(concurrency, timeoutSec int, validateURL string) *Validator {
timeout: time.Duration(timeoutSec) * time.Second, timeout: time.Duration(timeoutSec) * time.Second,
validateURL: validateURL, validateURL: validateURL,
maxResponseMs: maxMs, maxResponseMs: maxMs,
cfg: cfg,
} }
} }
@@ -160,12 +162,13 @@ func (v *Validator) ValidateOne(p storage.Proxy) (bool, time.Duration, string, s
return false, latency, exitIP, exitLocation return false, latency, exitIP, exitLocation
} }
// 过滤中国大陆出口香港的countryCode是HK不是CN // 过滤屏蔽国家出口(根据配置
if len(exitLocation) >= 2 { if v.cfg != nil && len(v.cfg.BlockedCountries) > 0 && len(exitLocation) >= 2 {
countryCode := exitLocation[:2] countryCode := exitLocation[:2]
if countryCode == "CN" { for _, blocked := range v.cfg.BlockedCountries {
// 中国大陆出口,直接拒绝 if countryCode == blocked {
return false, latency, exitIP, exitLocation return false, latency, exitIP, exitLocation
}
} }
} }