Implement geoIP and whois lookups directly from fail2ban-UI

This commit is contained in:
2025-12-15 21:50:19 +01:00
parent 3ad4821cb7
commit c57322e38d
19 changed files with 523 additions and 42 deletions

View File

@@ -70,6 +70,11 @@ type AppSettings struct {
Banaction string `json:"banaction"` // Default banning action
BanactionAllports string `json:"banactionAllports"` // Allports banning action
//Sender string `json:"sender"`
// GeoIP and Whois settings
GeoIPProvider string `json:"geoipProvider"` // "maxmind" or "builtin"
GeoIPDatabasePath string `json:"geoipDatabasePath"` // Path to MaxMind database (optional)
MaxLogLines int `json:"maxLogLines"` // Maximum log lines to include (default: 50)
}
type AdvancedActionsConfig struct {
@@ -172,9 +177,8 @@ actionban = /usr/bin/curl -X POST __CALLBACK_URL__/api/ban \
--arg jail '<name>' \
--arg hostname '<fq-hostname>' \
--arg failures '<failures>' \
--arg whois "$(whois <ip> || echo 'missing whois program')" \
--arg logs "$(tac <logpath> | grep <grepopts> -wF <ip>)" \
'{serverId: $serverId, ip: $ip, jail: $jail, hostname: $hostname, failures: $failures, whois: $whois, logs: $logs}')"
'{serverId: $serverId, ip: $ip, jail: $jail, hostname: $hostname, failures: $failures, logs: $logs}')"
[Init]
@@ -458,6 +462,9 @@ func toAppSettingsRecordLocked() (storage.AppSettingsRecord, error) {
Banaction: currentSettings.Banaction,
BanactionAllports: currentSettings.BanactionAllports,
AdvancedActionsJSON: string(advancedBytes),
GeoIPProvider: currentSettings.GeoIPProvider,
GeoIPDatabasePath: currentSettings.GeoIPDatabasePath,
MaxLogLines: currentSettings.MaxLogLines,
}, nil
}
@@ -577,6 +584,15 @@ func setDefaultsLocked() {
if currentSettings.BanactionAllports == "" {
currentSettings.BanactionAllports = "iptables-allports"
}
if currentSettings.GeoIPProvider == "" {
currentSettings.GeoIPProvider = "builtin"
}
if currentSettings.GeoIPDatabasePath == "" {
currentSettings.GeoIPDatabasePath = "/usr/share/GeoIP/GeoLite2-Country.mmdb"
}
if currentSettings.MaxLogLines == 0 {
currentSettings.MaxLogLines = 50
}
if (currentSettings.AdvancedActions == AdvancedActionsConfig{}) {
currentSettings.AdvancedActions = defaultAdvancedActionsConfig()

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Standard-Maximalversuche",
"settings.default_max_retry.description": "Anzahl der Fehler, bevor ein Host gesperrt wird.",
"settings.default_max_retry_placeholder": "Geben Sie die maximale Anzahl der Versuche ein",
"settings.geoip_provider": "GeoIP-Anbieter",
"settings.geoip_provider.description": "Wählen Sie den GeoIP-Lookup-Anbieter. MaxMind erfordert eine lokale Datenbankdatei, während Built-in eine kostenlose Online-API verwendet.",
"settings.geoip_provider.maxmind": "MaxMind (Lokale Datenbank)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "GeoIP-Datenbankpfad",
"settings.geoip_database_path.description": "Pfad zur MaxMind GeoLite2-Country-Datenbankdatei.",
"settings.max_log_lines": "Maximale Log-Zeilen",
"settings.max_log_lines.description": "Maximale Anzahl von Log-Zeilen, die in Ban-Benachrichtigungen enthalten sein sollen. Die relevantesten Zeilen werden automatisch ausgewählt.",
"settings.ignore_ips": "IP-Adressen ignorieren",
"settings.ignore_ips.description": "Durch Leerzeichen getrennte Liste von IP-Adressen, CIDR-Masken oder DNS-Hosts. Fail2ban wird keinen Host sperren, der mit einer Adresse in dieser Liste übereinstimmt.",
"settings.ignore_ips_placeholder": "IP-Adressen, getrennt durch Leerzeichen",

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Standard-Maximalversüech",
"settings.default_max_retry.description": "Aazahl vo de Fähler, bevor ä Host gsperrt wird.",
"settings.default_max_retry_placeholder": "Gib d'maximal Versüech ii",
"settings.geoip_provider": "GeoIP-Aabieter",
"settings.geoip_provider.description": "Wähl di GeoIP-Aabieter. MaxMind brucht e lokali Datenbankdatei, während Built-in e gratis Online-API verwendet.",
"settings.geoip_provider.maxmind": "MaxMind (Lokali Datenbank)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "GeoIP-Datenbankpfad",
"settings.geoip_database_path.description": "Pfad zur MaxMind GeoLite2-Country-Datebank.",
"settings.max_log_lines": "Maximali Log-Zeile",
"settings.max_log_lines.description": "Maximali Aazahl vo Log-Zeile, wo i Ban-Benachrichtigunge enthalte si söll. Di relevanteschte Zeile werdet automatisch usgwählt.",
"settings.ignore_ips": "IPs ignorierä",
"settings.ignore_ips.description": "Dur Leerzeichä trennti Lischte vo IP-Adrässe, CIDR-Maske oder DNS-Hosts. Fail2ban wird kei Host sperre, wo mit ere Adrässe i dere Lischte übereistimmt.",
"settings.ignore_ips_placeholder": "IPs, getrennt dur e Leerzeichä",

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Default Max Retry",
"settings.default_max_retry.description": "Number of failures before a host gets banned.",
"settings.default_max_retry_placeholder": "Enter maximum retries",
"settings.geoip_provider": "GeoIP Provider",
"settings.geoip_provider.description": "Choose the GeoIP lookup provider. MaxMind requires a local database file, while Built-in uses a free online API.",
"settings.geoip_provider.maxmind": "MaxMind (Local Database)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "GeoIP Database Path",
"settings.geoip_database_path.description": "Path to the MaxMind GeoLite2-Country database file.",
"settings.max_log_lines": "Maximum Log Lines",
"settings.max_log_lines.description": "Maximum number of log lines to include in ban notifications. Most relevant lines are selected automatically.",
"settings.ignore_ips": "Ignore IPs",
"settings.ignore_ips.description": "Space separated list of IP addresses, CIDR masks or DNS hosts. Fail2ban will not ban a host which matches an address in this list.",
"settings.ignore_ips_placeholder": "IPs to ignore, separated by spaces",

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Número máximo de reintentos por defecto",
"settings.default_max_retry.description": "Número de fallos antes de que un host sea bloqueado.",
"settings.default_max_retry_placeholder": "Introduce el número máximo de reintentos",
"settings.geoip_provider": "Proveedor de GeoIP",
"settings.geoip_provider.description": "Elija el proveedor de consulta GeoIP. MaxMind requiere un archivo de base de datos local, mientras que Built-in utiliza una API en línea gratuita.",
"settings.geoip_provider.maxmind": "MaxMind (Base de Datos Local)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "Ruta de la Base de Datos GeoIP",
"settings.geoip_database_path.description": "Ruta al archivo de base de datos MaxMind GeoLite2-Country.",
"settings.max_log_lines": "Líneas de Log Máximas",
"settings.max_log_lines.description": "Número máximo de líneas de log a incluir en las notificaciones de bloqueo. Las líneas más relevantes se seleccionan automáticamente.",
"settings.ignore_ips": "Ignorar IPs",
"settings.ignore_ips.description": "Lista separada por espacios de direcciones IP, máscaras CIDR o hosts DNS. Fail2ban no bloqueará un host que coincida con una dirección en esta lista.",
"settings.ignore_ips_placeholder": "IPs a ignorar, separadas por espacios",

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Nombre maximal de réessais par défaut",
"settings.default_max_retry.description": "Nombre d'échecs avant qu'un hôte ne soit banni.",
"settings.default_max_retry_placeholder": "Entrez le nombre maximal de réessais",
"settings.geoip_provider": "Fournisseur GeoIP",
"settings.geoip_provider.description": "Choisissez le fournisseur de recherche GeoIP. MaxMind nécessite un fichier de base de données local, tandis que Built-in utilise une API en ligne gratuite.",
"settings.geoip_provider.maxmind": "MaxMind (Base de Données Locale)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "Chemin de la Base de Données GeoIP",
"settings.geoip_database_path.description": "Chemin vers le fichier de base de données MaxMind GeoLite2-Country.",
"settings.max_log_lines": "Lignes de Log Maximales",
"settings.max_log_lines.description": "Nombre maximal de lignes de log à inclure dans les notifications de bannissement. Les lignes les plus pertinentes sont sélectionnées automatiquement.",
"settings.ignore_ips": "Ignorer les IPs",
"settings.ignore_ips.description": "Liste séparée par des espaces d'adresses IP, de masques CIDR ou d'hôtes DNS. Fail2ban ne bannira pas un hôte qui correspond à une adresse de cette liste.",
"settings.ignore_ips_placeholder": "IPs à ignorer, séparées par des espaces",

View File

@@ -138,6 +138,14 @@
"settings.default_max_retry": "Numero massimo di tentativi predefinito",
"settings.default_max_retry.description": "Numero di errori prima che un host venga bannato.",
"settings.default_max_retry_placeholder": "Inserisci il numero massimo di tentativi",
"settings.geoip_provider": "Provider GeoIP",
"settings.geoip_provider.description": "Scegli il provider di ricerca GeoIP. MaxMind richiede un file di database locale, mentre Built-in utilizza un'API online gratuita.",
"settings.geoip_provider.maxmind": "MaxMind (Database Locale)",
"settings.geoip_provider.builtin": "Built-in (ip-api.com)",
"settings.geoip_database_path": "Percorso Database GeoIP",
"settings.geoip_database_path.description": "Percorso al file del database MaxMind GeoLite2-Country.",
"settings.max_log_lines": "Righe di Log Massime",
"settings.max_log_lines.description": "Numero massimo di righe di log da includere nelle notifiche di ban. Le righe più rilevanti vengono selezionate automaticamente.",
"settings.ignore_ips": "Ignora IP",
"settings.ignore_ips.description": "Elenco separato da spazi di indirizzi IP, maschere CIDR o host DNS. Fail2ban non bannerà un host che corrisponde a un indirizzo in questo elenco.",
"settings.ignore_ips_placeholder": "IP da ignorare, separate da spazi",

View File

@@ -70,6 +70,9 @@ type AppSettingsRecord struct {
Banaction string
BanactionAllports string
AdvancedActionsJSON string
GeoIPProvider string
GeoIPDatabasePath string
MaxLogLines int
}
type ServerRecord struct {
@@ -171,17 +174,17 @@ func GetAppSettings(ctx context.Context) (AppSettingsRecord, bool, error) {
}
row := db.QueryRowContext(ctx, `
SELECT language, port, debug, callback_url, restart_needed, alert_countries, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions
SELECT language, port, debug, callback_url, restart_needed, alert_countries, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines
FROM app_settings
WHERE id = 1`)
var (
lang, callback, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, advancedActions sql.NullString
port, smtpPort, maxretry sql.NullInt64
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn sql.NullInt64
lang, callback, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, advancedActions, geoipProvider, geoipDatabasePath sql.NullString
port, smtpPort, maxretry, maxLogLines sql.NullInt64
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn sql.NullInt64
)
err := row.Scan(&lang, &port, &debug, &callback, &restartNeeded, &alerts, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions)
err := row.Scan(&lang, &port, &debug, &callback, &restartNeeded, &alerts, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions, &geoipProvider, &geoipDatabasePath, &maxLogLines)
if errors.Is(err, sql.ErrNoRows) {
return AppSettingsRecord{}, false, nil
}
@@ -212,6 +215,9 @@ WHERE id = 1`)
Banaction: stringFromNull(banaction),
BanactionAllports: stringFromNull(banactionAllports),
AdvancedActionsJSON: stringFromNull(advancedActions),
GeoIPProvider: stringFromNull(geoipProvider),
GeoIPDatabasePath: stringFromNull(geoipDatabasePath),
MaxLogLines: intFromNull(maxLogLines),
}
return rec, true, nil
@@ -223,9 +229,9 @@ func SaveAppSettings(ctx context.Context, rec AppSettingsRecord) error {
}
_, err := db.ExecContext(ctx, `
INSERT INTO app_settings (
id, language, port, debug, callback_url, restart_needed, alert_countries, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions
id, language, port, debug, callback_url, restart_needed, alert_countries, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines
) VALUES (
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
) ON CONFLICT(id) DO UPDATE SET
language = excluded.language,
port = excluded.port,
@@ -248,7 +254,10 @@ INSERT INTO app_settings (
destemail = excluded.destemail,
banaction = excluded.banaction,
banaction_allports = excluded.banaction_allports,
advanced_actions = excluded.advanced_actions
advanced_actions = excluded.advanced_actions,
geoip_provider = excluded.geoip_provider,
geoip_database_path = excluded.geoip_database_path,
max_log_lines = excluded.max_log_lines
`, rec.Language,
rec.Port,
boolToInt(rec.Debug),
@@ -271,6 +280,9 @@ INSERT INTO app_settings (
rec.Banaction,
rec.BanactionAllports,
rec.AdvancedActionsJSON,
rec.GeoIPProvider,
rec.GeoIPDatabasePath,
rec.MaxLogLines,
)
return err
}
@@ -451,7 +463,7 @@ INSERT INTO ban_events (
server_id, server_name, jail, ip, country, hostname, failures, whois, logs, occurred_at, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
result, err := db.ExecContext(
_, err := db.ExecContext(
ctx,
query,
record.ServerID,
@@ -470,12 +482,6 @@ INSERT INTO ban_events (
return err
}
// Get the inserted ID
id, err := result.LastInsertId()
if err == nil {
record.ID = id
}
return nil
}
@@ -797,7 +803,10 @@ CREATE TABLE IF NOT EXISTS app_settings (
destemail TEXT,
banaction TEXT,
banaction_allports TEXT,
advanced_actions TEXT
advanced_actions TEXT,
geoip_provider TEXT,
geoip_database_path TEXT,
max_log_lines INTEGER
);
CREATE TABLE IF NOT EXISTS servers (
@@ -884,6 +893,38 @@ CREATE INDEX IF NOT EXISTS idx_perm_blocks_status ON permanent_blocks(status);
}
}
// Add geoip_provider column
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN geoip_provider TEXT`); err != nil {
if !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
// Add geoip_database_path column
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN geoip_database_path TEXT`); err != nil {
if !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
// Add max_log_lines column
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN max_log_lines INTEGER`); err != nil {
if !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
// Set default values for new columns if they are NULL
if _, err := db.ExecContext(ctx, `UPDATE app_settings SET geoip_provider = 'maxmind' WHERE geoip_provider IS NULL`); err != nil {
log.Printf("Warning: Failed to set default value for geoip_provider: %v", err)
}
if _, err := db.ExecContext(ctx, `UPDATE app_settings SET geoip_database_path = '/usr/share/GeoIP/GeoLite2-Country.mmdb' WHERE geoip_database_path IS NULL`); err != nil {
log.Printf("Warning: Failed to set default value for geoip_database_path: %v", err)
}
if _, err := db.ExecContext(ctx, `UPDATE app_settings SET max_log_lines = 50 WHERE max_log_lines IS NULL OR max_log_lines = 0`); err != nil {
log.Printf("Warning: Failed to set default value for max_log_lines: %v", err)
}
return nil
}