mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-03-21 17:13:26 +01:00
Add default chain and default BantimeRndtime settings and make configurable through the settings page
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -33,4 +33,7 @@ fail2ban-ui.db*
|
||||
# Node.js / Tailwind CSS build
|
||||
node_modules/
|
||||
package-lock.json
|
||||
.tailwind-build/
|
||||
.tailwind-build/
|
||||
|
||||
# Server specific test-files
|
||||
server
|
||||
@@ -74,6 +74,8 @@ type AppSettings struct {
|
||||
Destemail string `json:"destemail"`
|
||||
Banaction string `json:"banaction"` // Default banning action
|
||||
BanactionAllports string `json:"banactionAllports"` // Allports banning action
|
||||
Chain string `json:"chain"` // Default iptables/nftables chain (INPUT, DOCKER-USER, FORWARD)
|
||||
BantimeRndtime string `json:"bantimeRndtime"` // Optional: bantime.rndtime in seconds (e.g. 2048) for bantime increment formula
|
||||
//Sender string `json:"sender"`
|
||||
|
||||
// GeoIP and Whois settings
|
||||
@@ -414,6 +416,12 @@ func applyAppSettingsRecordLocked(rec storage.AppSettingsRecord) {
|
||||
currentSettings.Destemail = rec.DestEmail
|
||||
currentSettings.Banaction = rec.Banaction
|
||||
currentSettings.BanactionAllports = rec.BanactionAllports
|
||||
if rec.Chain != "" {
|
||||
currentSettings.Chain = rec.Chain
|
||||
} else {
|
||||
currentSettings.Chain = "INPUT"
|
||||
}
|
||||
currentSettings.BantimeRndtime = rec.BantimeRndtime
|
||||
currentSettings.SMTP = SMTPSettings{
|
||||
Host: rec.SMTPHost,
|
||||
Port: rec.SMTPPort,
|
||||
@@ -527,6 +535,8 @@ func toAppSettingsRecordLocked() (storage.AppSettingsRecord, error) {
|
||||
DestEmail: currentSettings.Destemail,
|
||||
Banaction: currentSettings.Banaction,
|
||||
BanactionAllports: currentSettings.BanactionAllports,
|
||||
Chain: currentSettings.Chain,
|
||||
BantimeRndtime: currentSettings.BantimeRndtime,
|
||||
// Advanced features
|
||||
AdvancedActionsJSON: string(advancedBytes),
|
||||
GeoIPProvider: currentSettings.GeoIPProvider,
|
||||
@@ -672,6 +682,9 @@ func setDefaultsLocked() {
|
||||
if currentSettings.BanactionAllports == "" {
|
||||
currentSettings.BanactionAllports = "nftables-allports"
|
||||
}
|
||||
if currentSettings.Chain == "" {
|
||||
currentSettings.Chain = "INPUT"
|
||||
}
|
||||
if currentSettings.GeoIPProvider == "" {
|
||||
currentSettings.GeoIPProvider = "builtin"
|
||||
}
|
||||
@@ -739,6 +752,12 @@ func initializeFromJailFile() error {
|
||||
if val, ok := settings["banaction_allports"]; ok {
|
||||
currentSettings.BanactionAllports = val
|
||||
}
|
||||
if val, ok := settings["chain"]; ok && val != "" {
|
||||
currentSettings.Chain = val
|
||||
}
|
||||
if val, ok := settings["bantime.rndtime"]; ok && val != "" {
|
||||
currentSettings.BantimeRndtime = val
|
||||
}
|
||||
/*if val, ok := settings["destemail"]; ok {
|
||||
currentSettings.Destemail = val
|
||||
}*/
|
||||
@@ -911,6 +930,10 @@ func ensureJailLocalStructure() error {
|
||||
if banactionAllports == "" {
|
||||
banactionAllports = "nftables-allports"
|
||||
}
|
||||
chain := settings.Chain
|
||||
if chain == "" {
|
||||
chain = "INPUT"
|
||||
}
|
||||
defaultSection := fmt.Sprintf(`[DEFAULT]
|
||||
enabled = %t
|
||||
bantime.increment = %t
|
||||
@@ -920,8 +943,13 @@ findtime = %s
|
||||
maxretry = %d
|
||||
banaction = %s
|
||||
banaction_allports = %s
|
||||
chain = %s
|
||||
|
||||
`, settings.DefaultJailEnable, settings.BantimeIncrement, ignoreIPStr, settings.Bantime, settings.Findtime, settings.Maxretry, banaction, banactionAllports)
|
||||
`, settings.DefaultJailEnable, settings.BantimeIncrement, ignoreIPStr, settings.Bantime, settings.Findtime, settings.Maxretry, banaction, banactionAllports, chain)
|
||||
if settings.BantimeRndtime != "" {
|
||||
defaultSection += fmt.Sprintf("bantime.rndtime = %s\n", settings.BantimeRndtime)
|
||||
}
|
||||
defaultSection += "\n"
|
||||
|
||||
// Build action_mwlg configuration
|
||||
// Note: action_mwlg depends on action_ which depends on banaction (now defined above)
|
||||
@@ -979,6 +1007,10 @@ func updateJailLocalDefaultSection(settings AppSettings) error {
|
||||
if banactionAllports == "" {
|
||||
banactionAllports = "nftables-allports"
|
||||
}
|
||||
chain := settings.Chain
|
||||
if chain == "" {
|
||||
chain = "INPUT"
|
||||
}
|
||||
// Keys to update
|
||||
keysToUpdate := map[string]string{
|
||||
"enabled": fmt.Sprintf("enabled = %t", settings.DefaultJailEnable),
|
||||
@@ -989,6 +1021,14 @@ func updateJailLocalDefaultSection(settings AppSettings) error {
|
||||
"maxretry": fmt.Sprintf("maxretry = %d", settings.Maxretry),
|
||||
"banaction": fmt.Sprintf("banaction = %s", banaction),
|
||||
"banaction_allports": fmt.Sprintf("banaction_allports = %s", banactionAllports),
|
||||
"chain": fmt.Sprintf("chain = %s", chain),
|
||||
}
|
||||
if settings.BantimeRndtime != "" {
|
||||
keysToUpdate["bantime.rndtime"] = fmt.Sprintf("bantime.rndtime = %s", settings.BantimeRndtime)
|
||||
}
|
||||
defaultKeysOrder := []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports", "chain"}
|
||||
if settings.BantimeRndtime != "" {
|
||||
defaultKeysOrder = append(defaultKeysOrder, "bantime.rndtime")
|
||||
}
|
||||
keysUpdated := make(map[string]bool)
|
||||
|
||||
@@ -1021,14 +1061,24 @@ func updateJailLocalDefaultSection(settings AppSettings) error {
|
||||
} else if inDefault {
|
||||
// Check if this line is a key we need to update
|
||||
keyUpdated := false
|
||||
for key, newValue := range keysToUpdate {
|
||||
keyPattern := "^\\s*" + regexp.QuoteMeta(key) + "\\s*="
|
||||
if matched, _ := regexp.MatchString(keyPattern, trimmed); matched {
|
||||
outputLines = append(outputLines, newValue)
|
||||
keysUpdated[key] = true
|
||||
// When user cleared bantime.rndtime, remove the line from config instead of keeping old value
|
||||
if settings.BantimeRndtime == "" {
|
||||
if matched, _ := regexp.MatchString(`^\s*bantime\.rndtime\s*=`, trimmed); matched {
|
||||
keyUpdated = true
|
||||
defaultUpdated = true
|
||||
break
|
||||
// don't append: line is removed
|
||||
}
|
||||
}
|
||||
if !keyUpdated {
|
||||
for key, newValue := range keysToUpdate {
|
||||
keyPattern := "^\\s*" + regexp.QuoteMeta(key) + "\\s*="
|
||||
if matched, _ := regexp.MatchString(keyPattern, trimmed); matched {
|
||||
outputLines = append(outputLines, newValue)
|
||||
keysUpdated[key] = true
|
||||
keyUpdated = true
|
||||
defaultUpdated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !keyUpdated {
|
||||
@@ -1043,9 +1093,8 @@ func updateJailLocalDefaultSection(settings AppSettings) error {
|
||||
|
||||
// Add any missing keys to the DEFAULT section
|
||||
if inDefault {
|
||||
for key, newValue := range keysToUpdate {
|
||||
if !keysUpdated[key] {
|
||||
// Find the DEFAULT section and insert after it
|
||||
for _, key := range defaultKeysOrder {
|
||||
if newValue, ok := keysToUpdate[key]; ok && !keysUpdated[key] {
|
||||
for i, outputLine := range outputLines {
|
||||
if strings.TrimSpace(outputLine) == "[DEFAULT]" {
|
||||
outputLines = append(outputLines[:i+1], append([]string{newValue}, outputLines[i+1:]...)...)
|
||||
|
||||
@@ -424,6 +424,10 @@ func (ac *AgentConnector) UpdateDefaultSettings(ctx context.Context, settings co
|
||||
if banactionAllports == "" {
|
||||
banactionAllports = "nftables-allports"
|
||||
}
|
||||
chain := settings.Chain
|
||||
if chain == "" {
|
||||
chain = "INPUT"
|
||||
}
|
||||
payload := map[string]interface{}{
|
||||
"bantimeIncrement": settings.BantimeIncrement,
|
||||
"defaultJailEnable": settings.DefaultJailEnable,
|
||||
@@ -433,6 +437,8 @@ func (ac *AgentConnector) UpdateDefaultSettings(ctx context.Context, settings co
|
||||
"maxretry": settings.Maxretry,
|
||||
"banaction": banaction,
|
||||
"banactionAllports": banactionAllports,
|
||||
"chain": chain,
|
||||
"bantimeRndtime": settings.BantimeRndtime,
|
||||
}
|
||||
return ac.put(ctx, "/v1/jails/default-settings", payload, nil)
|
||||
}
|
||||
|
||||
@@ -1618,6 +1618,10 @@ func (sc *SSHConnector) UpdateDefaultSettings(ctx context.Context, settings conf
|
||||
if banactionAllportsVal == "" {
|
||||
banactionAllportsVal = "nftables-allports"
|
||||
}
|
||||
chainVal := settings.Chain
|
||||
if chainVal == "" {
|
||||
chainVal = "INPUT"
|
||||
}
|
||||
// Define the keys we want to update
|
||||
keysToUpdate := map[string]string{
|
||||
"enabled": fmt.Sprintf("enabled = %t", settings.DefaultJailEnable),
|
||||
@@ -1628,13 +1632,21 @@ func (sc *SSHConnector) UpdateDefaultSettings(ctx context.Context, settings conf
|
||||
"maxretry": fmt.Sprintf("maxretry = %d", settings.Maxretry),
|
||||
"banaction": fmt.Sprintf("banaction = %s", banactionVal),
|
||||
"banaction_allports": fmt.Sprintf("banaction_allports = %s", banactionAllportsVal),
|
||||
"chain": fmt.Sprintf("chain = %s", chainVal),
|
||||
}
|
||||
if settings.BantimeRndtime != "" {
|
||||
keysToUpdate["bantime.rndtime"] = fmt.Sprintf("bantime.rndtime = %s", settings.BantimeRndtime)
|
||||
}
|
||||
defaultKeysOrder := []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports", "chain"}
|
||||
if settings.BantimeRndtime != "" {
|
||||
defaultKeysOrder = append(defaultKeysOrder, "bantime.rndtime")
|
||||
}
|
||||
|
||||
// Parse existing content and update only specific keys in DEFAULT section
|
||||
if existingContent == "" {
|
||||
// File doesn't exist, create new one with DEFAULT section
|
||||
defaultLines := []string{"[DEFAULT]"}
|
||||
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
|
||||
for _, key := range defaultKeysOrder {
|
||||
defaultLines = append(defaultLines, keysToUpdate[key])
|
||||
}
|
||||
defaultLines = append(defaultLines, "")
|
||||
@@ -1662,6 +1674,11 @@ func (sc *SSHConnector) UpdateDefaultSettings(ctx context.Context, settings conf
|
||||
bantimeIncrementPython = "True"
|
||||
}
|
||||
|
||||
chainValEsc := escapeForShell(chainVal)
|
||||
bantimeRndtimeEsc := ""
|
||||
if settings.BantimeRndtime != "" {
|
||||
bantimeRndtimeEsc = escapeForShell(settings.BantimeRndtime)
|
||||
}
|
||||
updateScript := fmt.Sprintf(`python3 <<'PY'
|
||||
import re
|
||||
|
||||
@@ -1674,6 +1691,8 @@ bantime_increment_val = %s
|
||||
bantime_val = '%s'
|
||||
findtime_val = '%s'
|
||||
maxretry_val = %d
|
||||
chain_val = '%s'
|
||||
bantime_rndtime_val = '%s'
|
||||
keys_to_update = {
|
||||
'enabled': 'enabled = ' + str(default_jail_enable_val).lower(),
|
||||
'bantime.increment': 'bantime.increment = ' + str(bantime_increment_val).lower(),
|
||||
@@ -1682,8 +1701,14 @@ keys_to_update = {
|
||||
'findtime': 'findtime = ' + findtime_val,
|
||||
'maxretry': 'maxretry = ' + str(maxretry_val),
|
||||
'banaction': 'banaction = ' + banaction_val,
|
||||
'banaction_allports': 'banaction_allports = ' + banaction_allports_val
|
||||
'banaction_allports': 'banaction_allports = ' + banaction_allports_val,
|
||||
'chain': 'chain = ' + chain_val
|
||||
}
|
||||
if bantime_rndtime_val:
|
||||
keys_to_update['bantime.rndtime'] = 'bantime.rndtime = ' + bantime_rndtime_val
|
||||
keys_order = ['enabled', 'bantime.increment', 'ignoreip', 'bantime', 'findtime', 'maxretry', 'banaction', 'banaction_allports', 'chain']
|
||||
if bantime_rndtime_val:
|
||||
keys_order.append('bantime.rndtime')
|
||||
|
||||
try:
|
||||
with open(jail_file, 'r') as f:
|
||||
@@ -1716,13 +1741,18 @@ for line in lines:
|
||||
elif in_default:
|
||||
# Check if this line is a key we need to update
|
||||
key_updated = False
|
||||
for key, new_value in keys_to_update.items():
|
||||
pattern = r'^\s*' + re.escape(key) + r'\s*='
|
||||
if re.match(pattern, stripped):
|
||||
output_lines.append(new_value + '\n')
|
||||
keys_updated.add(key)
|
||||
key_updated = True
|
||||
break
|
||||
# When user cleared bantime.rndtime, remove the line from config instead of keeping old value
|
||||
if not bantime_rndtime_val and re.match(r'^\s*bantime\.rndtime\s*=', stripped):
|
||||
key_updated = True
|
||||
# don't append: line is removed
|
||||
if not key_updated:
|
||||
for key, new_value in keys_to_update.items():
|
||||
pattern = r'^\s*' + re.escape(key) + r'\s*='
|
||||
if re.match(pattern, stripped):
|
||||
output_lines.append(new_value + '\n')
|
||||
keys_updated.add(key)
|
||||
key_updated = True
|
||||
break
|
||||
if not key_updated:
|
||||
# Keep the line as-is (might be action_mwlg or other DEFAULT settings)
|
||||
output_lines.append(line)
|
||||
@@ -1733,13 +1763,13 @@ for line in lines:
|
||||
# If DEFAULT section wasn't found, create it at the beginning
|
||||
if not default_section_found:
|
||||
default_lines = ["[DEFAULT]\n"]
|
||||
for key in ["enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"]:
|
||||
for key in keys_order:
|
||||
default_lines.append(keys_to_update[key] + "\n")
|
||||
default_lines.append("\n")
|
||||
output_lines = default_lines + output_lines
|
||||
else:
|
||||
# Add any missing keys to the DEFAULT section
|
||||
for key in ["enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"]:
|
||||
for key in keys_order:
|
||||
if key not in keys_updated:
|
||||
# Find the DEFAULT section and insert after it
|
||||
for i, line in enumerate(output_lines):
|
||||
@@ -1749,7 +1779,7 @@ else:
|
||||
|
||||
with open(jail_file, 'w') as f:
|
||||
f.writelines(output_lines)
|
||||
PY`, escapeForShell(jailLocalPath), escapeForShell(ignoreIPStr), escapeForShell(banactionVal), escapeForShell(banactionAllportsVal), defaultJailEnablePython, bantimeIncrementPython, escapeForShell(settings.Bantime), escapeForShell(settings.Findtime), settings.Maxretry)
|
||||
PY`, escapeForShell(jailLocalPath), escapeForShell(ignoreIPStr), escapeForShell(banactionVal), escapeForShell(banactionAllportsVal), defaultJailEnablePython, bantimeIncrementPython, escapeForShell(settings.Bantime), escapeForShell(settings.Findtime), settings.Maxretry, chainValEsc, bantimeRndtimeEsc)
|
||||
|
||||
_, err = sc.runRemoteCommand(ctx, []string{updateScript})
|
||||
return err
|
||||
@@ -1779,6 +1809,10 @@ func (sc *SSHConnector) EnsureJailLocalStructure(ctx context.Context) error {
|
||||
if banactionAllportsVal == "" {
|
||||
banactionAllportsVal = "nftables-allports"
|
||||
}
|
||||
chainVal := settings.Chain
|
||||
if chainVal == "" {
|
||||
chainVal = "INPUT"
|
||||
}
|
||||
|
||||
// Build the new jail.local content in Go (mirrors local ensureJailLocalStructure)
|
||||
banner := config.JailLocalBanner()
|
||||
@@ -1792,6 +1826,7 @@ findtime = %s
|
||||
maxretry = %d
|
||||
banaction = %s
|
||||
banaction_allports = %s
|
||||
chain = %s
|
||||
|
||||
`,
|
||||
settings.DefaultJailEnable,
|
||||
@@ -1802,7 +1837,12 @@ banaction_allports = %s
|
||||
settings.Maxretry,
|
||||
banactionVal,
|
||||
banactionAllportsVal,
|
||||
chainVal,
|
||||
)
|
||||
if settings.BantimeRndtime != "" {
|
||||
defaultSection += fmt.Sprintf("bantime.rndtime = %s\n", settings.BantimeRndtime)
|
||||
}
|
||||
defaultSection += "\n"
|
||||
|
||||
actionMwlgConfig := `# Custom Fail2Ban action for UI callbacks
|
||||
action_mwlg = %(action_)s
|
||||
|
||||
@@ -1236,6 +1236,10 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
|
||||
if banactionAllports == "" {
|
||||
banactionAllports = "nftables-allports"
|
||||
}
|
||||
chain := settings.Chain
|
||||
if chain == "" {
|
||||
chain = "INPUT"
|
||||
}
|
||||
// Define the keys we want to update
|
||||
keysToUpdate := map[string]string{
|
||||
"enabled": fmt.Sprintf("enabled = %t", settings.DefaultJailEnable),
|
||||
@@ -1246,6 +1250,14 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
|
||||
"maxretry": fmt.Sprintf("maxretry = %d", settings.Maxretry),
|
||||
"banaction": fmt.Sprintf("banaction = %s", banaction),
|
||||
"banaction_allports": fmt.Sprintf("banaction_allports = %s", banactionAllports),
|
||||
"chain": fmt.Sprintf("chain = %s", chain),
|
||||
}
|
||||
if settings.BantimeRndtime != "" {
|
||||
keysToUpdate["bantime.rndtime"] = fmt.Sprintf("bantime.rndtime = %s", settings.BantimeRndtime)
|
||||
}
|
||||
defaultKeysOrder := []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports", "chain"}
|
||||
if settings.BantimeRndtime != "" {
|
||||
defaultKeysOrder = append(defaultKeysOrder, "bantime.rndtime")
|
||||
}
|
||||
|
||||
// Track which keys we've updated
|
||||
@@ -1257,7 +1269,7 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
|
||||
var newLines []string
|
||||
newLines = append(newLines, strings.Split(strings.TrimRight(config.JailLocalBanner(), "\n"), "\n")...)
|
||||
newLines = append(newLines, "[DEFAULT]")
|
||||
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
|
||||
for _, key := range defaultKeysOrder {
|
||||
newLines = append(newLines, keysToUpdate[key])
|
||||
}
|
||||
newLines = append(newLines, "")
|
||||
@@ -1307,14 +1319,23 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
|
||||
} else if inDefault {
|
||||
// We're in DEFAULT section - check if this line is a key we need to update
|
||||
keyUpdated := false
|
||||
for key, newValue := range keysToUpdate {
|
||||
// Check if this line contains the key (with or without spaces around =)
|
||||
keyPattern := "^\\s*" + regexp.QuoteMeta(key) + "\\s*="
|
||||
if matched, _ := regexp.MatchString(keyPattern, trimmed); matched {
|
||||
outputLines = append(outputLines, newValue)
|
||||
keysUpdated[key] = true
|
||||
// When user cleared bantime.rndtime, remove the line from config instead of keeping old value
|
||||
if settings.BantimeRndtime == "" {
|
||||
if matched, _ := regexp.MatchString(`^\s*bantime\.rndtime\s*=`, trimmed); matched {
|
||||
keyUpdated = true
|
||||
break
|
||||
// don't append: line is removed
|
||||
}
|
||||
}
|
||||
if !keyUpdated {
|
||||
for key, newValue := range keysToUpdate {
|
||||
// Check if this line contains the key (with or without spaces around =)
|
||||
keyPattern := "^\\s*" + regexp.QuoteMeta(key) + "\\s*="
|
||||
if matched, _ := regexp.MatchString(keyPattern, trimmed); matched {
|
||||
outputLines = append(outputLines, newValue)
|
||||
keysUpdated[key] = true
|
||||
keyUpdated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !keyUpdated {
|
||||
@@ -1330,14 +1351,14 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
|
||||
// If DEFAULT section wasn't found, create it at the beginning
|
||||
if !defaultSectionFound {
|
||||
defaultLines := []string{"[DEFAULT]"}
|
||||
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
|
||||
for _, key := range defaultKeysOrder {
|
||||
defaultLines = append(defaultLines, keysToUpdate[key])
|
||||
}
|
||||
defaultLines = append(defaultLines, "")
|
||||
outputLines = append(defaultLines, outputLines...)
|
||||
} else {
|
||||
// Add any missing keys to the DEFAULT section
|
||||
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
|
||||
for _, key := range defaultKeysOrder {
|
||||
if !keysUpdated[key] {
|
||||
// Find the DEFAULT section and insert after it
|
||||
for i, line := range outputLines {
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Standard-Bantime",
|
||||
"settings.default_bantime.description": "Die Anzahl der Sekunden, für die ein Host gesperrt wird. Zeitformat: 1m = 1 Minute, 1h = 1 Stunde, 1d = 1 Tag, 1w = 1 Woche, 1mo = 1 Monat, 1y = 1 Jahr.",
|
||||
"settings.default_bantime_placeholder": "z.B. 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Optional. Maximale Zufallssekunden in der Bantime-Inkrement-Formel (z.B. 2048). Leer lassen für Fail2ban-Standard.",
|
||||
"settings.bantime_rndtime_placeholder": "z.B. 2048",
|
||||
"settings.default_chain": "Standard-Chain",
|
||||
"settings.default_chain.description": "iptables/nftables-Chain für Bans (z.B. INPUT für Host, DOCKER-USER für Docker-Container).",
|
||||
"settings.chain_help_title": "Standard-Chain",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Für Anwendungen in Docker. Bans gelten für alle Container auf dem Host.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Für Anwendungen auf dem Host. Bans gelten nur für das Host-Netzwerk, nicht für Docker-Netze.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Nur bei älteren Docker-Setups, in denen DOCKER-USER nicht verfügbar ist.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Standard-Sperraktion (z.B. nftables-multiport, nftables-allports, firewallcmd-rich-rules, etc). Wird verwendet, um action_* Variablen zu definieren.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Standard-Bantime",
|
||||
"settings.default_bantime.description": "D Aazahl vo de Sekunde, wo ä Host gsperrt wird. Zytformat: 1m = 1 Minute, 1h = 1 Stund, 1d = 1 Tag, 1w = 1 Woche, 1mo = 1 Monet, 1y = 1 Jahr.",
|
||||
"settings.default_bantime_placeholder": "z.B. 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Optionau. Maximali Zufallssekunde i dr Bantime-Inkrement-Formle (z.B. 2048). Läär la füre Fail2ban-Standard.",
|
||||
"settings.bantime_rndtime_placeholder": "z.B. 2048",
|
||||
"settings.default_chain": "Standard-Chain",
|
||||
"settings.default_chain.description": "iptables/nftables-Chain für Bans (z.B. INPUT für Host, DOCKER-USER für Docker-Container).",
|
||||
"settings.chain_help_title": "Standard-Chain",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Für Aawändige i Docker. Bans gäute für alli Container uf em Host.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Für Aawändige uf em Host. Bans gäute nur für s Host-Netz, nid für Docker-Netz.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Nur bi äutere Docker-Setups, wo DOCKER-USER nid verfüegbar isch.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Standard-Sperraktione (z.B. nftables-multiport, nftables-allports, firewallcmd-rich-rules, etc). Wird brucht, zum action_* Variablen z definiere.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Default Bantime",
|
||||
"settings.default_bantime.description": "The number of seconds that a host is banned. Time format: 1m = 1 minutes, 1h = 1 hour, 1d = 1 day, 1w = 1 week, 1mo = 1 month, 1y = 1 year.",
|
||||
"settings.default_bantime_placeholder": "e.g., 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Optional. Max random seconds added in bantime increment formula (e.g. 2048). Leave empty to use Fail2ban default.",
|
||||
"settings.bantime_rndtime_placeholder": "e.g., 2048",
|
||||
"settings.default_chain": "Default Chain",
|
||||
"settings.default_chain.description": "iptables/nftables chain used for banning (e.g. INPUT for host, DOCKER-USER for Docker containers).",
|
||||
"settings.chain_help_title": "Default chain",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Use for apps running in Docker. Bans apply to all containers on the host.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Use for apps on the host. Bans apply to the host network only, not Docker networks.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Only for older Docker setups where DOCKER-USER is not available.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Default banning action (e.g. nftables-multiport, nftables-allports, firewallcmd-rich-rules, etc). It is used to define action_* variables.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Bantime por defecto",
|
||||
"settings.default_bantime.description": "El número de segundos que un host está bloqueado. Formato de tiempo: 1m = 1 minutos, 1h = 1 horas, 1d = 1 días, 1w = 1 semana, 1mo = 1 mes, 1y = 1 año.",
|
||||
"settings.default_bantime_placeholder": "p.ej., 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Opcional. Segundos aleatorios máximos en la fórmula de incremento de bantime (p.ej. 2048). Dejar vacío para usar el valor por defecto de Fail2ban.",
|
||||
"settings.bantime_rndtime_placeholder": "p.ej., 2048",
|
||||
"settings.default_chain": "Chain por defecto",
|
||||
"settings.default_chain.description": "Chain de iptables/nftables para baneos (p.ej. INPUT para host, DOCKER-USER para contenedores Docker).",
|
||||
"settings.chain_help_title": "Chain por defecto",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Para aplicaciones en Docker. Los bans se aplican a todos los contenedores del host.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Para aplicaciones en el host. Los bans solo afectan a la red del host, no a las redes Docker.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Solo en instalaciones Docker antiguas donde DOCKER-USER no existe.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Acción de bloqueo por defecto (p.ej. nftables-multiport, nftables-allports, firewallcmd-rich-rules, etc). Se utiliza para definir las variables action_*.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Bantime par défaut",
|
||||
"settings.default_bantime.description": "Le nombre de secondes pendant lesquelles un hôte est banni. Format de temps : 1m = 1 minutes, 1h = 1 heures, 1d = 1 jours, 1w = 1 semaines, 1mo = 1 mois, 1y = 1 années.",
|
||||
"settings.default_bantime_placeholder": "par exemple, 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Optionnel. Nombre maximal de secondes aléatoires dans la formule d'incrément de bantime (ex. 2048). Laisser vide pour utiliser la valeur par défaut de Fail2ban.",
|
||||
"settings.bantime_rndtime_placeholder": "par ex., 2048",
|
||||
"settings.default_chain": "Chain par défaut",
|
||||
"settings.default_chain.description": "Chain iptables/nftables pour les bannissements (ex. INPUT pour l'hôte, DOCKER-USER pour les conteneurs Docker).",
|
||||
"settings.chain_help_title": "Chain par défaut",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Pour les applications dans Docker. Les bans s'appliquent à tous les conteneurs de l'hôte.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Pour les applications sur l'hôte. Les bans ne concernent que le réseau de l'hôte, pas les réseaux Docker.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Uniquement pour les anciennes installations Docker sans DOCKER-USER.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Action de bannissement par défaut (par ex. nftables-multiport, nftables-allports, firewallcmd-rich-rules, etc). Elle est utilisée pour définir les variables action_*.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -171,6 +171,18 @@
|
||||
"settings.default_bantime": "Bantime predefinito",
|
||||
"settings.default_bantime.description": "Il numero di secondi per cui un host viene bannato. Formato tempo: 1m = 1 minuti, 1h = 1 ore, 1d = 1 giorni, 1w = 1 settimane, 1mo = 1 mesi, 1y = 1 anni.",
|
||||
"settings.default_bantime_placeholder": "es. 48h",
|
||||
"settings.bantime_rndtime": "Bantime Rndtime",
|
||||
"settings.bantime_rndtime.description": "Opzionale. Secondi casuali massimi nella formula di incremento bantime (es. 2048). Lasciare vuoto per usare il default di Fail2ban.",
|
||||
"settings.bantime_rndtime_placeholder": "es. 2048",
|
||||
"settings.default_chain": "Chain predefinita",
|
||||
"settings.default_chain.description": "Chain iptables/nftables per i ban (es. INPUT per l'host, DOCKER-USER per i container Docker).",
|
||||
"settings.chain_help_title": "Chain predefinita",
|
||||
"settings.chain_docker_user": "DOCKER-USER",
|
||||
"settings.chain_help_docker_user": "Per applicazioni in Docker. I ban si applicano a tutti i container sull'host.",
|
||||
"settings.chain_input": "INPUT",
|
||||
"settings.chain_help_input": "Per applicazioni sull'host. I ban riguardano solo la rete dell'host, non le reti Docker.",
|
||||
"settings.chain_forward": "FORWARD",
|
||||
"settings.chain_help_forward": "Solo per installazioni Docker vecchie dove DOCKER-USER non è disponibile.",
|
||||
"settings.banaction": "Banaction",
|
||||
"settings.banaction.description": "Azione di ban predefinita (es. nftables-multiport, nftables-allports, firewallcmd-rich-rules, ecc). Viene utilizzata per definire le variabili action_*.",
|
||||
"settings.banaction_allports": "Banaction Allports",
|
||||
|
||||
@@ -81,6 +81,8 @@ type AppSettingsRecord struct {
|
||||
DestEmail string
|
||||
Banaction string
|
||||
BanactionAllports string
|
||||
Chain string
|
||||
BantimeRndtime string
|
||||
// Advanced features
|
||||
AdvancedActionsJSON string
|
||||
GeoIPProvider string
|
||||
@@ -194,17 +196,17 @@ func GetAppSettings(ctx context.Context) (AppSettingsRecord, bool, error) {
|
||||
}
|
||||
|
||||
row := db.QueryRowContext(ctx, `
|
||||
SELECT language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, 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, console_output, smtp_insecure_skip_verify, smtp_auth_method
|
||||
SELECT language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, 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, console_output, smtp_insecure_skip_verify, smtp_auth_method, chain, bantime_rndtime
|
||||
FROM app_settings
|
||||
WHERE id = 1`)
|
||||
|
||||
var (
|
||||
lang, callback, callbackSecret, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, advancedActions, geoipProvider, geoipDatabasePath, smtpAuthMethod sql.NullString
|
||||
port, smtpPort, maxretry, maxLogLines sql.NullInt64
|
||||
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn, emailAlertsForBans, emailAlertsForUnbans, consoleOutput, smtpInsecureSkipVerify sql.NullInt64
|
||||
lang, callback, callbackSecret, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, chain, bantimeRndtime, advancedActions, geoipProvider, geoipDatabasePath, smtpAuthMethod sql.NullString
|
||||
port, smtpPort, maxretry, maxLogLines sql.NullInt64
|
||||
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn, emailAlertsForBans, emailAlertsForUnbans, consoleOutput, smtpInsecureSkipVerify sql.NullInt64
|
||||
)
|
||||
|
||||
err := row.Scan(&lang, &port, &debug, &restartNeeded, &callback, &callbackSecret, &alerts, &emailAlertsForBans, &emailAlertsForUnbans, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions, &geoipProvider, &geoipDatabasePath, &maxLogLines, &consoleOutput, &smtpInsecureSkipVerify, &smtpAuthMethod)
|
||||
err := row.Scan(&lang, &port, &debug, &restartNeeded, &callback, &callbackSecret, &alerts, &emailAlertsForBans, &emailAlertsForUnbans, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions, &geoipProvider, &geoipDatabasePath, &maxLogLines, &consoleOutput, &smtpInsecureSkipVerify, &smtpAuthMethod, &chain, &bantimeRndtime)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return AppSettingsRecord{}, false, nil
|
||||
}
|
||||
@@ -244,6 +246,8 @@ WHERE id = 1`)
|
||||
DestEmail: stringFromNull(destemail),
|
||||
Banaction: stringFromNull(banaction),
|
||||
BanactionAllports: stringFromNull(banactionAllports),
|
||||
Chain: stringFromNull(chain),
|
||||
BantimeRndtime: stringFromNull(bantimeRndtime),
|
||||
// Advanced features
|
||||
AdvancedActionsJSON: stringFromNull(advancedActions),
|
||||
GeoIPProvider: stringFromNull(geoipProvider),
|
||||
@@ -262,9 +266,9 @@ func SaveAppSettings(ctx context.Context, rec AppSettingsRecord) error {
|
||||
}
|
||||
_, err := db.ExecContext(ctx, `
|
||||
INSERT INTO app_settings (
|
||||
id, language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, 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, console_output, smtp_insecure_skip_verify, smtp_auth_method
|
||||
id, language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, 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, console_output, smtp_insecure_skip_verify, smtp_auth_method, chain, bantime_rndtime
|
||||
) VALUES (
|
||||
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
) ON CONFLICT(id) DO UPDATE SET
|
||||
language = excluded.language,
|
||||
port = excluded.port,
|
||||
@@ -296,7 +300,9 @@ INSERT INTO app_settings (
|
||||
max_log_lines = excluded.max_log_lines,
|
||||
console_output = excluded.console_output,
|
||||
smtp_insecure_skip_verify = excluded.smtp_insecure_skip_verify,
|
||||
smtp_auth_method = excluded.smtp_auth_method
|
||||
smtp_auth_method = excluded.smtp_auth_method,
|
||||
chain = excluded.chain,
|
||||
bantime_rndtime = excluded.bantime_rndtime
|
||||
`, rec.Language,
|
||||
rec.Port,
|
||||
boolToInt(rec.Debug),
|
||||
@@ -327,7 +333,9 @@ INSERT INTO app_settings (
|
||||
rec.MaxLogLines,
|
||||
boolToInt(rec.ConsoleOutput),
|
||||
boolToInt(rec.SMTPInsecureSkipVerify),
|
||||
rec.SMTPAuthMethod)
|
||||
rec.SMTPAuthMethod,
|
||||
rec.Chain,
|
||||
rec.BantimeRndtime)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1101,6 +1109,16 @@ CREATE INDEX IF NOT EXISTS idx_perm_blocks_status ON permanent_blocks(status);
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN chain TEXT DEFAULT 'INPUT'`); err != nil {
|
||||
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN bantime_rndtime TEXT DEFAULT ''`); err != nil {
|
||||
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = strings.Contains // Keep strings import for migration example above
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2129,10 +2129,12 @@ func UpdateSettingsHandler(c *gin.Context) {
|
||||
oldSettings.DefaultJailEnable != newSettings.DefaultJailEnable ||
|
||||
ignoreIPsChanged ||
|
||||
oldSettings.Bantime != newSettings.Bantime ||
|
||||
oldSettings.BantimeRndtime != newSettings.BantimeRndtime ||
|
||||
oldSettings.Findtime != newSettings.Findtime ||
|
||||
oldSettings.Maxretry != newSettings.Maxretry ||
|
||||
oldSettings.Banaction != newSettings.Banaction ||
|
||||
oldSettings.BanactionAllports != newSettings.BanactionAllports
|
||||
oldSettings.BanactionAllports != newSettings.BanactionAllports ||
|
||||
oldSettings.Chain != newSettings.Chain
|
||||
|
||||
if defaultSettingsChanged {
|
||||
config.DebugLog("Fail2Ban DEFAULT settings changed, pushing to all enabled servers")
|
||||
@@ -2348,6 +2350,10 @@ func ApplyFail2banSettings(jailLocalPath string) error {
|
||||
// TODO: -> maybe we store [DEFAULT] block in memory, replace lines
|
||||
// or do a line-based approach. Example is simplistic:
|
||||
|
||||
chain := s.Chain
|
||||
if chain == "" {
|
||||
chain = "INPUT"
|
||||
}
|
||||
newLines := []string{
|
||||
"[DEFAULT]",
|
||||
fmt.Sprintf("enabled = %t", s.DefaultJailEnable),
|
||||
@@ -2358,8 +2364,12 @@ func ApplyFail2banSettings(jailLocalPath string) error {
|
||||
fmt.Sprintf("maxretry = %d", s.Maxretry),
|
||||
fmt.Sprintf("banaction = %s", s.Banaction),
|
||||
fmt.Sprintf("banaction_allports = %s", s.BanactionAllports),
|
||||
"",
|
||||
fmt.Sprintf("chain = %s", chain),
|
||||
}
|
||||
if s.BantimeRndtime != "" {
|
||||
newLines = append(newLines, fmt.Sprintf("bantime.rndtime = %s", s.BantimeRndtime))
|
||||
}
|
||||
newLines = append(newLines, "")
|
||||
content := strings.Join(newLines, "\n")
|
||||
|
||||
return os.WriteFile(jailLocalPath, []byte(content), 0644)
|
||||
|
||||
@@ -160,8 +160,10 @@ function loadSettings() {
|
||||
document.getElementById('geoipDatabasePath').value = data.geoipDatabasePath || '/usr/share/GeoIP/GeoLite2-Country.mmdb';
|
||||
document.getElementById('maxLogLines').value = data.maxLogLines || 50;
|
||||
document.getElementById('banTime').value = data.bantime || '';
|
||||
document.getElementById('bantimeRndtime').value = data.bantimeRndtime || '';
|
||||
document.getElementById('findTime').value = data.findtime || '';
|
||||
document.getElementById('maxRetry').value = data.maxretry || '';
|
||||
document.getElementById('defaultChain').value = data.chain || 'INPUT';
|
||||
// Load IgnoreIPs as array
|
||||
const ignoreIPs = data.ignoreips || [];
|
||||
renderIgnoreIPsTags(ignoreIPs);
|
||||
@@ -233,11 +235,13 @@ function saveSettings(event) {
|
||||
bantimeIncrement: document.getElementById('bantimeIncrement').checked,
|
||||
defaultJailEnable: document.getElementById('defaultJailEnable').checked,
|
||||
bantime: document.getElementById('banTime').value.trim(),
|
||||
bantimeRndtime: document.getElementById('bantimeRndtime').value.trim(),
|
||||
findtime: document.getElementById('findTime').value.trim(),
|
||||
maxretry: parseInt(document.getElementById('maxRetry').value, 10) || 3,
|
||||
ignoreips: getIgnoreIPsArray(),
|
||||
banaction: document.getElementById('banaction').value,
|
||||
banactionAllports: document.getElementById('banactionAllports').value,
|
||||
chain: document.getElementById('defaultChain').value || 'INPUT',
|
||||
geoipProvider: document.getElementById('geoipProvider').value || 'builtin',
|
||||
geoipDatabasePath: document.getElementById('geoipDatabasePath').value || '/usr/share/GeoIP/GeoLite2-Country.mmdb',
|
||||
maxLogLines: parseInt(document.getElementById('maxLogLines').value, 10) || 50,
|
||||
|
||||
@@ -833,6 +833,14 @@
|
||||
<p class="text-xs text-red-600 mt-1 hidden" id="banTimeError"></p>
|
||||
</div>
|
||||
|
||||
<!-- Bantime Rndtime (optional, for bantime increment formula) -->
|
||||
<div class="mb-4">
|
||||
<label for="bantimeRndtime" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.bantime_rndtime">Bantime Rndtime</label>
|
||||
<p class="text-xs text-gray-500 mb-2" data-i18n="settings.bantime_rndtime.description">Optional. Max random seconds added in bantime increment formula (e.g. 2048). Leave empty to use Fail2ban default.</p>
|
||||
<input type="text" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" id="bantimeRndtime"
|
||||
data-i18n-placeholder="settings.bantime_rndtime_placeholder" placeholder="e.g., 2048" />
|
||||
</div>
|
||||
|
||||
<!-- Banaction -->
|
||||
<div class="mb-4">
|
||||
<label for="banaction" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.banaction">Banaction</label>
|
||||
@@ -907,6 +915,20 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Default Chain -->
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<label for="defaultChain" class="block text-sm font-medium text-gray-700" data-i18n="settings.default_chain">Default Chain</label>
|
||||
<button type="button" onclick="document.getElementById('chainHelpModal').classList.remove('hidden')" class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-200 text-gray-600 hover:bg-gray-300 text-xs font-medium" aria-label="Chain help" title="Chains help">?</button>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-2" data-i18n="settings.default_chain.description">iptables/nftables chain used for banning (e.g. INPUT for host, DOCKER-USER for Docker containers).</p>
|
||||
<select id="defaultChain" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="INPUT" data-i18n="settings.chain_input">INPUT</option>
|
||||
<option value="DOCKER-USER" data-i18n="settings.chain_docker_user">DOCKER-USER</option>
|
||||
<option value="FORWARD" data-i18n="settings.chain_forward">FORWARD</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Findtime -->
|
||||
<div class="mb-4">
|
||||
<label for="findTime" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.default_findtime">Default Findtime</label>
|
||||
@@ -1478,6 +1500,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chain Help Modal -->
|
||||
<div id="chainHelpModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
|
||||
<div class="relative flex min-h-full w-full items-center justify-center p-4 sm:p-6">
|
||||
<div class="fixed inset-0 bg-gray-500 opacity-75" aria-hidden="true"></div>
|
||||
|
||||
<div class="relative z-10 w-full rounded-lg bg-white text-left shadow-xl transition-all" style="max-width: 560px;">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900" data-i18n="settings.chain_help_title">Default chain</h3>
|
||||
<button type="button" onclick="closeModal('chainHelpModal')" class="text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600" aria-label="Close">
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<dl class="mt-4 space-y-4 text-sm">
|
||||
<div>
|
||||
<dt class="font-medium text-gray-900" data-i18n="settings.chain_docker_user">DOCKER-USER</dt>
|
||||
<dd class="text-gray-600 mt-1" data-i18n="settings.chain_help_docker_user">Use for apps running in Docker. Bans apply to all containers on the host.</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-medium text-gray-900" data-i18n="settings.chain_input">INPUT</dt>
|
||||
<dd class="text-gray-600 mt-1" data-i18n="settings.chain_help_input">Use for apps on the host. Bans apply to the host network only, not Docker networks.</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-medium text-gray-900" data-i18n="settings.chain_forward">FORWARD</dt>
|
||||
<dd class="text-gray-600 mt-1" data-i18n="settings.chain_help_forward">Only for older Docker setups where DOCKER-USER is not available.</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button type="button" class="w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" onclick="closeModal('chainHelpModal')" data-i18n="modal.close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ********************** Modal Templates END ************************ -->
|
||||
|
||||
<!-- jQuery (used by Select2) -->
|
||||
|
||||
Reference in New Issue
Block a user