-
Notifications
You must be signed in to change notification settings - Fork 0
fix(security): Enforce blocks, harden auto blocking #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c2bf256
973e2c9
5446c87
0fae8e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package api | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoding/json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "log" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os/exec" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -20,22 +24,37 @@ func (s *Server) ingestSecurityEvent(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var event security.IngestEvent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := c.ShouldBindJSON(&event); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clientIP := c.ClientIP() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Security ingest: failed to parse JSON from %s: %v", clientIP, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secEvent, err := s.securityManager.IngestEvent(&event) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result, err := s.securityManager.IngestEvent(&event, s.config.Security.AutoBlockDuration) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Security ingest: failed to process event from IP %s (path=%s, method=%s): %v", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| event.SourceIP, event.RequestPath, event.RequestMethod, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if secEvent == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result.Event == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusOK, gin.H{"processed": false, "reason": "Event not security-relevant or IP blocked"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusCreated, gin.H{"processed": true, "event": secEvent}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If an IP was auto-blocked, notify nginx immediately | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result.AutoBlocked { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := s.notifyNginxBlockIP(result.BlockedIP, result.BlockTTL); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Warning: failed to notify nginx about auto-blocked IP %s: %v", result.BlockedIP, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusCreated, gin.H{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "processed": true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "event": result.Event, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "auto_blocked": result.AutoBlocked, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // getSecurityStats returns security statistics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -198,6 +217,11 @@ func (s *Server) blockIP(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Notify nginx to immediately block the IP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := s.notifyNginxBlockIP(req.IP, req.Duration); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Warning: failed to notify nginx about blocked IP %s: %v", req.IP, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusCreated, gin.H{"id": id, "message": "IP blocked successfully"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -219,6 +243,11 @@ func (s *Server) unblockIP(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Notify nginx to immediately unblock the IP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := s.notifyNginxUnblockIP(ip); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Warning: failed to notify nginx about unblocked IP %s: %v", ip, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusOK, gin.H{"message": "IP unblocked successfully"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -699,6 +728,69 @@ func (s *Server) updateSecuritySettings(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(http.StatusOK, result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // notifyNginxBlockIP notifies nginx to immediately add an IP to its blocked list | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (s *Server) notifyNginxBlockIP(ip string, ttlSeconds int) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if s.config.Nginx.ContainerName == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("nginx container name not configured") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ttlSeconds <= 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ttlSeconds = 86400 * 365 // 1 year for permanent blocks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload := map[string]interface{}{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ip": ip, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ttl": ttlSeconds, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jsonPayload, err := json.Marshal(payload) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to marshal payload: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd := fmt.Sprintf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `curl -s -X POST -H "Content-Type: application/json" -d '%s' http://127.0.0.1:8081/_internal/security/block-ip`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string(jsonPayload), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd := exec.Command("docker", "exec", s.config.Nginx.ContainerName, "sh", "-c", curlCmd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output, err := cmd.CombinedOutput() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to notify nginx: %s - %w", string(output), err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+743
to
+759
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Additionally, consider defining the internal API path
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Notified nginx to block IP %s (ttl=%ds): %s", ip, ttlSeconds, string(output)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // notifyNginxUnblockIP notifies nginx to immediately remove an IP from its blocked list | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (s *Server) notifyNginxUnblockIP(ip string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if s.config.Nginx.ContainerName == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("nginx container name not configured") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload := map[string]interface{}{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ip": ip, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jsonPayload, err := json.Marshal(payload) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to marshal payload: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd := fmt.Sprintf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `curl -s -X POST -H "Content-Type: application/json" -d '%s' http://127.0.0.1:8081/_internal/security/unblock-ip`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string(jsonPayload), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd := exec.Command("docker", "exec", s.config.Nginx.ContainerName, "sh", "-c", curlCmd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output, err := cmd.CombinedOutput() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to notify nginx: %s - %w", string(output), err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+776
to
+788
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("Notified nginx to unblock IP %s: %s", ip, string(output)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // refreshSecurityScripts regenerates Lua scripts with correct agent IP and reloads nginx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (s *Server) refreshSecurityScripts(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !s.config.Security.Enabled { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -686,11 +686,41 @@ func (m *Manager) CheckSecurityHealth() *SecurityHealthCheck { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_global_traffic_logging"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, "nginx.conf does not have global traffic logging enabled") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for IP blocking shared dict | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.Contains(contentStr, "lua_shared_dict blocked_ips") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_blocked_ips_dict"] = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_blocked_ips_dict"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, "nginx.conf missing lua_shared_dict blocked_ips for IP blocking") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, "Use POST /api/security/refresh to regenerate nginx.conf with IP blocking support") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for IP blocking access check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.Contains(contentStr, "access_by_lua_block") && strings.Contains(contentStr, "security.is_blocked") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_ip_blocking"] = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_ip_blocking"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, "nginx.conf missing access_by_lua_block for IP blocking") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, "Use POST /api/security/refresh to regenerate nginx.conf with IP blocking") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for internal API server block | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.Contains(contentStr, "listen 127.0.0.1:8081") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_internal_api"] = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_internal_api"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, "nginx.conf missing internal API server for instant IP blocking") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, "Use POST /api/security/refresh to regenerate nginx.conf with internal API") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_exists"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_lua_init"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_traffic_module"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_global_traffic_logging"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_blocked_ips_dict"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_ip_blocking"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_conf_has_internal_api"] = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, "nginx.conf does not exist at "+nginxConfPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, "Enable realtime capture in Security settings") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -815,6 +845,16 @@ func (m *Manager) CheckSecurityHealth() *SecurityHealthCheck { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "3. Use POST /api/security/refresh to regenerate scripts with correct IP") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check 9: Internal API (port 8081) is reachable for instant IP blocking | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| internalAPIReachable := m.checkNginxInternalAPIReachable() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Checks["nginx_internal_api_reachable"] = internalAPIReachable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !internalAPIReachable { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Issues = append(result.Issues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Nginx internal API (port 8081) is not responding - instant IP blocking will not work") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.Recommendations = append(result.Recommendations, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Check nginx error logs for Lua errors, ensure nginx.conf has internal server block on 127.0.0.1:8081") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Determine overall status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -824,11 +864,15 @@ func (m *Manager) CheckSecurityHealth() *SecurityHealthCheck { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "traffic_lua_exists", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "traffic_lua_ip_injected", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_conf_has_lua_init", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_conf_has_blocked_ips_dict", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_conf_has_ip_blocking", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_conf_has_internal_api", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_container_running", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_lua_module_loaded", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_conf_mounted", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lua_directory_mounted", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_can_reach_agent", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nginx_internal_api_reachable", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "vhosts_have_security_hook", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -990,6 +1034,17 @@ func (m *Manager) checkNginxCanReachAgent(agentIP string, agentPort int) bool { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return strings.Contains(string(output), "yes") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (m *Manager) checkNginxInternalAPIReachable() bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testCmd := "curl -s --connect-timeout 2 --max-time 5 -X POST http://127.0.0.1:8081/_internal/security/refresh-blocked-ips 2>/dev/null | grep -q success && echo yes || echo no" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd := exec.Command("docker", "exec", m.config.Nginx.ContainerName, "sh", "-c", testCmd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output, err := cmd.Output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return strings.Contains(string(output), "yes") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1037
to
+1046
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // securityVolumeMounts are added when security is enabled and removed when disabled | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var securityVolumeMounts = []string{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "./nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value
86400 * 365represents 1 year in seconds. It's a 'magic number' that would be clearer if defined as a named constant (e.g.,security.PermanentBlockDurationSeconds). This improves readability and makes future modifications easier.