mirror of
https://github.com/fosrl/gerbil.git
synced 2026-02-08 05:56:40 +00:00
Merge branch 'main' into dev
This commit is contained in:
4
.github/workflows/cicd.yml
vendored
4
.github/workflows/cicd.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.25
|
go-version: 1.25
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
|
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -3,8 +3,9 @@ module github.com/fosrl/gerbil
|
|||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.42.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,10 +15,9 @@ require (
|
|||||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.43.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.36.0 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
12
go.sum
12
go.sum
@@ -16,16 +16,16 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo=
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
|
|||||||
140
relay/relay.go
140
relay/relay.go
@@ -64,6 +64,17 @@ type WireGuardSession struct {
|
|||||||
LastSeen time.Time
|
LastSeen time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type for tracking bidirectional communication patterns to rebuild sessions
|
||||||
|
type CommunicationPattern struct {
|
||||||
|
FromClient *net.UDPAddr // The client address
|
||||||
|
ToDestination *net.UDPAddr // The destination address
|
||||||
|
ClientIndex uint32 // The receiver index seen from client
|
||||||
|
DestIndex uint32 // The receiver index seen from destination
|
||||||
|
LastFromClient time.Time // Last packet from client to destination
|
||||||
|
LastFromDest time.Time // Last packet from destination to client
|
||||||
|
PacketCount int // Number of packets observed
|
||||||
|
}
|
||||||
|
|
||||||
type InitialMappings struct {
|
type InitialMappings struct {
|
||||||
Mappings map[string]ProxyMapping `json:"mappings"` // key is "ip:port"
|
Mappings map[string]ProxyMapping `json:"mappings"` // key is "ip:port"
|
||||||
}
|
}
|
||||||
@@ -105,6 +116,9 @@ type UDPProxyServer struct {
|
|||||||
// Session tracking for WireGuard peers
|
// Session tracking for WireGuard peers
|
||||||
// Key format: "senderIndex:receiverIndex"
|
// Key format: "senderIndex:receiverIndex"
|
||||||
wgSessions sync.Map
|
wgSessions sync.Map
|
||||||
|
// Communication pattern tracking for rebuilding sessions
|
||||||
|
// Key format: "clientIP:clientPort-destIP:destPort"
|
||||||
|
commPatterns sync.Map
|
||||||
// ReachableAt is the URL where this server can be reached
|
// ReachableAt is the URL where this server can be reached
|
||||||
ReachableAt string
|
ReachableAt string
|
||||||
}
|
}
|
||||||
@@ -156,6 +170,9 @@ func (s *UDPProxyServer) Start() error {
|
|||||||
// Start the proxy mapping cleanup routine
|
// Start the proxy mapping cleanup routine
|
||||||
go s.cleanupIdleProxyMappings()
|
go s.cleanupIdleProxyMappings()
|
||||||
|
|
||||||
|
// Start the communication pattern cleanup routine
|
||||||
|
go s.cleanupIdleCommunicationPatterns()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,6 +462,9 @@ func (s *UDPProxyServer) handleWireGuardPacket(packet []byte, remoteAddr *net.UD
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track communication pattern for session rebuilding
|
||||||
|
s.trackCommunicationPattern(remoteAddr, destAddr, receiverIndex, true)
|
||||||
|
|
||||||
_, err = conn.Write(packet)
|
_, err = conn.Write(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("Failed to forward transport data: %v", err)
|
logger.Debug("Failed to forward transport data: %v", err)
|
||||||
@@ -465,6 +485,9 @@ func (s *UDPProxyServer) handleWireGuardPacket(packet []byte, remoteAddr *net.UD
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track communication pattern for session rebuilding
|
||||||
|
s.trackCommunicationPattern(remoteAddr, destAddr, receiverIndex, true)
|
||||||
|
|
||||||
_, err = conn.Write(packet)
|
_, err = conn.Write(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("Failed to forward transport data: %v", err)
|
logger.Debug("Failed to forward transport data: %v", err)
|
||||||
@@ -548,6 +571,9 @@ func (s *UDPProxyServer) handleResponses(conn *net.UDPConn, destAddr *net.UDPAdd
|
|||||||
LastSeen: time.Now(),
|
LastSeen: time.Now(),
|
||||||
})
|
})
|
||||||
logger.Debug("Stored session mapping: %s -> %s", sessionKey, destAddr.String())
|
logger.Debug("Stored session mapping: %s -> %s", sessionKey, destAddr.String())
|
||||||
|
} else if ok && buffer[0] == WireGuardMessageTypeTransportData {
|
||||||
|
// Track communication pattern for session rebuilding (reverse direction)
|
||||||
|
s.trackCommunicationPattern(destAddr, remoteAddr, receiverIndex, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,3 +849,117 @@ func (s *UDPProxyServer) UpdateDestinationInMappings(oldDest, newDest PeerDestin
|
|||||||
|
|
||||||
return updatedCount
|
return updatedCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trackCommunicationPattern tracks bidirectional communication patterns to rebuild sessions
|
||||||
|
func (s *UDPProxyServer) trackCommunicationPattern(fromAddr, toAddr *net.UDPAddr, receiverIndex uint32, fromClient bool) {
|
||||||
|
var clientAddr, destAddr *net.UDPAddr
|
||||||
|
var clientIndex, destIndex uint32
|
||||||
|
|
||||||
|
if fromClient {
|
||||||
|
clientAddr = fromAddr
|
||||||
|
destAddr = toAddr
|
||||||
|
clientIndex = receiverIndex
|
||||||
|
destIndex = 0 // We don't know the destination index yet
|
||||||
|
} else {
|
||||||
|
clientAddr = toAddr
|
||||||
|
destAddr = fromAddr
|
||||||
|
clientIndex = 0 // We don't know the client index yet
|
||||||
|
destIndex = receiverIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
patternKey := fmt.Sprintf("%s-%s", clientAddr.String(), destAddr.String())
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
if existingPattern, ok := s.commPatterns.Load(patternKey); ok {
|
||||||
|
pattern := existingPattern.(*CommunicationPattern)
|
||||||
|
|
||||||
|
// Update the pattern
|
||||||
|
if fromClient {
|
||||||
|
pattern.LastFromClient = now
|
||||||
|
if pattern.ClientIndex == 0 {
|
||||||
|
pattern.ClientIndex = clientIndex
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pattern.LastFromDest = now
|
||||||
|
if pattern.DestIndex == 0 {
|
||||||
|
pattern.DestIndex = destIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern.PacketCount++
|
||||||
|
s.commPatterns.Store(patternKey, pattern)
|
||||||
|
|
||||||
|
// Check if we have bidirectional communication and can rebuild a session
|
||||||
|
s.tryRebuildSession(pattern)
|
||||||
|
} else {
|
||||||
|
// Create new pattern
|
||||||
|
pattern := &CommunicationPattern{
|
||||||
|
FromClient: clientAddr,
|
||||||
|
ToDestination: destAddr,
|
||||||
|
ClientIndex: clientIndex,
|
||||||
|
DestIndex: destIndex,
|
||||||
|
PacketCount: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromClient {
|
||||||
|
pattern.LastFromClient = now
|
||||||
|
} else {
|
||||||
|
pattern.LastFromDest = now
|
||||||
|
}
|
||||||
|
|
||||||
|
s.commPatterns.Store(patternKey, pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryRebuildSession attempts to rebuild a WireGuard session from communication patterns
|
||||||
|
func (s *UDPProxyServer) tryRebuildSession(pattern *CommunicationPattern) {
|
||||||
|
// Check if we have bidirectional communication within a reasonable time window
|
||||||
|
timeDiff := pattern.LastFromClient.Sub(pattern.LastFromDest)
|
||||||
|
if timeDiff < 0 {
|
||||||
|
timeDiff = -timeDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only rebuild if we have recent bidirectional communication and both indices
|
||||||
|
if timeDiff < 30*time.Second && pattern.ClientIndex != 0 && pattern.DestIndex != 0 && pattern.PacketCount >= 4 {
|
||||||
|
// Create session mapping: client's index maps to destination
|
||||||
|
sessionKey := fmt.Sprintf("%d:%d", pattern.DestIndex, pattern.ClientIndex)
|
||||||
|
|
||||||
|
// Check if we already have this session
|
||||||
|
if _, exists := s.wgSessions.Load(sessionKey); !exists {
|
||||||
|
session := &WireGuardSession{
|
||||||
|
ReceiverIndex: pattern.DestIndex,
|
||||||
|
SenderIndex: pattern.ClientIndex,
|
||||||
|
DestAddr: pattern.ToDestination,
|
||||||
|
LastSeen: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.wgSessions.Store(sessionKey, session)
|
||||||
|
logger.Info("Rebuilt WireGuard session from communication pattern: %s -> %s (packets: %d)",
|
||||||
|
sessionKey, pattern.ToDestination.String(), pattern.PacketCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanupIdleCommunicationPatterns periodically removes idle communication patterns
|
||||||
|
func (s *UDPProxyServer) cleanupIdleCommunicationPatterns() {
|
||||||
|
ticker := time.NewTicker(10 * time.Minute)
|
||||||
|
for range ticker.C {
|
||||||
|
now := time.Now()
|
||||||
|
s.commPatterns.Range(func(key, value interface{}) bool {
|
||||||
|
pattern := value.(*CommunicationPattern)
|
||||||
|
|
||||||
|
// Get the most recent activity
|
||||||
|
lastActivity := pattern.LastFromClient
|
||||||
|
if pattern.LastFromDest.After(lastActivity) {
|
||||||
|
lastActivity = pattern.LastFromDest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove patterns that haven't had activity in 20 minutes
|
||||||
|
if now.Sub(lastActivity) > 20*time.Minute {
|
||||||
|
s.commPatterns.Delete(key)
|
||||||
|
logger.Debug("Removed idle communication pattern: %s", key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user