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:
380
aggregator-server/internal/services/agent_builder.go
Normal file
380
aggregator-server/internal/services/agent_builder.go
Normal file
@@ -0,0 +1,380 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AgentBuilder handles generating embedded agent configurations
|
||||
type AgentBuilder struct {
|
||||
buildContext string
|
||||
}
|
||||
|
||||
// NewAgentBuilder creates a new agent builder
|
||||
func NewAgentBuilder() *AgentBuilder {
|
||||
return &AgentBuilder{}
|
||||
}
|
||||
|
||||
// BuildAgentWithConfig generates agent configuration and prepares signed binary
|
||||
func (ab *AgentBuilder) BuildAgentWithConfig(config *AgentConfiguration) (*BuildResult, error) {
|
||||
// Create temporary build directory
|
||||
buildDir, err := os.MkdirTemp("", "agent-build-")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create build directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate config.json (not embedded in binary)
|
||||
configJSONPath := filepath.Join(buildDir, "config.json")
|
||||
configJSON, err := ab.generateConfigJSON(config)
|
||||
if err != nil {
|
||||
os.RemoveAll(buildDir)
|
||||
return nil, fmt.Errorf("failed to generate config JSON: %w", err)
|
||||
}
|
||||
|
||||
// Write config.json to file
|
||||
if err := os.WriteFile(configJSONPath, []byte(configJSON), 0600); err != nil {
|
||||
os.RemoveAll(buildDir)
|
||||
return nil, fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
// Note: Binary is pre-built and stored in /app/binaries/{platform}/
|
||||
// We don't build or modify the binary here - it's generic for all agents
|
||||
// The signing happens at the platform level, not per-agent
|
||||
|
||||
return &BuildResult{
|
||||
BuildDir: buildDir,
|
||||
AgentID: config.AgentID,
|
||||
ConfigFile: configJSONPath,
|
||||
ConfigJSON: configJSON,
|
||||
Platform: config.Platform,
|
||||
BuildTime: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateConfigJSON converts configuration to JSON format
|
||||
func (ab *AgentBuilder) generateConfigJSON(config *AgentConfiguration) (string, error) {
|
||||
// Create complete configuration
|
||||
completeConfig := make(map[string]interface{})
|
||||
|
||||
// Copy public configuration
|
||||
for k, v := range config.PublicConfig {
|
||||
completeConfig[k] = v
|
||||
}
|
||||
|
||||
// Add secrets (they will be protected by file permissions at runtime)
|
||||
for k, v := range config.Secrets {
|
||||
completeConfig[k] = v
|
||||
}
|
||||
|
||||
// CRITICAL: Add both version fields explicitly
|
||||
// These MUST be present or middleware will block the agent
|
||||
completeConfig["version"] = config.ConfigVersion // Config schema version (e.g., "5")
|
||||
completeConfig["agent_version"] = config.AgentVersion // Agent binary version (e.g., "0.1.23.5")
|
||||
|
||||
// Add agent metadata
|
||||
completeConfig["agent_id"] = config.AgentID
|
||||
completeConfig["server_url"] = config.ServerURL
|
||||
completeConfig["organization"] = config.Organization
|
||||
completeConfig["environment"] = config.Environment
|
||||
completeConfig["template"] = config.Template
|
||||
completeConfig["build_time"] = config.BuildTime.Format(time.RFC3339)
|
||||
|
||||
// Convert to JSON
|
||||
jsonBytes, err := json.MarshalIndent(completeConfig, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal config to JSON: %w", err)
|
||||
}
|
||||
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
// BuildResult contains the results of the build process
|
||||
type BuildResult struct {
|
||||
BuildDir string `json:"build_dir"`
|
||||
AgentID string `json:"agent_id"`
|
||||
ConfigFile string `json:"config_file"`
|
||||
ConfigJSON string `json:"config_json"`
|
||||
Platform string `json:"platform"`
|
||||
BuildTime time.Time `json:"build_time"`
|
||||
}
|
||||
|
||||
// generateEmbeddedConfig generates the embedded configuration Go file
|
||||
func (ab *AgentBuilder) generateEmbeddedConfig(filename string, config *AgentConfiguration) error {
|
||||
// Create directory structure
|
||||
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert configuration to JSON for embedding
|
||||
configJSON, err := ab.configToJSON(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate Go source file with embedded configuration
|
||||
tmpl := `// Code generated by dynamic build system. DO NOT EDIT.
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EmbeddedAgentConfiguration contains the pre-built agent configuration
|
||||
var EmbeddedAgentConfiguration = []byte(` + "`" + `{{.ConfigJSON}}` + "`" + `)
|
||||
|
||||
// EmbeddedAgentID contains the agent ID
|
||||
var EmbeddedAgentID = "{{.AgentID}}"
|
||||
|
||||
// EmbeddedServerURL contains the server URL
|
||||
var EmbeddedServerURL = "{{.ServerURL}}"
|
||||
|
||||
// EmbeddedOrganization contains the organization
|
||||
var EmbeddedOrganization = "{{.Organization}}"
|
||||
|
||||
// EmbeddedEnvironment contains the environment
|
||||
var EmbeddedEnvironment = "{{.Environment}}"
|
||||
|
||||
// EmbeddedTemplate contains the template type
|
||||
var EmbeddedTemplate = "{{.Template}}"
|
||||
|
||||
// EmbeddedBuildTime contains the build time
|
||||
var EmbeddedBuildTime, _ = time.Parse(time.RFC3339, "{{.BuildTime}}")
|
||||
|
||||
// GetEmbeddedConfig returns the embedded configuration as a map
|
||||
func GetEmbeddedConfig() (map[string]interface{}, error) {
|
||||
var config map[string]interface{}
|
||||
err := json.Unmarshal(EmbeddedAgentConfiguration, &config)
|
||||
return config, err
|
||||
}
|
||||
|
||||
// SecretsMapping maps configuration fields to Docker secrets
|
||||
var SecretsMapping = map[string]string{
|
||||
{{range $key, $value := .Secrets}}"{{$key}}": "{{$value}}",
|
||||
{{end}}
|
||||
}
|
||||
`
|
||||
|
||||
// Execute template
|
||||
t, err := template.New("embedded").Parse(tmpl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data := struct {
|
||||
ConfigJSON string
|
||||
AgentID string
|
||||
ServerURL string
|
||||
Organization string
|
||||
Environment string
|
||||
Template string
|
||||
BuildTime string
|
||||
Secrets map[string]string
|
||||
}{
|
||||
ConfigJSON: configJSON,
|
||||
AgentID: config.AgentID,
|
||||
ServerURL: config.ServerURL,
|
||||
Organization: config.Organization,
|
||||
Environment: config.Environment,
|
||||
Template: config.Template,
|
||||
BuildTime: config.BuildTime.Format(time.RFC3339),
|
||||
Secrets: config.Secrets,
|
||||
}
|
||||
|
||||
if err := t.Execute(file, data); err != nil {
|
||||
return fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateDockerCompose generates a docker-compose.yml file
|
||||
func (ab *AgentBuilder) generateDockerCompose(filename string, config *AgentConfiguration) error {
|
||||
tmpl := `# Generated dynamically based on configuration
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
redflag-agent:
|
||||
image: {{.ImageTag}}
|
||||
container_name: redflag-agent-{{.AgentID}}
|
||||
restart: unless-stopped
|
||||
secrets:
|
||||
{{range $key := .SecretsKeys}}- {{$key}}
|
||||
{{end}}
|
||||
volumes:
|
||||
- /var/lib/redflag:/var/lib/redflag
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- REDFLAG_AGENT_ID={{.AgentID}}
|
||||
- REDFLAG_ENVIRONMENT={{.Environment}}
|
||||
- REDFLAG_SERVER_URL={{.ServerURL}}
|
||||
- REDFLAG_ORGANIZATION={{.Organization}}
|
||||
networks:
|
||||
- redflag
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
secrets:
|
||||
{{range $key, $value := .Secrets}}{{$key}}:
|
||||
external: true
|
||||
{{end}}
|
||||
|
||||
networks:
|
||||
redflag:
|
||||
external: true
|
||||
`
|
||||
|
||||
t, err := template.New("compose").Parse(tmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Extract secret keys for template
|
||||
secretsKeys := make([]string, 0, len(config.Secrets))
|
||||
for key := range config.Secrets {
|
||||
secretsKeys = append(secretsKeys, key)
|
||||
}
|
||||
|
||||
data := struct {
|
||||
ImageTag string
|
||||
AgentID string
|
||||
Environment string
|
||||
ServerURL string
|
||||
Organization string
|
||||
Secrets map[string]string
|
||||
SecretsKeys []string
|
||||
}{
|
||||
ImageTag: fmt.Sprintf("redflag-agent:%s", config.AgentID[:8]),
|
||||
AgentID: config.AgentID,
|
||||
Environment: config.Environment,
|
||||
ServerURL: config.ServerURL,
|
||||
Organization: config.Organization,
|
||||
Secrets: config.Secrets,
|
||||
SecretsKeys: secretsKeys,
|
||||
}
|
||||
|
||||
return t.Execute(file, data)
|
||||
}
|
||||
|
||||
// generateDockerfile generates a Dockerfile for building the agent
|
||||
func (ab *AgentBuilder) generateDockerfile(filename string, config *AgentConfiguration) error {
|
||||
tmpl := `# Dockerfile for RedFlag Agent with embedded configuration
|
||||
FROM golang:1.21-alpine AS builder
|
||||
|
||||
# Install ca-certificates for SSL/TLS
|
||||
RUN apk add --no-cache ca-certificates git
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy go mod files (these should be in the same directory as the Dockerfile)
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Copy generated embedded configuration
|
||||
COPY pkg/embedded/config.go ./pkg/embedded/config.go
|
||||
|
||||
# Build the agent with embedded configuration
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
-ldflags="-w -s -X main.version=dynamic-build-{{.AgentID}}" \
|
||||
-o redflag-agent \
|
||||
./cmd/agent
|
||||
|
||||
# Final stage
|
||||
FROM scratch
|
||||
|
||||
# Copy ca-certificates for SSL/TLS
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
# Copy the agent binary
|
||||
COPY --from=builder /app/redflag-agent /redflag-agent
|
||||
|
||||
# Set environment variables (these can be overridden by docker-compose)
|
||||
ENV REDFLAG_AGENT_ID="{{.AgentID}}"
|
||||
ENV REDFLAG_ENVIRONMENT="{{.Environment}}"
|
||||
ENV REDFLAG_SERVER_URL="{{.ServerURL}}"
|
||||
ENV REDFLAG_ORGANIZATION="{{.Organization}}"
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD ["/redflag-agent", "--health-check"]
|
||||
|
||||
# Run the agent
|
||||
ENTRYPOINT ["/redflag-agent"]
|
||||
`
|
||||
|
||||
t, err := template.New("dockerfile").Parse(tmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data := struct {
|
||||
AgentID string
|
||||
Environment string
|
||||
ServerURL string
|
||||
Organization string
|
||||
}{
|
||||
AgentID: config.AgentID,
|
||||
Environment: config.Environment,
|
||||
ServerURL: config.ServerURL,
|
||||
Organization: config.Organization,
|
||||
}
|
||||
|
||||
return t.Execute(file, data)
|
||||
}
|
||||
|
||||
// configToJSON converts configuration to JSON string
|
||||
func (ab *AgentBuilder) configToJSON(config *AgentConfiguration) (string, error) {
|
||||
// Create complete configuration with embedded values
|
||||
completeConfig := make(map[string]interface{})
|
||||
|
||||
// Copy public configuration
|
||||
for k, v := range config.PublicConfig {
|
||||
completeConfig[k] = v
|
||||
}
|
||||
|
||||
// Add secrets values (they will be overridden by Docker secrets at runtime)
|
||||
for k, v := range config.Secrets {
|
||||
completeConfig[k] = v
|
||||
}
|
||||
|
||||
// Convert to JSON with proper escaping
|
||||
jsonBytes, err := json.MarshalIndent(completeConfig, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal config to JSON: %w", err)
|
||||
}
|
||||
|
||||
// Escape backticks for Go string literal
|
||||
jsonStr := string(jsonBytes)
|
||||
jsonStr = strings.ReplaceAll(jsonStr, "`", "` + \"`\" + `")
|
||||
|
||||
return jsonStr, nil
|
||||
}
|
||||
Reference in New Issue
Block a user