feat: Add IP access control functionality

- Implement IP whitelist/blacklist access control mechanism
- Add --allow-ip and --deny-ip command-line arguments to configure IP access rules
- Support CIDR format for IP range configuration
- Enable IP access control in HTTP, HTTPS, and TCP tunnels
- Add IP access check logic to server-side proxy handling
- Update documentation to explain how to use IP access control
This commit is contained in:
Gouryella
2026-01-11 14:22:41 +08:00
parent 4b0e15dfb5
commit 85a0f44e44
14 changed files with 297 additions and 0 deletions

View File

@@ -81,6 +81,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
if tconn.HasIPAccessControl() {
clientIP := h.extractClientIP(r)
if !tconn.IsIPAllowed(clientIP) {
http.Error(w, "Access denied: your IP is not allowed", http.StatusForbidden)
return
}
}
tType := tconn.GetTunnelType()
if tType != "" && tType != protocol.TunnelTypeHTTP && tType != protocol.TunnelTypeHTTPS {
http.Error(w, "Tunnel does not accept HTTP traffic", http.StatusBadGateway)
@@ -328,6 +336,32 @@ func (h *Handler) extractSubdomain(host string) string {
return ""
}
// extractClientIP extracts the client IP from the request.
// It checks X-Forwarded-For and X-Real-IP headers first (for reverse proxy setups),
// then falls back to the remote address.
func (h *Handler) extractClientIP(r *http.Request) string {
// Check X-Forwarded-For header (may contain multiple IPs)
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
// Take the first IP (original client)
if idx := strings.Index(xff, ","); idx != -1 {
return strings.TrimSpace(xff[:idx])
}
return strings.TrimSpace(xff)
}
// Check X-Real-IP header
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return strings.TrimSpace(xri)
}
// Fall back to remote address
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
return host
}
func (h *Handler) serveHomePage(w http.ResponseWriter, r *http.Request) {
html := `<!DOCTYPE html>
<html lang="en">