!21 feat: tunnel ip port server port

Merge pull request !21 from samwaf/feat_tunnel_ip_port
This commit is contained in:
samwaf
2025-11-14 06:39:47 +00:00
committed by Gitee
3 changed files with 112 additions and 55 deletions
+20 -10
View File
@@ -3,24 +3,28 @@ package waftunnelengine
import (
"SamWaf/common/zlog"
"SamWaf/model"
"fmt"
"net"
"strings"
)
// CheckIPAccess 检查IP是否允许访问
// 参数: protocol 协议类型(TCP/UDP), clientIP 客户端IP, clientPort 客户端端口, serverPort 服务端口, tunnel 隧道配置
// 返回值: true表示允许访问,false表示拒绝访问
func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool {
func CheckIPAccess(protocol string, clientIP string, clientPort string, serverPort string, tunnel model.Tunnel) bool {
// 如果客户端IP为空,拒绝访问
if clientIP == "" {
zlog.Warn("客户端IP为空,拒绝访问")
zlog.Warn(fmt.Sprintf("客户端IP为空,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]",
protocol, clientIP, clientPort, serverPort))
return false
}
// 处理IP地址,去除端口部分
// 处理IP地址,去除端口部分(如果传入的是完整地址)
if strings.Contains(clientIP, ":") {
host, _, err := net.SplitHostPort(clientIP)
if err != nil {
zlog.Error("解析客户端IP失败: " + err.Error())
zlog.Error(fmt.Sprintf("解析客户端IP失败 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
protocol, clientIP, clientPort, serverPort, err.Error()))
return false
}
clientIP = host
@@ -40,12 +44,14 @@ func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool {
// CIDR格式
_, ipNet, err := net.ParseCIDR(ip)
if err == nil && ipNet.Contains(net.ParseIP(clientIP)) {
zlog.Info("IP " + clientIP + " 在黑名单CIDR " + ip + " 范围内,拒绝访问")
zlog.Info(fmt.Sprintf("IP在黑名单CIDR范围内,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s CIDR:%s]",
protocol, clientIP, clientPort, serverPort, ip))
return false
}
} else if ip == clientIP {
// 精确匹配
zlog.Info("IP " + clientIP + " 在黑名单中,拒绝访问")
zlog.Info(fmt.Sprintf("IP在黑名单中,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]",
protocol, clientIP, clientPort, serverPort))
return false
}
}
@@ -66,22 +72,26 @@ func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool {
// CIDR格式
_, ipNet, err := net.ParseCIDR(ip)
if err == nil && ipNet.Contains(net.ParseIP(clientIP)) {
zlog.Info("IP " + clientIP + " 在白名单CIDR " + ip + " 范围内,允许访问")
zlog.Info(fmt.Sprintf("IP在白名单CIDR范围内,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s CIDR:%s]",
protocol, clientIP, clientPort, serverPort, ip))
return true
}
} else if ip == clientIP {
// 精确匹配
zlog.Info("IP " + clientIP + " 在白名单中,允许访问")
zlog.Info(fmt.Sprintf("IP在白名单中,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]",
protocol, clientIP, clientPort, serverPort))
return true
}
}
// 如果有白名单但IP不在其中,拒绝访问
zlog.Info("IP " + clientIP + " 不在白名单中,拒绝访问")
zlog.Info(fmt.Sprintf("IP不在白名单中,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]",
protocol, clientIP, clientPort, serverPort))
return false
}
// 如果没有设置白名单,且不在黑名单中,则允许访问
zlog.Info("隧道访问 IP " + clientIP + " 通过访问控制检查,允许访问")
zlog.Info(fmt.Sprintf("隧道访问通过访问控制检查,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]",
protocol, clientIP, clientPort, serverPort))
return true
}
+54 -23
View File
@@ -4,6 +4,7 @@ import (
"SamWaf/common/zlog"
"SamWaf/model/waftunnelmodel"
"context"
"fmt"
"io"
"net"
"strconv"
@@ -16,7 +17,8 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime)
addr := ":" + strconv.Itoa(netRuntime.Port)
listener, err := net.Listen("tcp", addr)
if err != nil {
zlog.Error("TCP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Error(fmt.Sprintf("TCP服务器启动失败 [服务端口:%s 错误:%s]", serverPort, err.Error()))
return
}
@@ -27,19 +29,29 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime)
netClone.Svr = listener
waf.NetListerOnline.Set(key, netClone)
zlog.Info("启动TCP服务器,端口: " + strconv.Itoa(netRuntime.Port))
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Info(fmt.Sprintf("启动TCP服务器 [服务端口:%s]", serverPort))
for {
conn, err := listener.Accept()
if err != nil {
zlog.Error("TCP连接接收失败: " + err.Error())
zlog.Error(fmt.Sprintf("TCP连接接收失败 [服务端口:%s 错误:%s]", serverPort, err.Error()))
break
}
// 获取隧道配置
tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(netRuntime.Port))
if !ok {
zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(netRuntime.Port))
// 获取客户端信息用于日志
clientAddr := conn.RemoteAddr().String()
clientIP, clientPort, _ := net.SplitHostPort(clientAddr)
if clientIP == "" {
clientIP = clientAddr
clientPort = "unknown"
}
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
conn.Close()
continue
}
@@ -48,9 +60,16 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime)
if tunnelInfo.Tunnel.MaxInConnect > 0 {
inConnCount := waf.TCPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
if inConnCount >= tunnelInfo.Tunnel.MaxInConnect {
zlog.Warn("TCP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) +
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) +
", 端口: " + strconv.Itoa(netRuntime.Port))
// 获取客户端信息用于日志
clientAddr := conn.RemoteAddr().String()
clientIP, clientPort, _ := net.SplitHostPort(clientAddr)
if clientIP == "" {
clientIP = clientAddr
clientPort = "unknown"
}
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Warn(fmt.Sprintf("TCP入站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]",
clientIP, clientPort, serverPort, inConnCount, tunnelInfo.Tunnel.MaxInConnect))
conn.Close()
continue
}
@@ -60,25 +79,31 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime)
go waf.handleTCPConnection(conn, netRuntime.Port)
}
zlog.Info("TCP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port))
zlog.Info(fmt.Sprintf("TCP服务器关闭 [服务端口:%s]", serverPort))
}
// handleTCPConnection 处理TCP连接
func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
// 获取客户端IP
clientIP := clientConn.RemoteAddr().String()
// 获取客户端IP和端口
clientAddr := clientConn.RemoteAddr().String()
clientIP, clientPort, _ := net.SplitHostPort(clientAddr)
if clientIP == "" {
clientIP = clientAddr
clientPort = "unknown"
}
serverPort := strconv.Itoa(port)
// 获取隧道配置
tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(port))
if !ok {
zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(port))
zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]", clientIP, clientPort, serverPort))
clientConn.Close()
return
}
// 检查IP访问权限
if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) {
zlog.Warn("TCP连接被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port))
if !CheckIPAccess("TCP", clientIP, clientPort, serverPort, tunnelInfo.Tunnel) {
zlog.Warn(fmt.Sprintf("TCP连接被拒绝 [客户端IP:%s 客户端端口:%s 服务端口:%s]", clientIP, clientPort, serverPort))
clientConn.Close()
return
}
@@ -94,9 +119,8 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
if tunnelInfo.Tunnel.MaxOutConnect > 0 {
outConnCount := waf.TCPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect {
zlog.Warn("TCP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) +
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) +
", 端口: " + strconv.Itoa(port))
zlog.Warn(fmt.Sprintf("TCP出站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]",
clientIP, clientPort, serverPort, outConnCount, tunnelInfo.Tunnel.MaxOutConnect))
return
}
}
@@ -105,7 +129,8 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort)
targetConn, err := net.Dial("tcp", targetAddr)
if err != nil {
zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error())
zlog.Error(fmt.Sprintf("连接目标服务器失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]",
clientIP, clientPort, serverPort, targetAddr, err.Error()))
return
}
@@ -149,9 +174,11 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
// 使用io.Copy,当连接断开时会自动返回
_, err := io.Copy(targetConn, clientConn)
if err != nil {
zlog.Error("客户端->目标 数据转发结束: " + err.Error())
zlog.Error(fmt.Sprintf("客户端->目标 数据转发结束 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
clientIP, clientPort, serverPort, err.Error()))
} else {
zlog.Debug("客户端->目标 数据转发正常结束")
zlog.Debug(fmt.Sprintf("客户端->目标 数据转发正常结束 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
}
}()
@@ -163,9 +190,11 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
// 使用io.Copy,当连接断开时会自动返回
_, err := io.Copy(clientConn, targetConn)
if err != nil {
zlog.Error("目标->客户端 数据转发结束: " + err.Error())
zlog.Error(fmt.Sprintf("目标->客户端 数据转发结束 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
clientIP, clientPort, serverPort, err.Error()))
} else {
zlog.Debug("目标->客户端 数据转发正常结束")
zlog.Debug(fmt.Sprintf("目标->客户端 数据转发正常结束 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
}
}()
@@ -175,10 +204,12 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
// context被取消时,主动关闭两个连接
clientConn.Close()
targetConn.Close()
zlog.Debug("Context取消,主动关闭连接")
zlog.Debug(fmt.Sprintf("Context取消,主动关闭连接 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
}()
// 等待任一方向的连接断开
wg.Wait()
zlog.Info("TCP连接处理完成,端口: " + strconv.Itoa(port))
zlog.Info(fmt.Sprintf("TCP连接处理完成 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
}
+38 -22
View File
@@ -3,6 +3,7 @@ package waftunnelengine
import (
"SamWaf/common/zlog"
"SamWaf/model/waftunnelmodel"
"fmt"
"net"
"strconv"
"time"
@@ -13,13 +14,15 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime)
addr := ":" + strconv.Itoa(netRuntime.Port)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
zlog.Error("UDP地址解析失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Error(fmt.Sprintf("UDP地址解析失败 [服务端口:%s 错误:%s]", serverPort, err.Error()))
return
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
zlog.Error("UDP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Error(fmt.Sprintf("UDP服务器启动失败 [服务端口:%s 错误:%s]", serverPort, err.Error()))
return
}
@@ -33,21 +36,27 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime)
netClone.Svr = conn
waf.NetListerOnline.Set(key, netClone)
zlog.Info("启动UDP服务器,端口: " + strconv.Itoa(netRuntime.Port))
serverPort := strconv.Itoa(netRuntime.Port)
zlog.Info(fmt.Sprintf("启动UDP服务器 [服务端口:%s]", serverPort))
// 处理UDP数据
buffer := make([]byte, 1024)
for {
n, remoteAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
zlog.Error("UDP读取失败: " + err.Error())
zlog.Error(fmt.Sprintf("UDP读取失败 [服务端口:%s 错误:%s]", serverPort, err.Error()))
break
}
// 获取客户端信息用于日志
clientIP := remoteAddr.IP.String()
clientPort := strconv.Itoa(remoteAddr.Port)
// 获取隧道配置
tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(netRuntime.Port))
if !ok {
zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(netRuntime.Port))
zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
continue
}
@@ -55,9 +64,8 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime)
if tunnelInfo.Tunnel.MaxInConnect > 0 {
inConnCount := waf.UDPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
if inConnCount >= tunnelInfo.Tunnel.MaxInConnect {
zlog.Warn("UDP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) +
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) +
", 端口: " + strconv.Itoa(netRuntime.Port))
zlog.Warn(fmt.Sprintf("UDP入站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]",
clientIP, clientPort, serverPort, inConnCount, tunnelInfo.Tunnel.MaxInConnect))
continue
}
}
@@ -66,25 +74,29 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime)
go waf.handleUDPData(conn, remoteAddr, buffer[:n], netRuntime.Port)
}
zlog.Info("UDP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port))
zlog.Info(fmt.Sprintf("UDP服务器关闭 [服务端口:%s]", serverPort))
waf.UDPConnections.RemoveConn(netRuntime.Port, conn)
}
// handleUDPData 处理UDP数据
func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *net.UDPAddr, data []byte, port int) {
// 获取客户端IP
clientIP := clientAddr.String()
// 获取客户端IP和端口
clientIP := clientAddr.IP.String()
clientPort := strconv.Itoa(clientAddr.Port)
serverPort := strconv.Itoa(port)
// 获取隧道配置
tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(port))
if !ok {
zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(port))
zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
return
}
// 检查IP访问权限
if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) {
zlog.Warn("UDP数据被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port))
if !CheckIPAccess("UDP", clientIP, clientPort, serverPort, tunnelInfo.Tunnel) {
zlog.Warn(fmt.Sprintf("UDP数据被拒绝 [客户端IP:%s 客户端端口:%s 服务端口:%s]",
clientIP, clientPort, serverPort))
return
}
@@ -92,9 +104,8 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n
if tunnelInfo.Tunnel.MaxOutConnect > 0 {
outConnCount := waf.UDPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect {
zlog.Warn("UDP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) +
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) +
", 端口: " + strconv.Itoa(port))
zlog.Warn(fmt.Sprintf("UDP出站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]",
clientIP, clientPort, serverPort, outConnCount, tunnelInfo.Tunnel.MaxOutConnect))
return
}
}
@@ -103,14 +114,16 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n
targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort)
raddr, err := net.ResolveUDPAddr("udp", targetAddr)
if err != nil {
zlog.Error("解析目标地址失败: " + targetAddr + ", 错误: " + err.Error())
zlog.Error(fmt.Sprintf("解析目标地址失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]",
clientIP, clientPort, serverPort, targetAddr, err.Error()))
return
}
// 创建到目标的连接
targetConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error())
zlog.Error(fmt.Sprintf("连接目标服务器失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]",
clientIP, clientPort, serverPort, targetAddr, err.Error()))
return
}
@@ -139,7 +152,8 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n
// 发送数据到目标
_, err = targetConn.Write(data)
if err != nil {
zlog.Error("发送数据到目标失败: " + err.Error())
zlog.Error(fmt.Sprintf("发送数据到目标失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
clientIP, clientPort, serverPort, err.Error()))
return
}
@@ -147,14 +161,16 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n
buffer := make([]byte, 4096)
n, _, err := targetConn.ReadFromUDP(buffer)
if err != nil {
zlog.Error("从目标接收数据失败: " + err.Error())
zlog.Error(fmt.Sprintf("从目标接收数据失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
clientIP, clientPort, serverPort, err.Error()))
return
}
// 发送响应回客户端
_, err = serverConn.WriteToUDP(buffer[:n], clientAddr)
if err != nil {
zlog.Error("发送响应到客户端失败: " + err.Error())
zlog.Error(fmt.Sprintf("发送响应到客户端失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]",
clientIP, clientPort, serverPort, err.Error()))
return
}
}