Files
kor-elf-shield/internal/daemon/analyzer/log/analysis/rules_index.go
T
kor-elf 097cf362e3 Add brute force protection core logic and SSH-specific rules
- Integrated brute force protection mechanisms into the analyzer.
- Added `BruteForceProtection` and `BruteForceProtectionGroup` structures with rate-limiting and group-based blocking logic.
- Implemented IP blocking via the firewall service.
- Introduced SSH brute force detection rules and notifications for detected attempts.
- Updated analyzer and firewall services to handle brute force protection rules.
- Localized new brute force protection alert messages.
2026-02-26 00:01:06 +05:00

119 lines
2.9 KiB
Go

package analysis
import (
"errors"
"fmt"
config2 "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config/brute_force_protection"
)
type RulesIndex struct {
byKey map[indexKey]RulesBucket
}
type indexKey struct {
source config2.SourceType
val string
}
func (idx *RulesIndex) Add(source *config2.Source) error {
if source.AlertRule == nil && source.BruteForceProtectionRule == nil {
return fmt.Errorf("no alert rule or brute force protection rule")
}
key, err := generateIndexKeyBySource(source)
if err != nil {
return err
}
if _, ok := idx.byKey[key]; !ok {
idx.byKey[key] = newRulesBucket()
}
if source.AlertRule != nil {
idx.byKey[key].addAlertRule(source.AlertRule)
}
if source.BruteForceProtectionRule != nil {
idx.byKey[key].addBruteForceProtectionRule(source.BruteForceProtectionRule)
}
return nil
}
func (idx *RulesIndex) Alerts(entry *Entry) ([]*config2.AlertRule, error) {
rules := make([]*config2.AlertRule, 0)
keys, err := generateIndexKeysByEntry(entry)
if err != nil {
return rules, err
}
for _, key := range keys {
b, ok := idx.byKey[key]
if !ok {
continue
}
rules = append(rules, b.Alerts()...)
}
return rules, nil
}
func (idx *RulesIndex) BruteForceProtections(entry *Entry) ([]*brute_force_protection.Rule, error) {
rules := make([]*brute_force_protection.Rule, 0)
keys, err := generateIndexKeysByEntry(entry)
if err != nil {
return rules, err
}
for _, key := range keys {
b, ok := idx.byKey[key]
if !ok {
continue
}
rules = append(rules, b.BruteForceProtectionRules()...)
}
return rules, nil
}
func NewRulesIndex() *RulesIndex {
return &RulesIndex{byKey: make(map[indexKey]RulesBucket)}
}
func generateIndexKeyBySource(source *config2.Source) (indexKey, error) {
switch source.Type {
case config2.SourceTypeJournal:
match := source.Journal.JournalctlMatch()
if source.Journal.Field == "" || source.Journal.Match == "" {
return indexKey{}, errors.New("journalctl match is empty")
}
return indexKey{source: source.Type, val: match}, nil
case config2.SourceTypeFile:
return indexKey{source: source.Type, val: source.File.Path}, nil
}
return indexKey{}, errors.New(fmt.Sprintf("unknown source type: %s", source.Type))
}
func generateIndexKeysByEntry(entry *Entry) ([]indexKey, error) {
var keys []indexKey
switch entry.Source {
case config2.SourceTypeJournal:
keys = append(keys, indexKey{source: entry.Source, val: string(config2.JournalFieldSystemdUnit) + "=" + entry.Unit})
keys = append(keys, indexKey{source: entry.Source, val: string(config2.JournalFieldSyslogIdentifier) + "=" + entry.SyslogIdentifier})
return keys, nil
case config2.SourceTypeFile:
keys = append(keys, indexKey{source: entry.Source, val: entry.File})
return keys, nil
}
return []indexKey{}, errors.New(fmt.Sprintf("unknown source type: %s", entry.Source))
}