fix(security): A-3 auth middleware coverage fixes
Fixes 9 auth middleware findings from the A-3 recon audit. F-A3-11 CRITICAL: Removed JWT secret from WebAuthMiddleware log output. Replaced emoji-prefixed fmt.Printf with ETHOS-compliant log.Printf. No secret values in any log output. F-A3-7 CRITICAL: Config download now requires WebAuthMiddleware. GET /downloads/config/:agent_id is admin-only (agents never call it). F-A3-6 HIGH: Update package download now requires AuthMiddleware. GET /downloads/updates/:package_id requires valid agent JWT. F-A3-10 HIGH: Scheduler stats changed from AuthMiddleware to WebAuthMiddleware. Agent JWTs can no longer view scheduler internals. F-A3-13 LOW: RequireAdmin() middleware implemented. 7 security settings routes re-enabled (GET/PUT/POST under /security/settings). security_settings.go.broken renamed to .go, API mismatches fixed. F-A3-12 MEDIUM: JWT issuer claims added for token type separation. Agent tokens: issuer=redflag-agent, Web tokens: issuer=redflag-web. AuthMiddleware rejects tokens with wrong issuer. Grace period: tokens with no issuer still accepted (backward compat). F-A3-2 MEDIUM: /auth/verify now has WebAuthMiddleware applied. Endpoint returns 200 with valid=true for valid admin tokens. F-A3-9 MEDIUM: Agent self-unregister (DELETE /:id) now rate-limited using the same agent_reports rate limiter as other agent routes. F-A3-14 LOW: CORS origin configurable via REDFLAG_CORS_ORIGIN env var. Defaults to http://localhost:3000 for development. Added PATCH method and agent-specific headers to CORS config. All 27 server tests pass. All 14 agent tests pass. No regressions. See docs/A3_Fix_Implementation.md and docs/Deviations_Report.md (DEV-020 through DEV-022). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -365,6 +365,13 @@ func main() {
|
||||
} else {
|
||||
log.Printf("[OK] Security settings service initialized")
|
||||
}
|
||||
|
||||
// Initialize security settings handler (F-A3-13: re-enable security settings routes)
|
||||
var securitySettingsHandler *handlers.SecuritySettingsHandler
|
||||
if securitySettingsService != nil {
|
||||
securitySettingsHandler = handlers.NewSecuritySettingsHandler(securitySettingsService)
|
||||
}
|
||||
|
||||
// Setup router
|
||||
router := gin.Default()
|
||||
|
||||
@@ -385,7 +392,7 @@ func main() {
|
||||
// Authentication routes (with rate limiting)
|
||||
api.POST("/auth/login", rateLimiter.RateLimit("public_access", middleware.KeyByIP), authHandler.Login)
|
||||
api.POST("/auth/logout", authHandler.Logout)
|
||||
api.GET("/auth/verify", authHandler.VerifyToken)
|
||||
api.GET("/auth/verify", authHandler.WebAuthMiddleware(), authHandler.VerifyToken)
|
||||
|
||||
// Public system routes (no authentication required)
|
||||
api.GET("/public-key", rateLimiter.RateLimit("public_access", middleware.KeyByIP), systemHandler.GetPublicKey)
|
||||
@@ -406,11 +413,13 @@ func main() {
|
||||
buildRoutes.POST("/detect", rateLimiter.RateLimit("agent_build", middleware.KeyByAgentID), handlers.DetectAgentInstallation)
|
||||
}
|
||||
|
||||
// Public download routes (no authentication - agents need these!)
|
||||
// Public download routes (agent binaries and install scripts remain public for bootstrapping)
|
||||
api.GET("/downloads/:platform", rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.DownloadAgent)
|
||||
api.GET("/downloads/updates/:package_id", rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.DownloadUpdatePackage)
|
||||
api.GET("/downloads/config/:agent_id", rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.HandleConfigDownload)
|
||||
api.GET("/install/:platform", rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.InstallScript)
|
||||
|
||||
// Protected download routes (F-A3-6, F-A3-7: require authentication)
|
||||
api.GET("/downloads/updates/:package_id", middleware.AuthMiddleware(), rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.DownloadUpdatePackage)
|
||||
api.GET("/downloads/config/:agent_id", authHandler.WebAuthMiddleware(), rateLimiter.RateLimit("public_access", middleware.KeyByIP), downloadHandler.HandleConfigDownload)
|
||||
}
|
||||
|
||||
// Start background goroutine to mark offline agents
|
||||
@@ -476,7 +485,7 @@ func main() {
|
||||
}
|
||||
verificationHandler.VerifySignature(c)
|
||||
})
|
||||
agents.DELETE("/:id", agentHandler.UnregisterAgent)
|
||||
agents.DELETE("/:id", rateLimiter.RateLimit("agent_reports", middleware.KeyByAgentID), agentHandler.UnregisterAgent)
|
||||
|
||||
// New dedicated endpoints for metrics and docker images (data classification fix)
|
||||
agents.POST("/:id/metrics", rateLimiter.RateLimit("agent_reports", middleware.KeyByAgentID), metricsHandler.ReportMetrics)
|
||||
@@ -597,17 +606,20 @@ func main() {
|
||||
dashboard.GET("/security/metrics", securityHandler.SecurityMetrics)
|
||||
|
||||
// Security Settings Management endpoints (admin-only)
|
||||
// securitySettings := dashboard.Group("/security/settings")
|
||||
// securitySettings.Use(middleware.RequireAdmin())
|
||||
// {
|
||||
// securitySettings.GET("", securitySettingsHandler.GetAllSecuritySettings)
|
||||
// securitySettings.GET("/audit", securitySettingsHandler.GetSecurityAuditTrail)
|
||||
// securitySettings.GET("/overview", securitySettingsHandler.GetSecurityOverview)
|
||||
// securitySettings.GET("/:category", securitySettingsHandler.GetSecuritySettingsByCategory)
|
||||
// securitySettings.PUT("/:category/:key", securitySettingsHandler.UpdateSecuritySetting)
|
||||
// securitySettings.POST("/validate", securitySettingsHandler.ValidateSecuritySettings)
|
||||
// securitySettings.POST("/apply", securitySettingsHandler.ApplySecuritySettings)
|
||||
// }
|
||||
// F-A3-13 fix: RequireAdmin() middleware implemented, routes re-enabled
|
||||
if securitySettingsHandler != nil {
|
||||
securitySettings := dashboard.Group("/security/settings")
|
||||
securitySettings.Use(middleware.RequireAdmin())
|
||||
{
|
||||
securitySettings.GET("", securitySettingsHandler.GetAllSecuritySettings)
|
||||
securitySettings.GET("/audit", securitySettingsHandler.GetSecurityAuditTrail)
|
||||
securitySettings.GET("/overview", securitySettingsHandler.GetSecurityOverview)
|
||||
securitySettings.GET("/:category", securitySettingsHandler.GetSecuritySettingsByCategory)
|
||||
securitySettings.PUT("/:category/:key", securitySettingsHandler.UpdateSecuritySetting)
|
||||
securitySettings.POST("/validate", securitySettingsHandler.ValidateSecuritySettings)
|
||||
securitySettings.POST("/apply", securitySettingsHandler.ApplySecuritySettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load subsystems into queue
|
||||
@@ -624,7 +636,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Add scheduler stats endpoint (after scheduler is initialized)
|
||||
router.GET("/api/v1/scheduler/stats", middleware.AuthMiddleware(), func(c *gin.Context) {
|
||||
// F-A3-10 fix: use WebAuthMiddleware (admin only), not AuthMiddleware (agent JWT)
|
||||
router.GET("/api/v1/scheduler/stats", authHandler.WebAuthMiddleware(), func(c *gin.Context) {
|
||||
stats := subsystemScheduler.GetStats()
|
||||
queueStats := subsystemScheduler.GetQueueStats()
|
||||
c.JSON(200, gin.H{
|
||||
|
||||
Reference in New Issue
Block a user