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

View File

@@ -1,12 +1,16 @@
package proxy
import (
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"strings"
"time"
"golang.org/x/net/proxy"
@@ -35,11 +39,24 @@ func (s *Server) Start() error {
if s.mode == "lowest-latency" {
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)
}
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 {
s.handleTunnel(w, r)
} 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 请求(带自动重试)
func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) {
var tried []string