Use some delay and rechecks for first time-local init, if service fails

This commit is contained in:
2026-04-19 20:23:30 +02:00
parent e030eca6ce
commit 96d5a453cf
3 changed files with 58 additions and 8 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ func ReloadFail2banManager() error {
for _, srv := range s.Servers {
if srv.Enabled && srv.Type == "local" {
if err := EnsureLocalFail2banAction(srv); err != nil {
return err
DebugLog("Warning: failed to ensure local fail2ban action for server %s: %v", srv.Name, err)
}
}
}
+45 -7
View File
@@ -813,8 +813,15 @@ func UpsertServerHandler(c *gin.Context) {
}
// Ensures the jail.local structure is properly initialized for newly enabled/added servers
var actionFileWarning string
var jailLocalWarning bool
var restartWarning string
if justEnabled && server.Type == "local" {
if err := config.EnsureLocalFail2banAction(server); err != nil {
config.DebugLog("Warning: failed to prepare local action artifacts for server %s: %v", server.Name, err)
actionFileWarning = err.Error()
}
}
if justEnabled || !wasEnabled {
conn, err := fail2ban.GetManager().Connector(server.ID)
if err == nil {
@@ -822,10 +829,12 @@ func UpsertServerHandler(c *gin.Context) {
// - file missing --> creates it
// - file is ours --> updates it
// - file is user's own --> leave it alone
if err := conn.EnsureJailLocalStructure(c.Request.Context()); err != nil {
config.DebugLog("Warning: failed to ensure jail.local structure for server %s: %v", server.Name, err)
} else {
config.DebugLog("Successfully ensured jail.local structure for server %s", server.Name)
if !(server.Type == "local" && actionFileWarning != "") {
if err := conn.EnsureJailLocalStructure(c.Request.Context()); err != nil {
config.DebugLog("Warning: failed to ensure jail.local structure for server %s: %v", server.Name, err)
} else {
config.DebugLog("Successfully ensured jail.local structure for server %s", server.Name)
}
}
// Checks the integrity AFTER ensuring structure so fresh servers don't trigger a false-positive warning.
@@ -837,9 +846,15 @@ func UpsertServerHandler(c *gin.Context) {
// Tries to restart Fail2ban and performs a basic health check after the server was enabled
if justEnabled {
if err := conn.Restart(c.Request.Context()); err != nil {
msg := fmt.Sprintf("failed to restart fail2ban for server %s: %v", server.Name, err)
config.DebugLog("Warning: %s", msg)
restartWarning = msg
// Local connectors can report a transient "Could not find server" during initial startup.
// Recheck briefly before surfacing a warning toast.
if server.Type == "local" && waitForConnectorReady(c.Request.Context(), conn, 4, 750*time.Millisecond) {
config.DebugLog("Local connector %s became healthy after transient restart/reload error: %v", server.Name, err)
} else {
msg := fmt.Sprintf("failed to restart fail2ban for server %s: %v", server.Name, err)
config.DebugLog("Warning: %s", msg)
restartWarning = msg
}
} else {
if _, err := conn.GetJailInfos(c.Request.Context()); err != nil {
config.DebugLog("Warning: fail2ban appears unhealthy on server %s after restart: %v", server.Name, err)
@@ -855,6 +870,9 @@ func UpsertServerHandler(c *gin.Context) {
if jailLocalWarning {
resp["jailLocalWarning"] = true
}
if actionFileWarning != "" {
resp["actionFileWarning"] = actionFileWarning
}
if restartWarning != "" {
resp["restartWarning"] = restartWarning
}
@@ -1003,6 +1021,26 @@ func TestServerHandler(c *gin.Context) {
c.JSON(http.StatusOK, resp)
}
func waitForConnectorReady(ctx context.Context, conn fail2ban.Connector, attempts int, delay time.Duration) bool {
if attempts < 1 {
attempts = 1
}
for i := 0; i < attempts; i++ {
if _, err := conn.GetJailInfos(ctx); err == nil {
return true
}
if i == attempts-1 {
break
}
select {
case <-ctx.Done():
return false
case <-time.After(delay):
}
}
return false
}
// =========================================================================
// Notification Processing (Internal)
// =========================================================================
+12
View File
@@ -489,6 +489,9 @@ function submitServerForm(event) {
if (data.restartWarning) {
showToast(data.restartWarning, 'warning', 12000);
}
if (data.actionFileWarning) {
showToast(data.actionFileWarning, 'warning', 12000);
}
var saved = data.server || {};
currentServerId = saved.id || currentServerId;
return loadServers().then(function() {
@@ -620,6 +623,15 @@ function setServerEnabled(serverId, enabled) {
currentServerId = null;
currentServer = null;
}
if (data.jailLocalWarning) {
showToast(t('servers.jail_local_warning', 'Warning: jail.local is not managed by Fail2ban-UI. Move each jail into its own file under jail.d/ and delete jail.local so Fail2ban-UI can recreate it. See docs for permissions.'), 'warning', 12000);
}
if (data.restartWarning) {
showToast(data.restartWarning, 'warning', 12000);
}
if (data.actionFileWarning) {
showToast(data.actionFileWarning, 'warning', 12000);
}
return loadServers().then(function() {
renderServerManagerList();
renderServerSelector();