cleanup: remove 2,369 lines of dead code
Removed backup files and unused legacy scanner function. All code verified as unreferenced.
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries"
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/utils"
|
||||
@@ -38,6 +46,48 @@ func MachineBindingMiddleware(agentQueries *queries.AgentQueries, minAgentVersio
|
||||
return
|
||||
}
|
||||
|
||||
// Check if agent is reporting an update completion
|
||||
reportedVersion := c.GetHeader("X-Agent-Version")
|
||||
updateNonce := c.GetHeader("X-Update-Nonce")
|
||||
|
||||
if agent.IsUpdating && updateNonce != "" {
|
||||
// Validate the nonce first (proves server authorized this update)
|
||||
if agent.PublicKeyFingerprint == nil {
|
||||
log.Printf("[SECURITY] Agent %s has no public key fingerprint for nonce validation", agentID)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "server public key not configured"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if err := validateUpdateNonceMiddleware(updateNonce, *agent.PublicKeyFingerprint); err != nil {
|
||||
log.Printf("[SECURITY] Invalid update nonce for agent %s: %v", agentID, err)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "invalid update nonce"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Check for downgrade attempt (security boundary)
|
||||
if !isVersionUpgrade(reportedVersion, agent.CurrentVersion) {
|
||||
log.Printf("[SECURITY] Downgrade attempt detected: agent %s %s → %s",
|
||||
agentID, agent.CurrentVersion, reportedVersion)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "downgrade not allowed"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Valid upgrade - complete it in database
|
||||
go func() {
|
||||
if err := agentQueries.CompleteAgentUpdate(agentID.String(), reportedVersion); err != nil {
|
||||
log.Printf("[ERROR] Failed to complete agent update: %v", err)
|
||||
} else {
|
||||
log.Printf("[system] Agent %s updated: %s → %s", agentID, agent.CurrentVersion, reportedVersion)
|
||||
}
|
||||
}()
|
||||
|
||||
// Allow this request through
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// Check minimum version (hard cutoff for legacy de-support)
|
||||
if agent.CurrentVersion != "" && minAgentVersion != "" {
|
||||
if !utils.IsNewerOrEqualVersion(agent.CurrentVersion, minAgentVersion) {
|
||||
@@ -97,3 +147,82 @@ func MachineBindingMiddleware(agentQueries *queries.AgentQueries, minAgentVersio
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func validateUpdateNonceMiddleware(nonceB64, serverPublicKey string) error {
|
||||
// Decode base64 nonce
|
||||
data, err := base64.StdEncoding.DecodeString(nonceB64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid base64: %w", err)
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
var nonce struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
TargetVersion string `json:"target_version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &nonce); err != nil {
|
||||
return fmt.Errorf("invalid format: %w", err)
|
||||
}
|
||||
|
||||
// Check freshness
|
||||
if time.Now().Unix()-nonce.Timestamp > 600 { // 10 minutes
|
||||
return fmt.Errorf("nonce expired (age: %d seconds)", time.Now().Unix()-nonce.Timestamp)
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
signature, err := base64.StdEncoding.DecodeString(nonce.Signature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid signature encoding: %w", err)
|
||||
}
|
||||
|
||||
// Parse server's public key
|
||||
pubKeyBytes, err := hex.DecodeString(serverPublicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid server public key: %w", err)
|
||||
}
|
||||
|
||||
// Remove signature for verification
|
||||
originalSig := nonce.Signature
|
||||
nonce.Signature = ""
|
||||
verifyData, err := json.Marshal(nonce)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal verify data: %w", err)
|
||||
}
|
||||
|
||||
if !ed25519.Verify(ed25519.PublicKey(pubKeyBytes), verifyData, signature) {
|
||||
return fmt.Errorf("signature verification failed")
|
||||
}
|
||||
|
||||
// Restore signature (not needed but good practice)
|
||||
nonce.Signature = originalSig
|
||||
return nil
|
||||
}
|
||||
|
||||
func isVersionUpgrade(new, current string) bool {
|
||||
// Parse semantic versions
|
||||
newParts := strings.Split(new, ".")
|
||||
curParts := strings.Split(current, ".")
|
||||
|
||||
// Convert to integers for comparison
|
||||
newMajor, _ := strconv.Atoi(newParts[0])
|
||||
newMinor, _ := strconv.Atoi(newParts[1])
|
||||
newPatch, _ := strconv.Atoi(newParts[2])
|
||||
|
||||
curMajor, _ := strconv.Atoi(curParts[0])
|
||||
curMinor, _ := strconv.Atoi(curParts[1])
|
||||
curPatch, _ := strconv.Atoi(curParts[2])
|
||||
|
||||
// Check if new > current (not equal, not less)
|
||||
if newMajor > curMajor {
|
||||
return true
|
||||
}
|
||||
if newMajor == curMajor && newMinor > curMinor {
|
||||
return true
|
||||
}
|
||||
if newMajor == curMajor && newMinor == curMinor && newPatch > curPatch {
|
||||
return true
|
||||
}
|
||||
return false // Equal or downgrade
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user