feat: audit api, sdwan persist, relay fallback updates

This commit is contained in:
2026-03-06 14:47:03 +08:00
parent e96a2e5dd9
commit 57b4dadd42
26 changed files with 991 additions and 183 deletions

View File

@@ -36,17 +36,17 @@ const (
// ServerConfig holds inp2ps configuration.
type ServerConfig struct {
WSPort int `json:"wsPort"`
STUNUDP1 int `json:"stunUDP1"`
STUNUDP2 int `json:"stunUDP2"`
STUNTCP1 int `json:"stunTCP1"`
STUNTCP2 int `json:"stunTCP2"`
WebPort int `json:"webPort"`
APIPort int `json:"apiPort"`
DBPath string `json:"dbPath"`
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
LogLevel int `json:"logLevel"` // 0=debug, 1=info, 2=warn, 3=error
WSPort int `json:"wsPort"`
STUNUDP1 int `json:"stunUDP1"`
STUNUDP2 int `json:"stunUDP2"`
STUNTCP1 int `json:"stunTCP1"`
STUNTCP2 int `json:"stunTCP2"`
WebPort int `json:"webPort"`
APIPort int `json:"apiPort"`
DBPath string `json:"dbPath"`
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
LogLevel int `json:"logLevel"` // 0=debug, 1=info, 2=warn, 3=error
Token uint64 `json:"token"` // master token for auth
Tokens []uint64 `json:"tokens"` // additional tenant tokens
JWTKey string `json:"jwtKey"` // auto-generated if empty
@@ -132,10 +132,11 @@ type ClientConfig struct {
STUNTCP1 int `json:"stunTCP1,omitempty"`
STUNTCP2 int `json:"stunTCP2,omitempty"`
RelayEnabled bool `json:"relayEnabled"` // --relay
SuperRelay bool `json:"superRelay"` // --super
RelayPort int `json:"relayPort"`
MaxRelayLoad int `json:"maxRelayLoad"`
RelayEnabled bool `json:"relayEnabled"` // --relay
SuperRelay bool `json:"superRelay"` // --super
RelayOfficial bool `json:"relayOfficial"` // official relay tag
RelayPort int `json:"relayPort"`
MaxRelayLoad int `json:"maxRelayLoad"`
ShareBandwidth int `json:"shareBandwidth"` // Mbps
LogLevel int `json:"logLevel"`
@@ -163,6 +164,8 @@ func DefaultClientConfig() ClientConfig {
ShareBandwidth: 10,
RelayPort: DefaultRelayPort,
MaxRelayLoad: DefaultMaxRelayLoad,
RelayEnabled: true,
RelayOfficial: false,
LogLevel: 1,
}
}

View File

@@ -197,8 +197,9 @@ type LoginReq struct {
Version string `json:"version"`
NATType NATType `json:"natType"`
ShareBandwidth int `json:"shareBandwidth"`
RelayEnabled bool `json:"relayEnabled"` // --relay flag
SuperRelay bool `json:"superRelay"` // --super flag
RelayEnabled bool `json:"relayEnabled"` // --relay flag
SuperRelay bool `json:"superRelay"` // --super flag
RelayOfficial bool `json:"relayOfficial"` // official relay tag
PublicIP string `json:"publicIP,omitempty"`
PublicPort int `json:"publicPort,omitempty"`
}
@@ -264,6 +265,7 @@ type ConnectRsp struct {
// RelayNodeReq asks the server for a relay node.
type RelayNodeReq struct {
PeerNode string `json:"peerNode"`
Mode string `json:"mode,omitempty"` // "tenant" | "official"
}
type RelayNodeRsp struct {
@@ -292,17 +294,24 @@ type SDWANNode struct {
IP string `json:"ip"`
}
type SubnetProxy struct {
Node string `json:"node"`
LocalCIDR string `json:"localCIDR"`
VirtualCIDR string `json:"virtualCIDR"`
}
type SDWANConfig struct {
Enabled bool `json:"enabled,omitempty"`
Name string `json:"name,omitempty"`
GatewayCIDR string `json:"gatewayCIDR"`
Mode string `json:"mode,omitempty"` // hub | mesh | fullmesh
HubNode string `json:"hubNode,omitempty"`
IP string `json:"ip,omitempty"` // node self IP if pushed per-node
MTU int `json:"mtu,omitempty"`
Routes []string `json:"routes,omitempty"`
Nodes []SDWANNode `json:"nodes"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Name string `json:"name,omitempty"`
GatewayCIDR string `json:"gatewayCIDR"`
Mode string `json:"mode,omitempty"` // hub | mesh | fullmesh
HubNode string `json:"hubNode,omitempty"`
IP string `json:"ip,omitempty"` // node self IP if pushed per-node
MTU int `json:"mtu,omitempty"`
Routes []string `json:"routes,omitempty"`
Nodes []SDWANNode `json:"nodes"`
SubnetProxies []SubnetProxy `json:"subnetProxies,omitempty"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
}
type SDWANPeer struct {

View File

@@ -1,13 +1,13 @@
// Package relay implements relay/super-relay node capabilities.
//
// Relay flow:
// 1. Client A asks server for relay (RelayNodeReq)
// 2. Server finds relay R, generates TOTP/token, responds to A (RelayNodeRsp)
// 3. Server pushes RelayOffer to R with session info
// 4. A connects to R:relayPort, sends RelayHandshake{SessionID, Role="from", Token}
// 5. B connects to R:relayPort, sends RelayHandshake{SessionID, Role="to", Token}
// (B gets the session info via server push)
// 6. R verifies both tokens, bridges A↔B
// 1. Client A asks server for relay (RelayNodeReq)
// 2. Server finds relay R, generates TOTP/token, responds to A (RelayNodeRsp)
// 3. Server pushes RelayOffer to R with session info
// 4. A connects to R:relayPort, sends RelayHandshake{SessionID, Role="from", Token}
// 5. B connects to R:relayPort, sends RelayHandshake{SessionID, Role="to", Token}
// (B gets the session info via server push)
// 6. R verifies both tokens, bridges A↔B
package relay
import (
@@ -68,6 +68,7 @@ type Manager struct {
enabled bool
superRelay bool
maxLoad int
maxMbps int
token uint64 // this node's auth token
port int
listener net.Listener
@@ -92,11 +93,12 @@ type Session struct {
}
// NewManager creates a relay manager.
func NewManager(port int, enabled, superRelay bool, maxLoad int, token uint64) *Manager {
func NewManager(port int, enabled, superRelay bool, maxLoad int, token uint64, maxMbps int) *Manager {
return &Manager{
enabled: enabled,
superRelay: superRelay,
maxLoad: maxLoad,
maxMbps: maxMbps,
token: token,
port: port,
pending: make(map[string]*pendingSession),
@@ -296,14 +298,47 @@ func (m *Manager) bridge(ps *pendingSession) {
var wg sync.WaitGroup
wg.Add(2)
copyWithLimit := func(dst, src net.Conn) int64 {
if m.maxMbps <= 0 {
n, _ := io.Copy(dst, src)
return n
}
bytesPerSec := int64(m.maxMbps) * 1024 * 1024 / 8
if bytesPerSec < 1 {
bytesPerSec = 1
}
var total int64
buf := make([]byte, 32*1024)
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
var allowance = bytesPerSec / 10
for {
n, err := src.Read(buf)
if n > 0 {
// simple token bucket
if allowance < int64(n) {
<-ticker.C
allowance = bytesPerSec / 10
}
allowance -= int64(n)
w, _ := dst.Write(buf[:n])
total += int64(w)
}
if err != nil {
break
}
}
return total
}
go func() {
defer wg.Done()
n, _ := io.Copy(sess.ConnB, sess.ConnA)
n := copyWithLimit(sess.ConnB, sess.ConnA)
atomic.AddInt64(&sess.BytesFwd, n)
}()
go func() {
defer wg.Done()
n, _ := io.Copy(sess.ConnA, sess.ConnB)
n := copyWithLimit(sess.ConnA, sess.ConnB)
atomic.AddInt64(&sess.BytesFwd, n)
}()

View File

@@ -12,7 +12,7 @@ import (
func TestRelayBridge(t *testing.T) {
token := auth.MakeToken("test", "pass")
mgr := NewManager(29700, true, false, 10, token)
mgr := NewManager(29700, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}
@@ -94,7 +94,7 @@ func TestRelayBridge(t *testing.T) {
func TestRelayLargeData(t *testing.T) {
token := auth.MakeToken("test", "pass")
mgr := NewManager(29701, true, false, 10, token)
mgr := NewManager(29701, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}
@@ -173,7 +173,7 @@ func TestRelayLargeData(t *testing.T) {
func TestRelayAuthDenied(t *testing.T) {
token := auth.MakeToken("real", "token")
mgr := NewManager(29702, true, false, 10, token)
mgr := NewManager(29702, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}