2025-01-29 19:49:51 +01:00
|
|
|
// Fail2ban UI - A Swiss made, management interface for Fail2ban.
|
|
|
|
|
//
|
2026-02-20 00:02:06 +01:00
|
|
|
// Copyright (C) 2026 Swissmakers GmbH (https://swissmakers.ch)
|
2025-01-29 19:49:51 +01:00
|
|
|
//
|
|
|
|
|
// Licensed under the GNU General Public License, Version 3 (GPL-3.0)
|
|
|
|
|
// You may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
2026-02-20 00:02:06 +01:00
|
|
|
// Shared types, helpers, and high-level functions used across all connectors.
|
2025-01-25 16:21:14 +01:00
|
|
|
package fail2ban
|
|
|
|
|
|
2026-02-20 00:02:06 +01:00
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"sort"
|
|
|
|
|
"sync"
|
|
|
|
|
)
|
2025-01-25 16:21:14 +01:00
|
|
|
|
2026-02-14 21:17:25 +01:00
|
|
|
// =========================================================================
|
|
|
|
|
// Types
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
2026-02-20 00:02:06 +01:00
|
|
|
// JailInfo holds summary data for a single Fail2ban jail.
|
2025-01-25 16:21:14 +01:00
|
|
|
type JailInfo struct {
|
2025-01-27 13:21:45 +01:00
|
|
|
JailName string `json:"jailName"`
|
|
|
|
|
TotalBanned int `json:"totalBanned"`
|
|
|
|
|
NewInLastHour int `json:"newInLastHour"`
|
|
|
|
|
BannedIPs []string `json:"bannedIPs"`
|
2025-02-26 16:55:21 +01:00
|
|
|
Enabled bool `json:"enabled"`
|
2025-01-25 16:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 21:17:25 +01:00
|
|
|
// =========================================================================
|
2026-02-20 00:02:06 +01:00
|
|
|
// Service Control
|
2026-02-14 21:17:25 +01:00
|
|
|
// =========================================================================
|
|
|
|
|
|
2026-02-20 00:02:06 +01:00
|
|
|
// RestartFail2ban restarts (or reloads) the Fail2ban service on the given server.
|
2025-12-17 19:16:20 +01:00
|
|
|
func RestartFail2ban(serverID string) (string, error) {
|
2025-11-14 10:22:44 +01:00
|
|
|
manager := GetManager()
|
|
|
|
|
var (
|
|
|
|
|
conn Connector
|
|
|
|
|
err error
|
|
|
|
|
)
|
|
|
|
|
if serverID != "" {
|
|
|
|
|
conn, err = manager.Connector(serverID)
|
|
|
|
|
} else {
|
|
|
|
|
conn, err = manager.DefaultConnector()
|
|
|
|
|
}
|
2025-02-26 16:55:21 +01:00
|
|
|
if err != nil {
|
2025-12-17 19:16:20 +01:00
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if withMode, ok := conn.(interface {
|
|
|
|
|
RestartWithMode(ctx context.Context) (string, error)
|
|
|
|
|
}); ok {
|
|
|
|
|
return withMode.RestartWithMode(context.Background())
|
|
|
|
|
}
|
|
|
|
|
if err := conn.Restart(context.Background()); err != nil {
|
|
|
|
|
return "", err
|
2025-01-27 13:21:45 +01:00
|
|
|
}
|
2025-12-17 19:16:20 +01:00
|
|
|
return "restart", nil
|
2025-01-27 13:21:45 +01:00
|
|
|
}
|
2026-02-20 00:02:06 +01:00
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// Jail Info Collection
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
// bannedIPsFn is the signature used by any connector's GetBannedIPs method.
|
|
|
|
|
type bannedIPsFn func(ctx context.Context, jail string) ([]string, error)
|
|
|
|
|
|
|
|
|
|
// collectJailInfos fans out to fetch banned IPs for each jail concurrently,
|
|
|
|
|
// then returns the results sorted alphabetically. Both the local and SSH
|
|
|
|
|
// connectors delegate to this function from their GetJailInfos methods.
|
|
|
|
|
func collectJailInfos(ctx context.Context, jails []string, getBannedIPs bannedIPsFn) ([]JailInfo, error) {
|
|
|
|
|
type jailResult struct {
|
|
|
|
|
jail JailInfo
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
results := make(chan jailResult, len(jails))
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
|
|
for _, jail := range jails {
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
go func(j string) {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
ips, err := getBannedIPs(ctx, j)
|
|
|
|
|
if err != nil {
|
|
|
|
|
results <- jailResult{err: err}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
results <- jailResult{
|
|
|
|
|
jail: JailInfo{
|
|
|
|
|
JailName: j,
|
|
|
|
|
TotalBanned: len(ips),
|
|
|
|
|
BannedIPs: ips,
|
|
|
|
|
Enabled: true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}(jail)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
wg.Wait()
|
|
|
|
|
close(results)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
var infos []JailInfo
|
|
|
|
|
for r := range results {
|
|
|
|
|
if r.err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
infos = append(infos, r.jail)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.SliceStable(infos, func(i, j int) bool {
|
|
|
|
|
return infos[i].JailName < infos[j].JailName
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return infos, nil
|
|
|
|
|
}
|