Files
GoTunnel/plan.md
Flik 4d2a2a7117
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 58s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 48s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m23s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 56s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 58s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 52s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m19s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 54s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 2m3s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m1s
update
2025-12-29 23:08:15 +08:00

9.9 KiB
Raw Blame History

GoTunnel 架构修复计划

面向 100 万用户发布前的安全与稳定性修复方案

问题概览

严重程度 数量 状态
P0 严重 5 已修复
P1 高 5 已修复
P2 中 13 计划中
P3 低 15 后续迭代

修复完成总结

P0 严重问题 (已全部修复)

编号 问题 修复文件 状态
1.1 TLS 证书验证 pkg/crypto/tls.go TOFU 机制
1.2 Web 控制台无认证 cmd/server/main.go, config/config.go 强制认证
1.3 认证检查端点失效 router/auth.go 实际验证 JWT
1.4 Token 生成错误 config/config.go 错误检查
1.5 客户端 ID 未验证 tunnel/server.go 正则验证

P1 高优先级问题 (已全部修复)

编号 问题 修复文件 状态
2.1 无连接数限制 tunnel/server.go 10000 上限
2.3 无优雅关闭 tunnel/server.go, cmd/server/main.go 信号处理
2.4 消息大小未验证 protocol/message.go 已有验证
2.5 无安全事件日志 pkg/security/audit.go 新增模块

第一阶段P0 严重问题 (发布前必须修复)

1.1 TLS 证书验证被禁用

文件: pkg/crypto/tls.go

问题: InsecureSkipVerify: true 导致中间人攻击风险

修复方案:

  • 添加服务端证书指纹验证机制
  • 客户端首次连接时保存服务端证书指纹
  • 后续连接验证指纹是否匹配Trust On First Use
  • 提供 --skip-verify 参数供测试环境使用

修改内容:

// pkg/crypto/tls.go
func ClientTLSConfig(serverFingerprint string) *tls.Config {
    return &tls.Config{
        MinVersion:         tls.VersionTLS12,
        InsecureSkipVerify: true, // 仍需要,因为是自签名证书
        VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
            // 验证证书指纹
            return verifyCertFingerprint(rawCerts, serverFingerprint)
        },
    }
}

1.2 Web 控制台无认证

文件: cmd/server/main.go

问题: 默认配置下 Web 控制台完全开放

修复方案:

  • 首次启动时自动生成随机密码
  • 强制要求配置用户名密码
  • 无认证时拒绝启动 Web 服务

修改内容:

// cmd/server/main.go
if cfg.Web.Enabled {
    if cfg.Web.Username == "" || cfg.Web.Password == "" {
        // 自动生成凭据
        cfg.Web.Username = "admin"
        cfg.Web.Password = generateSecurePassword(16)
        log.Printf("[Web] 自动生成凭据 - 用户名: %s, 密码: %s",
            cfg.Web.Username, cfg.Web.Password)
        // 保存到配置文件
        saveConfig(cfg)
    }
}

1.3 认证检查端点失效

文件: internal/server/router/auth.go

问题: /auth/check 始终返回 valid: true

修复方案:

  • 实际验证 JWT Token
  • 返回真实的验证结果

修改内容:

// internal/server/router/auth.go
func (h *AuthHandler) handleCheck(w http.ResponseWriter, r *http.Request) {
    // 从 Authorization header 获取 token
    token := extractToken(r)
    if token == "" {
        jsonError(w, "missing token", http.StatusUnauthorized)
        return
    }

    // 验证 token
    claims, err := h.validateToken(token)
    if err != nil {
        jsonError(w, "invalid token", http.StatusUnauthorized)
        return
    }

    json.NewEncoder(w).Encode(map[string]interface{}{
        "valid": true,
        "user":  claims.Username,
    })
}

1.4 Token 生成错误未处理

文件: internal/server/config/config.go

问题: rand.Read() 错误被忽略,可能生成弱 Token

修复方案:

  • 检查 rand.Read() 返回值
  • 失败时 panic 或返回错误
  • 增加 Token 强度验证

修改内容:

// internal/server/config/config.go
func generateToken(length int) (string, error) {
    bytes := make([]byte, length/2)
    n, err := rand.Read(bytes)
    if err != nil {
        return "", fmt.Errorf("failed to generate token: %w", err)
    }
    if n != len(bytes) {
        return "", fmt.Errorf("insufficient random bytes: got %d, want %d", n, len(bytes))
    }
    return hex.EncodeToString(bytes), nil
}

1.5 客户端 ID 未验证

文件: internal/server/tunnel/server.go

问题: tunnel server 中未使用已有的 ID 验证函数

修复方案:

  • 在 handleConnection 中验证 clientID
  • 拒绝非法格式的 ID
  • 记录安全日志

修改内容:

// internal/server/tunnel/server.go
func (s *Server) handleConnection(conn net.Conn) {
    // ... 读取认证消息后

    clientID := authReq.ClientID
    if clientID != "" && !isValidClientID(clientID) {
        log.Printf("[Security] Invalid client ID format from %s: %s",
            conn.RemoteAddr(), clientID)
        sendAuthResponse(conn, false, "invalid client id format")
        return
    }
    // ...
}

var clientIDRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,64}$`)

func isValidClientID(id string) bool {
    return clientIDRegex.MatchString(id)
}

第二阶段P1 高优先级问题 (发布前建议修复)

2.1 无连接数限制

文件: internal/server/tunnel/server.go

修复方案:

  • 添加全局最大连接数限制
  • 添加单客户端连接数限制
  • 使用 semaphore 控制并发

修改内容:

type Server struct {
    // ...
    maxConns     int
    connSem      chan struct{} // semaphore
    clientConns  map[string]int
}

func (s *Server) handleConnection(conn net.Conn) {
    select {
    case s.connSem <- struct{}{}:
        defer func() { <-s.connSem }()
    default:
        conn.Close()
        log.Printf("[Server] Connection rejected: max connections reached")
        return
    }
    // ...
}

2.2 Goroutine 泄漏

文件: 多个文件

修复方案:

  • 使用 context 控制 goroutine 生命周期
  • 添加 goroutine 池
  • 确保所有 goroutine 有退出机制

2.3 无优雅关闭

文件: cmd/server/main.go

修复方案:

  • 监听 SIGTERM/SIGINT 信号
  • 关闭所有监听器
  • 等待现有连接完成
  • 设置关闭超时

修改内容:

// cmd/server/main.go
func main() {
    // ...

    // 优雅关闭
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        <-quit
        log.Println("[Server] Shutting down...")

        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()

        server.Shutdown(ctx)
        webServer.Shutdown(ctx)
    }()

    server.Run()
}

2.4 消息大小未验证

文件: pkg/protocol/message.go

修复方案:

  • 在 ReadMessage 中检查消息长度
  • 超过限制时返回错误

2.5 无读写超时

文件: internal/server/tunnel/server.go

修复方案:

  • 所有连接设置读写超时
  • 使用 SetDeadline 而非一次性设置

2.6 竞态条件

文件: internal/server/tunnel/server.go

修复方案:

  • 使用 sync.Map 替代 map + mutex
  • 或确保所有 map 访问都在锁保护下

2.7 无安全事件日志

修复方案:

  • 添加安全日志模块
  • 记录认证失败、异常访问等事件
  • 支持日志轮转

第三阶段P2 中优先级问题 (发布后迭代)

编号 问题 文件
3.1 配置文件权限过宽 (0644) config/config.go
3.2 心跳机制不完善 tunnel/server.go
3.3 HTTP 代理无 SSRF 防护 proxy/http.go
3.4 SOCKS5 代理无验证 proxy/socks5.go
3.5 数据库操作无超时 db/sqlite.go
3.6 错误处理不一致 多个文件
3.7 UDP 缓冲区无限制 tunnel/server.go
3.8 代理规则无验证 tunnel/server.go
3.9 客户端注册竞态 tunnel/server.go
3.10 Relay 资源泄漏 relay/relay.go
3.11 插件配置无验证 tunnel/server.go
3.12 端口号无边界检查 tunnel/server.go
3.13 插件商店 URL 硬编码 config/config.go

第四阶段P3 低优先级问题 (后续优化)

编号 问题 建议
4.1 无结构化日志 引入 zap/zerolog
4.2 无连接池 实现连接池
4.3 线性查找规则 使用 map 索引
4.4 无数据库缓存 添加内存缓存
4.5 魔法数字 提取为常量
4.6 无 godoc 注释 补充文档
4.7 配置无验证 添加验证逻辑

修复顺序

Week 1: P0 问题 (5个)
  ├── Day 1-2: 1.1 TLS 证书验证
  ├── Day 2-3: 1.2 Web 控制台认证
  ├── Day 3-4: 1.3 认证检查端点
  ├── Day 4: 1.4 Token 生成
  └── Day 5: 1.5 客户端 ID 验证

Week 2: P1 问题 (7个)
  ├── Day 1-2: 2.1 连接数限制
  ├── Day 2-3: 2.2 Goroutine 泄漏
  ├── Day 3-4: 2.3 优雅关闭
  ├── Day 4: 2.4 消息大小验证
  ├── Day 5: 2.5 读写超时
  └── Day 5: 2.6-2.7 竞态条件 + 安全日志

Week 3+: P2/P3 问题
  └── 按优先级逐步修复

测试计划

安全测试

  • TLS 中间人攻击测试
  • 认证绕过测试
  • 注入攻击测试
  • DoS 攻击测试

稳定性测试

  • 长时间运行测试 (72h+)
  • 高并发连接测试 (10000+)
  • 内存泄漏测试
  • Goroutine 泄漏测试

性能测试

  • 吞吐量基准测试
  • 延迟基准测试
  • 资源使用监控

回滚方案

如发布后发现严重问题:

  1. 立即回滚: 保留上一版本二进制文件
  2. 热修复: 针对特定问题发布补丁
  3. 降级运行: 禁用问题功能模块

监控告警

发布后需要监控的指标:

  • 连接数 / 活跃客户端数
  • 内存使用 / Goroutine 数量
  • 认证失败率
  • 错误日志频率
  • 响应延迟 P99

文档版本: 1.0 创建时间: 2025-12-29 状态: 待审核