Files
honeydany/app/dashboard/integration.go
T
2025-09-28 21:28:39 +01:00

440 lines
11 KiB
Go

package dashboard
import (
"context"
"encoding/json"
"log"
"strings"
"time"
)
// ThreatManager integrates threat analysis with the main application
type ThreatManager struct {
analyzer *ThreatAnalyzer
api *ThreatAPI
authManager *AuthManager
securityManager *SecurityManager
userAPI *UserAPI
blocklistExporter *BlocklistExporter
webTemplateAPI *WebTemplateAPI
webServicesAPI *WebServicesAPI
}
// NewThreatManager creates a new threat manager instance
func NewThreatManager(dbPath string) (*ThreatManager, error) {
analyzer, err := NewThreatAnalyzer(dbPath)
if err != nil {
return nil, err
}
// Initialize authentication manager with the same database
authManager, err := NewAuthManager(analyzer.db)
if err != nil {
return nil, err
}
// Initialize security manager
securityManager := NewSecurityManager(authManager)
// Initialize APIs
api := NewThreatAPI(analyzer)
userAPI := NewUserAPI(authManager, securityManager)
// Initialize blocklist exporter
blocklistExporter := NewBlocklistExporter(analyzer)
// Initialize web template API
templateManager := NewWebTemplateManager("webtemplates")
webTemplateAPI := NewWebTemplateAPI(templateManager)
// Initialize web services API
configManager := NewConfigManager("config.json")
webServicesAPI := NewWebServicesAPI(configManager)
tm := &ThreatManager{
analyzer: analyzer,
api: api,
authManager: authManager,
securityManager: securityManager,
userAPI: userAPI,
blocklistExporter: blocklistExporter,
webTemplateAPI: webTemplateAPI,
webServicesAPI: webServicesAPI,
}
return tm, nil
}
// ProcessHoneypotRecord processes a honeypot log record for threat analysis
func (tm *ThreatManager) ProcessHoneypotRecord(timestamp time.Time, remoteAddr, remotePort, service string, details map[string]interface{}, rawPayload string) {
// Convert to LogRecord format
record := LogRecord{
IP: remoteAddr,
Service: service,
Timestamp: timestamp,
Details: details,
}
// Add additional analysis data
if record.Details == nil {
record.Details = make(map[string]interface{})
}
record.Details["remote_port"] = remotePort
record.Details["raw_payload_length"] = len(rawPayload)
// Analyze payload for suspicious patterns
if suspiciousPatterns := analyzeSuspiciousPatterns(rawPayload, service); len(suspiciousPatterns) > 0 {
record.Details["suspicious_patterns"] = suspiciousPatterns
}
// Process the record
if err := tm.analyzer.ProcessLogRecord(record); err != nil {
log.Printf("Failed to process log record for threat analysis: %v", err)
}
}
// analyzeSuspiciousPatterns detects suspicious patterns in payloads
func analyzeSuspiciousPatterns(payload, service string) []string {
var patterns []string
// Common attack patterns
suspiciousStrings := []string{
"admin", "root", "administrator", "test", "guest", "user",
"password", "123456", "qwerty", "letmein", "welcome",
"../", "../../", "/etc/passwd", "/etc/shadow",
"<script>", "javascript:", "eval(", "exec(",
"union select", "drop table", "insert into",
"wget", "curl", "nc -", "netcat", "/bin/sh", "/bin/bash",
"cmd.exe", "powershell", "certutil",
}
payloadLower := strings.ToLower(payload)
for _, pattern := range suspiciousStrings {
if strings.Contains(payloadLower, pattern) {
patterns = append(patterns, pattern)
}
}
// Service-specific patterns
switch service {
case "ssh":
sshPatterns := []string{
"SSH-2.0-libssh", "SSH-2.0-PuTTY", "SSH-2.0-OpenSSH",
}
for _, pattern := range sshPatterns {
if strings.Contains(payload, pattern) {
patterns = append(patterns, "ssh_client:"+pattern)
}
}
case "http", "https":
httpPatterns := []string{
"User-Agent:", "Mozilla/", "curl/", "wget/", "python-requests/",
"sqlmap", "nikto", "nmap", "masscan", "zmap",
}
for _, pattern := range httpPatterns {
if strings.Contains(payloadLower, pattern) {
patterns = append(patterns, "http_pattern:"+pattern)
}
}
case "ftp":
ftpPatterns := []string{
"USER anonymous", "USER ftp", "PASS anonymous",
}
for _, pattern := range ftpPatterns {
if strings.Contains(payloadLower, pattern) {
patterns = append(patterns, "ftp_pattern:"+pattern)
}
}
}
return patterns
}
// GetAnalyzer returns the threat analyzer instance
func (tm *ThreatManager) GetAnalyzer() *ThreatAnalyzer {
return tm.analyzer
}
// GetAPI returns the threat API instance
func (tm *ThreatManager) GetAPI() *ThreatAPI {
return tm.api
}
// GetSecurityManager returns the security manager instance
func (tm *ThreatManager) GetSecurityManager() *SecurityManager {
return tm.securityManager
}
// GetUserAPI returns the user API instance
func (tm *ThreatManager) GetUserAPI() *UserAPI {
return tm.userAPI
}
// GetBlocklistExporter returns the blocklist exporter
func (tm *ThreatManager) GetBlocklistExporter() *BlocklistExporter {
return tm.blocklistExporter
}
// GetWebTemplateAPI returns the web template API
func (tm *ThreatManager) GetWebTemplateAPI() *WebTemplateAPI {
return tm.webTemplateAPI
}
// GetWebServicesAPI returns the web services API
func (tm *ThreatManager) GetWebServicesAPI() *WebServicesAPI {
return tm.webServicesAPI
}
// GetBlockedIPs returns currently blocked IPs for firewall integration
func (tm *ThreatManager) GetBlockedIPs() ([]string, error) {
return tm.analyzer.GetBlockedIPs()
}
// IsIPBlocked checks if an IP is currently blocked
func (tm *ThreatManager) IsIPBlocked(ip string) (bool, error) {
report, err := tm.analyzer.AnalyzeIP(ip)
if err != nil {
return false, err
}
return report.IsBlocked, nil
}
// RunPeriodicTasks starts background tasks for threat analysis
func (tm *ThreatManager) RunPeriodicTasks(ctx context.Context) {
// Update threat scores every 5 minutes
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := tm.updateThreatScores(); err != nil {
// Only log if not a context cancellation or database closed error
if ctx.Err() == nil && !strings.Contains(err.Error(), "database is closed") {
log.Printf("Failed to update threat scores: %v", err)
}
}
}
}
}()
// Run rule evaluation every minute
go func() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := tm.evaluateAllRules(); err != nil {
// Only log if not a context cancellation or database closed error
if ctx.Err() == nil && !strings.Contains(err.Error(), "database is closed") {
log.Printf("Failed to evaluate rules: %v", err)
}
}
}
}
}()
}
// updateThreatScores recalculates threat scores for all IPs
func (tm *ThreatManager) updateThreatScores() error {
// Get all IPs from the last 24 hours
query := `SELECT DISTINCT ip FROM ip_analysis WHERE last_seen >= ?`
yesterday := time.Now().Add(-24 * time.Hour)
rows, err := tm.analyzer.db.Query(query, yesterday)
if err != nil {
return err
}
defer rows.Close()
var ips []string
for rows.Next() {
var ip string
if err := rows.Scan(&ip); err != nil {
continue
}
ips = append(ips, ip)
}
// Calculate threat score for each IP
for _, ip := range ips {
score := tm.calculateThreatScore(ip)
// Update the threat score in database
updateQuery := `UPDATE ip_analysis SET threat_score = ?, updated_at = CURRENT_TIMESTAMP WHERE ip = ?`
_, err := tm.analyzer.db.Exec(updateQuery, score, ip)
if err != nil {
log.Printf("Failed to update threat score for IP %s: %v", ip, err)
}
}
return nil
}
// calculateThreatScore calculates a threat score (0-100) for an IP
func (tm *ThreatManager) calculateThreatScore(ip string) int {
score := 0
// Get IP statistics
var connections, authAttempts int
var servicesJSON string
query := `SELECT total_connections, total_auth_attempts, services FROM ip_analysis WHERE ip = ?`
err := tm.analyzer.db.QueryRow(query, ip).Scan(&connections, &authAttempts, &servicesJSON)
if err != nil {
return 0
}
// Base score from connections (max 20 points)
if connections > 100 {
score += 20
} else if connections > 50 {
score += 15
} else if connections > 20 {
score += 10
} else if connections > 10 {
score += 5
}
// Score from auth attempts (max 25 points)
if authAttempts > 50 {
score += 25
} else if authAttempts > 20 {
score += 20
} else if authAttempts > 10 {
score += 15
} else if authAttempts > 5 {
score += 10
}
// Score from service diversity (max 15 points)
var services []string
if servicesJSON != "" {
json.Unmarshal([]byte(servicesJSON), &services)
}
serviceCount := len(services)
if serviceCount > 5 {
score += 15
} else if serviceCount > 3 {
score += 10
} else if serviceCount > 1 {
score += 5
}
// Score from threat events (max 40 points)
var eventCount int
eventQuery := `SELECT COUNT(*) FROM threat_events WHERE ip = ? AND last_seen >= ?`
yesterday := time.Now().Add(-24 * time.Hour)
err = tm.analyzer.db.QueryRow(eventQuery, ip, yesterday).Scan(&eventCount)
if err == nil {
if eventCount > 10 {
score += 40
} else if eventCount > 5 {
score += 30
} else if eventCount > 2 {
score += 20
} else if eventCount > 0 {
score += 10
}
}
// Cap at 100
if score > 100 {
score = 100
}
return score
}
// evaluateAllRules runs all active rules against recent activity
func (tm *ThreatManager) evaluateAllRules() error {
// Get recent log entries (last 5 minutes)
// This would need to be integrated with the actual logging system
// For now, we'll just update existing threat events
rules, err := tm.analyzer.GetRules()
if err != nil {
return err
}
for _, rule := range rules {
if !rule.Enabled {
continue
}
// Evaluate rule against recent activity
if err := tm.evaluateRuleForRecentActivity(rule); err != nil {
log.Printf("Failed to evaluate rule %s: %v", rule.Name, err)
}
}
return nil
}
// evaluateRuleForRecentActivity evaluates a rule against recent activity
func (tm *ThreatManager) evaluateRuleForRecentActivity(rule ThreatRule) error {
timeWindow := time.Now().Add(-time.Duration(rule.TimeWindow) * time.Minute)
// Get IPs with recent activity
query := `SELECT DISTINCT ip FROM ip_analysis WHERE last_seen >= ?`
rows, err := tm.analyzer.db.Query(query, timeWindow)
if err != nil {
return err
}
defer rows.Close()
var ips []string
for rows.Next() {
var ip string
if err := rows.Scan(&ip); err != nil {
continue
}
ips = append(ips, ip)
}
// Evaluate rule for each IP
for _, ip := range ips {
// Create a mock log record for evaluation
record := LogRecord{
IP: ip,
Service: rule.Service,
Timestamp: time.Now(),
Details: make(map[string]interface{}),
}
triggered, err := tm.analyzer.evaluateRule(rule, record)
if err != nil {
continue
}
if triggered {
// Check if we already have a recent event for this IP/rule combination
var existingCount int
checkQuery := `SELECT COUNT(*) FROM threat_events
WHERE ip = ? AND rule_id = ? AND last_seen >= ?`
err := tm.analyzer.db.QueryRow(checkQuery, ip, rule.ID, timeWindow).Scan(&existingCount)
if err == nil && existingCount == 0 {
// Create new threat event
if err := tm.analyzer.createThreatEvent(rule, record); err != nil {
log.Printf("Failed to create threat event: %v", err)
}
}
}
}
return nil
}
// Close closes the threat manager and its resources
func (tm *ThreatManager) Close() error {
return tm.analyzer.Close()
}