mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-06 05:52:19 +08:00
feat(security): 快照并持久化告警时的连接跟踪数据
This commit is contained in:
@@ -180,6 +180,12 @@ func (ss *SecurityScanner) monitorLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SecurityScanner) alertCount() int {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
|
return len(ss.alerts)
|
||||||
|
}
|
||||||
|
|
||||||
func (ss *SecurityScanner) checkAllContainers() {
|
func (ss *SecurityScanner) checkAllContainers() {
|
||||||
for _, c := range config.AppConfig.Containers {
|
for _, c := range config.AppConfig.Containers {
|
||||||
if c.Status != "running" || c.IP == "" {
|
if c.Status != "running" || c.IP == "" {
|
||||||
@@ -208,6 +214,7 @@ func (ss *SecurityScanner) checkContainer(name, ip string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alertBefore := ss.alertCount()
|
||||||
ss.detectPortScans(name, ip, stats)
|
ss.detectPortScans(name, ip, stats)
|
||||||
ss.detectBruteForce(name, ip, stats)
|
ss.detectBruteForce(name, ip, stats)
|
||||||
ss.detectSpam(name, ip, stats)
|
ss.detectSpam(name, ip, stats)
|
||||||
@@ -216,6 +223,11 @@ func (ss *SecurityScanner) checkContainer(name, ip string) {
|
|||||||
ss.detectMining(name, ip, stats)
|
ss.detectMining(name, ip, stats)
|
||||||
ss.detectProxyAndTor(name, ip, stats)
|
ss.detectProxyAndTor(name, ip, stats)
|
||||||
ss.detectMalware(name, ip, stats)
|
ss.detectMalware(name, ip, stats)
|
||||||
|
|
||||||
|
// If new alerts were generated, snapshot the conntrack data for later retrieval.
|
||||||
|
if ss.alertCount() > alertBefore {
|
||||||
|
config.SaveConntrackSnapshot(ip, lines)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTrafficStats() *trafficStats {
|
func newTrafficStats() *trafficStats {
|
||||||
@@ -759,28 +771,49 @@ func HandleSecurityLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func getConnectionLogs(ip string) []map[string]interface{} {
|
func getConnectionLogs(ip string) []map[string]interface{} {
|
||||||
logs := make([]map[string]interface{}, 0)
|
logs := make([]map[string]interface{}, 0)
|
||||||
|
seen := map[string]bool{}
|
||||||
|
|
||||||
for _, line := range readConntrackLines(ip) {
|
parseLine := func(line string) map[string]interface{} {
|
||||||
srcIP := extractField(line, "src=")
|
srcIP := extractField(line, "src=")
|
||||||
dstIP := extractField(line, "dst=")
|
dstIP := extractField(line, "dst=")
|
||||||
srcPort := extractField(line, "sport=")
|
srcPort := extractField(line, "sport=")
|
||||||
dstPort := extractField(line, "dport=")
|
dstPort := extractField(line, "dport=")
|
||||||
|
|
||||||
sPort, _ := strconv.Atoi(srcPort)
|
sPort, _ := strconv.Atoi(srcPort)
|
||||||
dPort, _ := strconv.Atoi(dstPort)
|
dPort, _ := strconv.Atoi(dstPort)
|
||||||
|
return map[string]interface{}{
|
||||||
logs = append(logs, map[string]interface{}{
|
|
||||||
"src_ip": srcIP,
|
"src_ip": srcIP,
|
||||||
"dst_ip": dstIP,
|
"dst_ip": dstIP,
|
||||||
"src_port": sPort,
|
"src_port": sPort,
|
||||||
"dst_port": dPort,
|
"dst_port": dPort,
|
||||||
"protocol": extractProtocol(line),
|
"protocol": extractProtocol(line),
|
||||||
"state": extractConnState(line),
|
"state": extractConnState(line),
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, load stored snapshots from database (persisted at alert time).
|
||||||
|
for _, line := range config.GetConntrackSnapshotLines(ip) {
|
||||||
if len(logs) >= 100 {
|
if len(logs) >= 100 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
key := strings.TrimSpace(line)
|
||||||
|
if key == "" || seen[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
logs = append(logs, parseLine(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, merge live conntrack data (deduplicated).
|
||||||
|
for _, line := range readConntrackLines(ip) {
|
||||||
|
if len(logs) >= 100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
key := strings.TrimSpace(line)
|
||||||
|
if key == "" || seen[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
logs = append(logs, parseLine(line))
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs
|
return logs
|
||||||
|
|||||||
@@ -254,6 +254,14 @@ func ensureSchema() error {
|
|||||||
success INTEGER,
|
success INTEGER,
|
||||||
error TEXT
|
error TEXT
|
||||||
)`,
|
)`,
|
||||||
|
`CREATE TABLE IF NOT EXISTS security_conntrack_snapshots (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
container_ip TEXT NOT NULL,
|
||||||
|
line TEXT NOT NULL,
|
||||||
|
captured_at TEXT NOT NULL
|
||||||
|
)`,
|
||||||
|
`CREATE INDEX IF NOT EXISTS idx_conntrack_snapshots_ip_time
|
||||||
|
ON security_conntrack_snapshots(container_ip, captured_at)`,
|
||||||
`CREATE TABLE IF NOT EXISTS tasks (
|
`CREATE TABLE IF NOT EXISTS tasks (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
type TEXT,
|
type TEXT,
|
||||||
@@ -642,6 +650,59 @@ func saveAPIKeys(tx *sql.Tx) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveConntrackSnapshot stores raw conntrack lines for a container IP.
|
||||||
|
func SaveConntrackSnapshot(containerIP string, lines []string) {
|
||||||
|
if db == nil || len(lines) == 0 || strings.TrimSpace(containerIP) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
stmt, err := tx.Prepare(`INSERT INTO security_conntrack_snapshots (container_ip, line, captured_at) VALUES (?, ?, ?)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stmt.Exec(containerIP, line, now)
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
|
// Cleanup old snapshots (>1 hour)
|
||||||
|
db.Exec(`DELETE FROM security_conntrack_snapshots WHERE captured_at < ?`,
|
||||||
|
time.Now().Add(-1*time.Hour).Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConntrackSnapshotLines returns stored conntrack lines for a container IP.
|
||||||
|
func GetConntrackSnapshotLines(containerIP string) []string {
|
||||||
|
if db == nil || strings.TrimSpace(containerIP) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rows, err := db.Query(
|
||||||
|
`SELECT line FROM security_conntrack_snapshots WHERE container_ip = ? ORDER BY captured_at DESC LIMIT 200`,
|
||||||
|
containerIP,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var lines []string
|
||||||
|
for rows.Next() {
|
||||||
|
var line string
|
||||||
|
if rows.Scan(&line) == nil {
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
func saveAuditLogs(tx *sql.Tx) error {
|
func saveAuditLogs(tx *sql.Tx) error {
|
||||||
for _, log := range AppConfig.AuditLogs {
|
for _, log := range AppConfig.AuditLogs {
|
||||||
successSet := 0
|
successSet := 0
|
||||||
|
|||||||
Reference in New Issue
Block a user