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:
186
aggregator-server/internal/api/handlers/agent_build.go
Normal file
186
aggregator-server/internal/api/handlers/agent_build.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// BuildAgent handles the agent build endpoint
|
||||
func BuildAgent(c *gin.Context) {
|
||||
var req services.AgentSetupRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create config builder
|
||||
configBuilder := services.NewConfigBuilder(req.ServerURL)
|
||||
|
||||
// Build agent configuration
|
||||
config, err := configBuilder.BuildAgentConfig(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create agent builder
|
||||
agentBuilder := services.NewAgentBuilder()
|
||||
|
||||
// Generate build artifacts
|
||||
buildResult, err := agentBuilder.BuildAgentWithConfig(config)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create response with native binary instructions
|
||||
response := gin.H{
|
||||
"agent_id": config.AgentID,
|
||||
"config_file": buildResult.ConfigFile,
|
||||
"platform": buildResult.Platform,
|
||||
"config_version": config.ConfigVersion,
|
||||
"agent_version": config.AgentVersion,
|
||||
"build_time": buildResult.BuildTime,
|
||||
"next_steps": []string{
|
||||
"1. Download native binary from server",
|
||||
"2. Place binary in /usr/local/bin/redflag-agent",
|
||||
"3. Set permissions: chmod 755 /usr/local/bin/redflag-agent",
|
||||
"4. Create config directory: mkdir -p /etc/redflag",
|
||||
"5. Save config to /etc/redflag/config.json",
|
||||
"6. Set config permissions: chmod 600 /etc/redflag/config.json",
|
||||
"7. Start service: systemctl enable --now redflag-agent",
|
||||
},
|
||||
"configuration": config.PublicConfig,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetBuildInstructions returns build instructions for manual setup
|
||||
func GetBuildInstructions(c *gin.Context) {
|
||||
agentID := c.Param("agentID")
|
||||
if agentID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
instructions := gin.H{
|
||||
"title": "RedFlag Agent Build Instructions",
|
||||
"agent_id": agentID,
|
||||
"steps": []gin.H{
|
||||
{
|
||||
"step": 1,
|
||||
"title": "Prepare Build Environment",
|
||||
"commands": []string{
|
||||
"mkdir -p redflag-build",
|
||||
"cd redflag-build",
|
||||
},
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"title": "Copy Agent Source Code",
|
||||
"commands": []string{
|
||||
"cp -r ../aggregator-agent/* .",
|
||||
"ls -la",
|
||||
},
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"title": "Build Docker Image",
|
||||
"commands": []string{
|
||||
"docker build -t redflag-agent:" + agentID[:8] + " .",
|
||||
},
|
||||
},
|
||||
{
|
||||
"step": 4,
|
||||
"title": "Create Docker Network",
|
||||
"commands": []string{
|
||||
"docker network create redflag 2>/dev/null || true",
|
||||
},
|
||||
},
|
||||
{
|
||||
"step": 5,
|
||||
"title": "Deploy Agent",
|
||||
"commands": []string{
|
||||
"docker compose up -d",
|
||||
},
|
||||
},
|
||||
{
|
||||
"step": 6,
|
||||
"title": "Verify Deployment",
|
||||
"commands": []string{
|
||||
"docker compose logs -f",
|
||||
"docker ps",
|
||||
},
|
||||
},
|
||||
},
|
||||
"troubleshooting": []gin.H{
|
||||
{
|
||||
"issue": "Build fails with 'go mod download' errors",
|
||||
"solution": "Ensure go.mod and go.sum are copied correctly and internet connectivity is available",
|
||||
},
|
||||
{
|
||||
"issue": "Container fails to start",
|
||||
"solution": "Check docker-compose.yml and ensure Docker secrets are created with 'echo \"secret-value\" | docker secret create secret-name -'",
|
||||
},
|
||||
{
|
||||
"issue": "Agent cannot connect to server",
|
||||
"solution": "Verify server URL is accessible from container and firewall rules allow traffic",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, instructions)
|
||||
}
|
||||
|
||||
// DownloadBuildArtifacts provides download links for generated files
|
||||
func DownloadBuildArtifacts(c *gin.Context) {
|
||||
agentID := c.Param("agentID")
|
||||
fileType := c.Param("fileType")
|
||||
buildDir := c.Query("buildDir")
|
||||
|
||||
// Validate agent ID parameter
|
||||
if agentID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if buildDir == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "build directory is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Security check: ensure the buildDir is within expected path
|
||||
absBuildDir, err := filepath.Abs(buildDir)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid build directory"})
|
||||
return
|
||||
}
|
||||
|
||||
// Construct file path based on type
|
||||
var filePath string
|
||||
switch fileType {
|
||||
case "compose":
|
||||
filePath = filepath.Join(absBuildDir, "docker-compose.yml")
|
||||
case "dockerfile":
|
||||
filePath = filepath.Join(absBuildDir, "Dockerfile")
|
||||
case "config":
|
||||
filePath = filepath.Join(absBuildDir, "pkg", "embedded", "config.go")
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file type"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Serve file for download
|
||||
c.FileAttachment(filePath, filepath.Base(filePath))
|
||||
}
|
||||
79
aggregator-server/internal/api/handlers/agent_setup.go
Normal file
79
aggregator-server/internal/api/handlers/agent_setup.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupAgent handles the agent setup endpoint
|
||||
func SetupAgent(c *gin.Context) {
|
||||
var req services.AgentSetupRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create config builder
|
||||
configBuilder := services.NewConfigBuilder(req.ServerURL)
|
||||
|
||||
// Build agent configuration
|
||||
config, err := configBuilder.BuildAgentConfig(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create response
|
||||
response := gin.H{
|
||||
"agent_id": config.AgentID,
|
||||
"registration_token": config.Secrets["registration_token"],
|
||||
"server_public_key": config.Secrets["server_public_key"],
|
||||
"configuration": config.PublicConfig,
|
||||
"secrets": config.Secrets,
|
||||
"template": config.Template,
|
||||
"setup_time": config.BuildTime,
|
||||
"secrets_created": config.SecretsCreated,
|
||||
"secrets_path": config.SecretsPath,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetTemplates returns available agent templates
|
||||
func GetTemplates(c *gin.Context) {
|
||||
configBuilder := services.NewConfigBuilder("")
|
||||
templates := configBuilder.GetTemplates()
|
||||
c.JSON(http.StatusOK, gin.H{"templates": templates})
|
||||
}
|
||||
|
||||
// ValidateConfiguration validates a configuration before deployment
|
||||
func ValidateConfiguration(c *gin.Context) {
|
||||
var config map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&config); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
agentType, exists := config["agent_type"].(string)
|
||||
if !exists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "agent_type is required"})
|
||||
return
|
||||
}
|
||||
|
||||
configBuilder := services.NewConfigBuilder("")
|
||||
template, exists := configBuilder.GetTemplate(agentType)
|
||||
if !exists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Unknown agent type"})
|
||||
return
|
||||
}
|
||||
|
||||
// Simple validation response
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"valid": true,
|
||||
"message": "Configuration appears valid",
|
||||
"agent_type": agentType,
|
||||
"template": template.Name,
|
||||
})
|
||||
}
|
||||
229
aggregator-server/internal/api/handlers/build_orchestrator.go
Normal file
229
aggregator-server/internal/api/handlers/build_orchestrator.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// NewAgentBuild handles new agent installation requests
|
||||
func NewAgentBuild(c *gin.Context) {
|
||||
var req services.NewBuildRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate registration token
|
||||
if req.RegistrationToken == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "registration token is required for new installations"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to setup request format
|
||||
setupReq := services.AgentSetupRequest{
|
||||
ServerURL: req.ServerURL,
|
||||
Environment: req.Environment,
|
||||
AgentType: req.AgentType,
|
||||
Organization: req.Organization,
|
||||
CustomSettings: req.CustomSettings,
|
||||
DeploymentID: req.DeploymentID,
|
||||
}
|
||||
|
||||
// Create config builder
|
||||
configBuilder := services.NewConfigBuilder(req.ServerURL)
|
||||
|
||||
// Build agent configuration
|
||||
config, err := configBuilder.BuildAgentConfig(setupReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Override generated agent ID if provided (for upgrades)
|
||||
if req.AgentID != "" {
|
||||
config.AgentID = req.AgentID
|
||||
// Update public config with existing agent ID
|
||||
if config.PublicConfig == nil {
|
||||
config.PublicConfig = make(map[string]interface{})
|
||||
}
|
||||
config.PublicConfig["agent_id"] = req.AgentID
|
||||
}
|
||||
|
||||
// Create agent builder
|
||||
agentBuilder := services.NewAgentBuilder()
|
||||
|
||||
// Generate build artifacts
|
||||
buildResult, err := agentBuilder.BuildAgentWithConfig(config)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Construct download URL
|
||||
binaryURL := fmt.Sprintf("%s/api/v1/downloads/%s", req.ServerURL, config.Platform)
|
||||
|
||||
// Create response with native binary instructions
|
||||
response := gin.H{
|
||||
"agent_id": config.AgentID,
|
||||
"binary_url": binaryURL,
|
||||
"platform": config.Platform,
|
||||
"config_version": config.ConfigVersion,
|
||||
"agent_version": config.AgentVersion,
|
||||
"build_time": buildResult.BuildTime,
|
||||
"install_type": "new",
|
||||
"consumes_seat": true,
|
||||
"next_steps": []string{
|
||||
"1. Download native binary: curl -sL " + binaryURL + " -o /usr/local/bin/redflag-agent",
|
||||
"2. Set permissions: chmod 755 /usr/local/bin/redflag-agent",
|
||||
"3. Create config directory: mkdir -p /etc/redflag",
|
||||
"4. Save configuration (provided in this response) to /etc/redflag/config.json",
|
||||
"5. Set config permissions: chmod 600 /etc/redflag/config.json",
|
||||
"6. Start service: systemctl enable --now redflag-agent",
|
||||
},
|
||||
"configuration": config.PublicConfig,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// UpgradeAgentBuild handles agent upgrade requests
|
||||
func UpgradeAgentBuild(c *gin.Context) {
|
||||
agentID := c.Param("agentID")
|
||||
if agentID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req services.UpgradeBuildRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if req.ServerURL == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "server URL is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to setup request format
|
||||
setupReq := services.AgentSetupRequest{
|
||||
ServerURL: req.ServerURL,
|
||||
Environment: req.Environment,
|
||||
AgentType: req.AgentType,
|
||||
Organization: req.Organization,
|
||||
CustomSettings: req.CustomSettings,
|
||||
DeploymentID: req.DeploymentID,
|
||||
}
|
||||
|
||||
// Create config builder
|
||||
configBuilder := services.NewConfigBuilder(req.ServerURL)
|
||||
|
||||
// Build agent configuration
|
||||
config, err := configBuilder.BuildAgentConfig(setupReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Override with existing agent ID (this is the key for upgrades)
|
||||
config.AgentID = agentID
|
||||
if config.PublicConfig == nil {
|
||||
config.PublicConfig = make(map[string]interface{})
|
||||
}
|
||||
config.PublicConfig["agent_id"] = agentID
|
||||
|
||||
// For upgrades, we might want to preserve certain existing settings
|
||||
if req.PreserveExisting {
|
||||
// TODO: Load existing agent config and merge/override as needed
|
||||
// This would involve reading the existing agent's configuration
|
||||
// and selectively preserving certain fields
|
||||
}
|
||||
|
||||
// Create agent builder
|
||||
agentBuilder := services.NewAgentBuilder()
|
||||
|
||||
// Generate build artifacts
|
||||
buildResult, err := agentBuilder.BuildAgentWithConfig(config)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Construct download URL
|
||||
binaryURL := fmt.Sprintf("%s/api/v1/downloads/%s?version=%s", req.ServerURL, config.Platform, config.AgentVersion)
|
||||
|
||||
// Create response with native binary upgrade instructions
|
||||
response := gin.H{
|
||||
"agent_id": config.AgentID,
|
||||
"binary_url": binaryURL,
|
||||
"platform": config.Platform,
|
||||
"config_version": config.ConfigVersion,
|
||||
"agent_version": config.AgentVersion,
|
||||
"build_time": buildResult.BuildTime,
|
||||
"install_type": "upgrade",
|
||||
"consumes_seat": false,
|
||||
"preserves_agent_id": true,
|
||||
"next_steps": []string{
|
||||
"1. Stop agent service: systemctl stop redflag-agent",
|
||||
"2. Download updated binary: curl -sL " + binaryURL + " -o /usr/local/bin/redflag-agent",
|
||||
"3. Set permissions: chmod 755 /usr/local/bin/redflag-agent",
|
||||
"4. Update config (provided in this response) to /etc/redflag/config.json if needed",
|
||||
"5. Start service: systemctl start redflag-agent",
|
||||
"6. Verify: systemctl status redflag-agent",
|
||||
},
|
||||
"configuration": config.PublicConfig,
|
||||
"upgrade_notes": []string{
|
||||
"This upgrade preserves the existing agent ID: " + agentID,
|
||||
"No additional seat will be consumed",
|
||||
"Config version: " + config.ConfigVersion,
|
||||
"Agent binary version: " + config.AgentVersion,
|
||||
"Agent will receive latest security enhancements and bug fixes",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// DetectAgentInstallation detects existing agent installations
|
||||
func DetectAgentInstallation(c *gin.Context) {
|
||||
// This endpoint helps the installer determine what type of installation to perform
|
||||
var req struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create detector service
|
||||
detector := services.NewInstallationDetector()
|
||||
|
||||
// Detect existing installation
|
||||
detection, err := detector.DetectExistingInstallation(req.AgentID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
response := gin.H{
|
||||
"detection_result": detection,
|
||||
"recommended_action": func() string {
|
||||
if detection.HasExistingAgent {
|
||||
return "upgrade"
|
||||
}
|
||||
return "new_installation"
|
||||
}(),
|
||||
"installation_type": func() string {
|
||||
if detection.HasExistingAgent {
|
||||
return "upgrade"
|
||||
}
|
||||
return "new"
|
||||
}(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -180,6 +180,8 @@ func (h *SecurityHandler) MachineBindingStatus(c *gin.Context) {
|
||||
|
||||
// Get total agents for comparison
|
||||
if totalAgents, err := h.agentQueries.GetTotalAgentCount(); err == nil {
|
||||
response["checks"].(map[string]interface{})["total_agents"] = totalAgents
|
||||
|
||||
// Calculate version compliance (agents meeting minimum version requirement)
|
||||
if compliantAgents, err := h.agentQueries.GetAgentCountByVersion("0.1.22"); err == nil {
|
||||
response["checks"].(map[string]interface{})["version_compliance"] = compliantAgents
|
||||
|
||||
@@ -425,6 +425,13 @@ func (h *SetupHandler) GenerateSigningKeys(c *gin.Context) {
|
||||
c.Header("Pragma", "no-cache")
|
||||
c.Header("Expires", "0")
|
||||
|
||||
// Load configuration to check for existing key
|
||||
cfg, err := config.Load() // This will load from .env file
|
||||
if err == nil && cfg.SigningPrivateKey != "" {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "A signing key is already configured for this server."})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate Ed25519 keypair
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user