feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge
This commit is contained in:
@@ -41,9 +41,9 @@ func checkDashboardWSToken(r *http.Request) bool {
|
||||
}
|
||||
user, pass := parts[0], parts[1]
|
||||
usersMu.RLock()
|
||||
expectedPass, exists := authUsers[user]
|
||||
stored, exists := authUsers[user]
|
||||
usersMu.RUnlock()
|
||||
return exists && secureStringEqual(pass, expectedPass)
|
||||
return exists && checkPassword(stored, pass)
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
@@ -106,6 +106,8 @@ type WSHub struct {
|
||||
agentConfigs map[string]AgentForgeConfig
|
||||
agentCapabilities map[string]models.AgentCapabilities
|
||||
agentLogs map[string]string
|
||||
// T1016 DNS drift detection — stores last seen resolver list per agent
|
||||
agentDNS map[string][]string
|
||||
serverPolicy ServerPolicy
|
||||
pingIntervalSec int
|
||||
fleetSecret string // baked into forged agents; verified on WS connect
|
||||
@@ -120,6 +122,7 @@ func NewWSHub(database *db.Database) *WSHub {
|
||||
agentConfigs: make(map[string]AgentForgeConfig),
|
||||
agentCapabilities: make(map[string]models.AgentCapabilities),
|
||||
agentLogs: make(map[string]string),
|
||||
agentDNS: make(map[string][]string),
|
||||
pingIntervalSec: 30,
|
||||
}
|
||||
}
|
||||
@@ -519,6 +522,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
CPUUsagePct float64 `json:"cpu_usage_pct"`
|
||||
MemoryUsagePct float64 `json:"memory_usage_pct"`
|
||||
UptimeSeconds int `json:"uptime_seconds"`
|
||||
// DNS config (T1016)
|
||||
DNSServers []string `json:"dns_servers,omitempty"`
|
||||
DNSSearchDomains []string `json:"dns_search_domains,omitempty"`
|
||||
// Resource pressure
|
||||
CPUFreqMHz *int `json:"cpu_freq_mhz,omitempty"`
|
||||
CPUMaxMHz *int `json:"cpu_max_mhz,omitempty"`
|
||||
@@ -587,6 +593,23 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
if stats.GPUTempC != nil { broadcast["gpu_temp_c"] = *stats.GPUTempC }
|
||||
if stats.GPUUsagePct != nil { broadcast["gpu_usage_pct"] = *stats.GPUUsagePct }
|
||||
|
||||
// T1016 DNS drift detection
|
||||
if len(stats.DNSServers) > 0 {
|
||||
broadcast["dns_servers"] = stats.DNSServers
|
||||
if stats.DNSSearchDomains != nil {
|
||||
broadcast["dns_search_domains"] = stats.DNSSearchDomains
|
||||
}
|
||||
h.mu.Lock()
|
||||
prev, hasPrev := h.agentDNS[agentID]
|
||||
drifted := hasPrev && !dnsEqual(prev, stats.DNSServers)
|
||||
h.agentDNS[agentID] = stats.DNSServers
|
||||
h.mu.Unlock()
|
||||
if drifted {
|
||||
broadcast["dns_drifted"] = true
|
||||
log.Printf("[T1016] DNS drift detected on agent %s: %v → %v", agentID, prev, stats.DNSServers)
|
||||
}
|
||||
}
|
||||
|
||||
if stats.SSHAvailable != nil {
|
||||
broadcast["ssh_available"] = *stats.SSHAvailable
|
||||
}
|
||||
@@ -850,6 +873,25 @@ func mustMarshal(v interface{}) json.RawMessage {
|
||||
return data
|
||||
}
|
||||
|
||||
// dnsEqual returns true when two DNS server lists contain the same addresses
|
||||
// regardless of order. Used for T1016 drift detection.
|
||||
func dnsEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
m := make(map[string]int, len(a))
|
||||
for _, v := range a {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range b {
|
||||
m[v]--
|
||||
if m[v] < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// BroadcastToAgents sends a message to all connected agents
|
||||
func (h *WSHub) BroadcastToAgents(msg Message) {
|
||||
h.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user