diff --git a/internal/config/settings.go b/internal/config/settings.go index cc3fe98..9f573e2 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -90,6 +90,7 @@ type AdvancedActionsConfig struct { Integration string `json:"integration"` Mikrotik MikrotikIntegrationSettings `json:"mikrotik"` PfSense PfSenseIntegrationSettings `json:"pfSense"` + OPNsense OPNsenseIntegrationSettings `json:"opnsense"` } type MikrotikIntegrationSettings struct { @@ -109,6 +110,14 @@ type PfSenseIntegrationSettings struct { SkipTLSVerify bool `json:"skipTLSVerify"` } +type OPNsenseIntegrationSettings struct { + BaseURL string `json:"baseUrl"` + APIKey string `json:"apiKey"` + APISecret string `json:"apiSecret"` + Alias string `json:"alias"` + SkipTLSVerify bool `json:"skipTLSVerify"` +} + func defaultAdvancedActionsConfig() AdvancedActionsConfig { return AdvancedActionsConfig{ Enabled: false, diff --git a/internal/integrations/mikrotik.go b/internal/integrations/mikrotik.go index bfede1b..259e856 100644 --- a/internal/integrations/mikrotik.go +++ b/internal/integrations/mikrotik.go @@ -97,7 +97,18 @@ func (m *mikrotikIntegration) runCommand(req Request, command string) error { address := net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", port)) client, err := ssh.Dial("tcp", address, clientCfg) if err != nil { - return fmt.Errorf("failed to connect to mikrotik: %w", err) + // Provide more specific error messages for common connection issues + if netErr, ok := err.(net.Error); ok { + if netErr.Timeout() { + return fmt.Errorf("connection to mikrotik at %s timed out: %w", address, err) + } + } + if opErr, ok := err.(*net.OpError); ok { + if opErr.Err != nil { + return fmt.Errorf("failed to connect to mikrotik at %s: %v (check host, port %d, and network connectivity)", address, opErr.Err, port) + } + } + return fmt.Errorf("failed to connect to mikrotik at %s: %w (check host, port %d, username, and credentials)", address, err, port) } defer client.Close() diff --git a/internal/integrations/opnsense.go b/internal/integrations/opnsense.go new file mode 100644 index 0000000..4f2b295 --- /dev/null +++ b/internal/integrations/opnsense.go @@ -0,0 +1,128 @@ +package integrations + +import ( + "bytes" + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/swissmakers/fail2ban-ui/internal/config" +) + +type opnsenseIntegration struct{} + +func init() { + Register(&opnsenseIntegration{}) +} + +func (o *opnsenseIntegration) ID() string { + return "opnsense" +} + +func (o *opnsenseIntegration) DisplayName() string { + return "OPNsense" +} + +func (o *opnsenseIntegration) Validate(cfg config.AdvancedActionsConfig) error { + if cfg.OPNsense.BaseURL == "" { + return fmt.Errorf("OPNsense base URL is required") + } + if cfg.OPNsense.APIKey == "" || cfg.OPNsense.APISecret == "" { + return fmt.Errorf("OPNsense API key and secret are required") + } + if cfg.OPNsense.Alias == "" { + return fmt.Errorf("OPNsense alias is required") + } + return nil +} + +func (o *opnsenseIntegration) BlockIP(req Request) error { + if err := o.Validate(req.Config); err != nil { + return err + } + return o.callAPI(req, "add", req.IP) +} + +func (o *opnsenseIntegration) UnblockIP(req Request) error { + if err := o.Validate(req.Config); err != nil { + return err + } + return o.callAPI(req, "del", req.IP) +} + +func (o *opnsenseIntegration) callAPI(req Request, action, ip string) error { + cfg := req.Config.OPNsense + + // OPNsense uses /api/firewall/alias_util/{action}/{alias_name} + apiURL := strings.TrimSuffix(cfg.BaseURL, "/") + fmt.Sprintf("/api/firewall/alias_util/%s/%s", action, cfg.Alias) + + // Request body for OPNsense + payload := map[string]string{ + "address": ip, + } + data, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("failed to encode OPNsense payload: %w", err) + } + + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + if cfg.SkipTLSVerify { + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // #nosec G402 - user controlled + } + } + + reqLogger := "OPNsense" + if req.Logger != nil { + req.Logger("Calling OPNsense API %s action=%s payload=%s", apiURL, action, string(data)) + } + + httpReq, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewReader(data)) + if err != nil { + return fmt.Errorf("failed to create OPNsense request: %w", err) + } + httpReq.Header.Set("Content-Type", "application/json") + + // OPNsense uses Basic Auth with API key as username and API secret as password + auth := base64.StdEncoding.EncodeToString([]byte(cfg.APIKey + ":" + cfg.APISecret)) + httpReq.Header.Set("Authorization", "Basic "+auth) + + resp, err := httpClient.Do(httpReq) + if err != nil { + // Provide more specific error messages for connection issues + if netErr, ok := err.(interface { + Timeout() bool + Error() string + }); ok && netErr.Timeout() { + return fmt.Errorf("OPNsense API request to %s timed out: %w", apiURL, err) + } + return fmt.Errorf("OPNsense API request to %s failed: %w (check base URL, network connectivity, and API credentials)", apiURL, err) + } + defer resp.Body.Close() + + // Read response body for better error messages + bodyBytes, _ := io.ReadAll(resp.Body) + bodyStr := strings.TrimSpace(string(bodyBytes)) + + if resp.StatusCode >= 300 { + if bodyStr != "" { + return fmt.Errorf("OPNsense API request failed: status %s, response: %s", resp.Status, bodyStr) + } + return fmt.Errorf("OPNsense API request failed: status %s (check API credentials and alias name)", resp.Status) + } + + if req.Logger != nil { + req.Logger("%s API call succeeded", reqLogger) + if bodyStr != "" { + req.Logger("%s API response: %s", reqLogger, bodyStr) + } + } + return nil +} diff --git a/internal/integrations/pfsense.go b/internal/integrations/pfsense.go index 74b20c6..314f166 100644 --- a/internal/integrations/pfsense.go +++ b/internal/integrations/pfsense.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "encoding/json" "fmt" + "io" "net/http" "strings" "time" @@ -30,8 +31,8 @@ func (p *pfSenseIntegration) Validate(cfg config.AdvancedActionsConfig) error { if cfg.PfSense.BaseURL == "" { return fmt.Errorf("pfSense base URL is required") } - if cfg.PfSense.APIToken == "" || cfg.PfSense.APISecret == "" { - return fmt.Errorf("pfSense API token and secret are required") + if cfg.PfSense.APIToken == "" { + return fmt.Errorf("pfSense API key is required") } if cfg.PfSense.Alias == "" { return fmt.Errorf("pfSense alias is required") @@ -92,21 +93,38 @@ func (p *pfSenseIntegration) callAPI(req Request, action string, payload map[str return fmt.Errorf("failed to create pfSense request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("X-API-Key", cfg.APIToken) - httpReq.Header.Set("X-API-Secret", cfg.APISecret) + // pfSense REST API v2 uses KeyAuth with x-api-key header only + httpReq.Header.Set("x-api-key", cfg.APIToken) resp, err := httpClient.Do(httpReq) if err != nil { - return fmt.Errorf("pfSense request failed: %w", err) + // Provide more specific error messages for connection issues + if netErr, ok := err.(interface { + Timeout() bool + Error() string + }); ok && netErr.Timeout() { + return fmt.Errorf("pfSense API request to %s timed out: %w", apiURL, err) + } + return fmt.Errorf("pfSense API request to %s failed: %w (check base URL, network connectivity, and API credentials)", apiURL, err) } defer resp.Body.Close() + // Read response body for better error messages + bodyBytes, _ := io.ReadAll(resp.Body) + bodyStr := strings.TrimSpace(string(bodyBytes)) + if resp.StatusCode >= 300 { - return fmt.Errorf("pfSense request failed: status %s", resp.Status) + if bodyStr != "" { + return fmt.Errorf("pfSense API request failed: status %s, response: %s", resp.Status, bodyStr) + } + return fmt.Errorf("pfSense API request failed: status %s (check API credentials and alias name)", resp.Status) } if req.Logger != nil { req.Logger("%s API call succeeded", reqLogger) + if bodyStr != "" { + req.Logger("%s API response: %s", reqLogger, bodyStr) + } } return nil } diff --git a/internal/locales/de.json b/internal/locales/de.json index c24f11a..6dff9a9 100644 --- a/internal/locales/de.json +++ b/internal/locales/de.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "SSH-Passwort", "settings.advanced.mikrotik.key": "SSH-Key-Pfad (optional)", "settings.advanced.mikrotik.list": "Address-Listenname", - "settings.advanced.pfsense.note": "Benötigt das pfSense API-Paket. Verwende ein Token mit Alias-Rechten.", + "settings.advanced.pfsense.note": "Benötigt das pfSense REST API-Paket. Gib den API-Schlüssel und den Alias-Namen ein.", + "settings.advanced.pfsense.install_link": "REST API-Paket installieren", + "settings.advanced.pfsense.api_key_setup": "API-Schlüssel einrichten", "settings.advanced.pfsense.base_url": "Basis-URL", - "settings.advanced.pfsense.token": "API-Token", - "settings.advanced.pfsense.secret": "API-Secret", + "settings.advanced.pfsense.token": "API-Schlüssel", + "settings.advanced.pfsense.token_hint": "Erstellen in System > REST API > Keys im pfSense webConfigurator", "settings.advanced.pfsense.alias": "Alias-Name", "settings.advanced.pfsense.skip_tls": "TLS-Validierung überspringen (Self-Signed)", + "settings.advanced.opnsense.note": "Gib die OPNsense API-Anmeldedaten und den Alias-Namen ein.", + "settings.advanced.opnsense.api_docs": "API-Dokumentation", + "settings.advanced.opnsense.api_key_setup": "API-Schlüssel einrichten", + "settings.advanced.opnsense.base_url": "Basis-URL", + "settings.advanced.opnsense.key": "API-Schlüssel", + "settings.advanced.opnsense.key_hint": "Erstellen in System > Zugriff > Benutzer > API-Schlüssel", + "settings.advanced.opnsense.secret": "API-Geheimnis", + "settings.advanced.opnsense.secret_hint": "Zusammen mit dem API-Schlüssel erstellen", + "settings.advanced.opnsense.alias": "Alias-Name", + "settings.advanced.opnsense.skip_tls": "TLS-Validierung überspringen (Self-Signed)", "settings.advanced.log_title": "Log der permanenten Sperren", "settings.advanced.log_empty": "Noch keine permanenten Sperren vorhanden.", "settings.advanced.log_ip": "IP", diff --git a/internal/locales/de_ch.json b/internal/locales/de_ch.json index 7053ca8..2b72a67 100644 --- a/internal/locales/de_ch.json +++ b/internal/locales/de_ch.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "SSH-Passwort", "settings.advanced.mikrotik.key": "SSH-Key-Pfad (optional)", "settings.advanced.mikrotik.list": "Adress-Lischtename", - "settings.advanced.pfsense.note": "Bruucht s pfSense API-Päckli. Erstell es Token wo Aliase cha bearbeite.", + "settings.advanced.pfsense.note": "Bruucht s pfSense REST API-Päckli. Gib dr API-Schlüssu und dr Alias-Name i.", + "settings.advanced.pfsense.install_link": "REST API-Päckli installiere", + "settings.advanced.pfsense.api_key_setup": "API-Schlüssu iirichte", "settings.advanced.pfsense.base_url": "Basis-URL", - "settings.advanced.pfsense.token": "API-Token", - "settings.advanced.pfsense.secret": "API-Secret", + "settings.advanced.pfsense.token": "API-Schlüssu", + "settings.advanced.pfsense.token_hint": "Erstelle i System > REST API > Keys im pfSense webConfigurator", "settings.advanced.pfsense.alias": "Alias-Name", "settings.advanced.pfsense.skip_tls": "TLS-Prüfig überspringe (Self-Signed)", + "settings.advanced.opnsense.note": "Gib d OPNsense API-Ameldedate und dr Alias-Name i.", + "settings.advanced.opnsense.api_docs": "API-Dokumentation", + "settings.advanced.opnsense.api_key_setup": "API-Schlüssu iirichte", + "settings.advanced.opnsense.base_url": "Basis-URL", + "settings.advanced.opnsense.key": "API-Schlüssu", + "settings.advanced.opnsense.key_hint": "Erstelle i System > Zuegriff > Benutzer > API-Schlüssu", + "settings.advanced.opnsense.secret": "API-Geheimnis", + "settings.advanced.opnsense.secret_hint": "Zämme mit em API-Schlüssu erstelle", + "settings.advanced.opnsense.alias": "Alias-Name", + "settings.advanced.opnsense.skip_tls": "TLS-Prüfig überspringe (Self-Signed)", "settings.advanced.log_title": "Permanent gsperrti IPs", "settings.advanced.log_empty": "No ke permanenti Sperrig erfasst.", "settings.advanced.log_ip": "IP", diff --git a/internal/locales/en.json b/internal/locales/en.json index 7c2ab99..3cd1d3e 100644 --- a/internal/locales/en.json +++ b/internal/locales/en.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "SSH Password", "settings.advanced.mikrotik.key": "SSH Key Path (optional)", "settings.advanced.mikrotik.list": "Address List Name", - "settings.advanced.pfsense.note": "Requires the pfSense API package. Use an API token that may edit aliases.", + "settings.advanced.pfsense.note": "Requires the pfSense REST API package. Enter the API key and alias to manage.", + "settings.advanced.pfsense.install_link": "Install REST API Package", + "settings.advanced.pfsense.api_key_setup": "Setup API Key", "settings.advanced.pfsense.base_url": "Base URL", - "settings.advanced.pfsense.token": "API Token", - "settings.advanced.pfsense.secret": "API Secret", + "settings.advanced.pfsense.token": "API Key", + "settings.advanced.pfsense.token_hint": "Generate in System > REST API > Keys in pfSense webConfigurator", "settings.advanced.pfsense.alias": "Alias Name", "settings.advanced.pfsense.skip_tls": "Skip TLS verification (self-signed)", + "settings.advanced.opnsense.note": "Enter the OPNsense API credentials and alias to manage.", + "settings.advanced.opnsense.api_docs": "API Documentation", + "settings.advanced.opnsense.api_key_setup": "Setup API Key", + "settings.advanced.opnsense.base_url": "Base URL", + "settings.advanced.opnsense.key": "API Key", + "settings.advanced.opnsense.key_hint": "Generate in System > Access > Users > API Keys", + "settings.advanced.opnsense.secret": "API Secret", + "settings.advanced.opnsense.secret_hint": "Generate together with API key", + "settings.advanced.opnsense.alias": "Alias Name", + "settings.advanced.opnsense.skip_tls": "Skip TLS verification (self-signed)", "settings.advanced.log_title": "Permanent Block Log", "settings.advanced.log_empty": "No permanent blocks recorded yet.", "settings.advanced.log_ip": "IP", diff --git a/internal/locales/es.json b/internal/locales/es.json index 6109362..fa5aacb 100644 --- a/internal/locales/es.json +++ b/internal/locales/es.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "Contraseña SSH", "settings.advanced.mikrotik.key": "Ruta de la clave SSH (opcional)", "settings.advanced.mikrotik.list": "Nombre de la lista", - "settings.advanced.pfsense.note": "Requiere el paquete de API de pfSense. Usa un token con acceso a alias.", + "settings.advanced.pfsense.note": "Requiere el paquete REST API de pfSense. Introduce la clave API y el alias a gestionar.", + "settings.advanced.pfsense.install_link": "Instalar paquete REST API", + "settings.advanced.pfsense.api_key_setup": "Configurar clave API", "settings.advanced.pfsense.base_url": "URL base", - "settings.advanced.pfsense.token": "Token API", - "settings.advanced.pfsense.secret": "Secreto API", + "settings.advanced.pfsense.token": "Clave API", + "settings.advanced.pfsense.token_hint": "Generar en Sistema > REST API > Keys en el webConfigurator de pfSense", "settings.advanced.pfsense.alias": "Nombre del alias", "settings.advanced.pfsense.skip_tls": "Omitir verificación TLS (autofirmado)", + "settings.advanced.opnsense.note": "Introduce las credenciales API de OPNsense y el alias a gestionar.", + "settings.advanced.opnsense.api_docs": "Documentación API", + "settings.advanced.opnsense.api_key_setup": "Configurar clave API", + "settings.advanced.opnsense.base_url": "URL base", + "settings.advanced.opnsense.key": "Clave API", + "settings.advanced.opnsense.key_hint": "Generar en Sistema > Acceso > Usuarios > Claves API", + "settings.advanced.opnsense.secret": "Secreto API", + "settings.advanced.opnsense.secret_hint": "Generar junto con la clave API", + "settings.advanced.opnsense.alias": "Nombre del alias", + "settings.advanced.opnsense.skip_tls": "Omitir verificación TLS (autofirmado)", "settings.advanced.log_title": "Registro de bloqueos permanentes", "settings.advanced.log_empty": "Aún no hay bloqueos permanentes.", "settings.advanced.log_ip": "IP", diff --git a/internal/locales/fr.json b/internal/locales/fr.json index 5ce9b12..32de275 100644 --- a/internal/locales/fr.json +++ b/internal/locales/fr.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "Mot de passe SSH", "settings.advanced.mikrotik.key": "Chemin de clé SSH (optionnel)", "settings.advanced.mikrotik.list": "Nom de la liste", - "settings.advanced.pfsense.note": "Nécessite le paquet API pfSense. Utiliser un jeton ayant accès aux alias.", + "settings.advanced.pfsense.note": "Nécessite le paquet REST API pfSense. Entrez la clé API et l'alias à gérer.", + "settings.advanced.pfsense.install_link": "Installer le paquet REST API", + "settings.advanced.pfsense.api_key_setup": "Configurer la clé API", "settings.advanced.pfsense.base_url": "URL de base", - "settings.advanced.pfsense.token": "Jeton API", - "settings.advanced.pfsense.secret": "Secret API", - "settings.advanced.pfsense.alias": "Nom d’alias", + "settings.advanced.pfsense.token": "Clé API", + "settings.advanced.pfsense.token_hint": "Générer dans Système > REST API > Keys dans le webConfigurator pfSense", + "settings.advanced.pfsense.alias": "Nom d'alias", "settings.advanced.pfsense.skip_tls": "Ignorer la vérification TLS (auto-signé)", + "settings.advanced.opnsense.note": "Entrez les identifiants API OPNsense et l'alias à gérer.", + "settings.advanced.opnsense.api_docs": "Documentation API", + "settings.advanced.opnsense.api_key_setup": "Configurer la clé API", + "settings.advanced.opnsense.base_url": "URL de base", + "settings.advanced.opnsense.key": "Clé API", + "settings.advanced.opnsense.key_hint": "Générer dans Système > Accès > Utilisateurs > Clés API", + "settings.advanced.opnsense.secret": "Secret API", + "settings.advanced.opnsense.secret_hint": "Générer avec la clé API", + "settings.advanced.opnsense.alias": "Nom d'alias", + "settings.advanced.opnsense.skip_tls": "Ignorer la vérification TLS (auto-signé)", "settings.advanced.log_title": "Journal des blocages permanents", "settings.advanced.log_empty": "Aucun blocage permanent pour le moment.", "settings.advanced.log_ip": "IP", diff --git a/internal/locales/it.json b/internal/locales/it.json index 7c533aa..5a8a045 100644 --- a/internal/locales/it.json +++ b/internal/locales/it.json @@ -188,12 +188,24 @@ "settings.advanced.mikrotik.password": "Password SSH", "settings.advanced.mikrotik.key": "Percorso chiave SSH (opzionale)", "settings.advanced.mikrotik.list": "Nome della lista", - "settings.advanced.pfsense.note": "Richiede il pacchetto API di pfSense. Usa un token con accesso agli alias.", + "settings.advanced.pfsense.note": "Richiede il pacchetto REST API di pfSense. Inserisci la chiave API e l'alias da gestire.", + "settings.advanced.pfsense.install_link": "Installa pacchetto REST API", + "settings.advanced.pfsense.api_key_setup": "Configura chiave API", "settings.advanced.pfsense.base_url": "URL base", - "settings.advanced.pfsense.token": "Token API", - "settings.advanced.pfsense.secret": "Segreto API", + "settings.advanced.pfsense.token": "Chiave API", + "settings.advanced.pfsense.token_hint": "Genera in Sistema > REST API > Keys nel webConfigurator di pfSense", "settings.advanced.pfsense.alias": "Nome alias", "settings.advanced.pfsense.skip_tls": "Ignora verifica TLS (auto-firmato)", + "settings.advanced.opnsense.note": "Inserisci le credenziali API di OPNsense e l'alias da gestire.", + "settings.advanced.opnsense.api_docs": "Documentazione API", + "settings.advanced.opnsense.api_key_setup": "Configura chiave API", + "settings.advanced.opnsense.base_url": "URL base", + "settings.advanced.opnsense.key": "Chiave API", + "settings.advanced.opnsense.key_hint": "Genera in Sistema > Accesso > Utenti > Chiavi API", + "settings.advanced.opnsense.secret": "Segreto API", + "settings.advanced.opnsense.secret_hint": "Genera insieme alla chiave API", + "settings.advanced.opnsense.alias": "Nome alias", + "settings.advanced.opnsense.skip_tls": "Ignora verifica TLS (auto-firmato)", "settings.advanced.log_title": "Registro dei blocchi permanenti", "settings.advanced.log_empty": "Nessun blocco permanente ancora registrato.", "settings.advanced.log_ip": "IP", diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go index 5f494e7..4fdf81e 100644 --- a/pkg/web/handlers.go +++ b/pkg/web/handlers.go @@ -1424,9 +1424,8 @@ func ListPermanentBlocksHandler(c *gin.Context) { // AdvancedActionsTestHandler allows manual block/unblock tests. func AdvancedActionsTestHandler(c *gin.Context) { var req struct { - Action string `json:"action"` - IP string `json:"ip"` - ServerID string `json:"serverId"` + Action string `json:"action"` + IP string `json:"ip"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"}) @@ -1449,7 +1448,7 @@ func AdvancedActionsTestHandler(c *gin.Context) { // Check if integration is configured if settings.AdvancedActions.Integration == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "no integration configured. Please configure an integration (MikroTik or pfSense) in Advanced Actions settings first"}) + c.JSON(http.StatusBadRequest, gin.H{"error": "no integration configured. Please configure an integration (MikroTik, pfSense, or OPNsense) in Advanced Actions settings first"}) return } @@ -1466,15 +1465,8 @@ func AdvancedActionsTestHandler(c *gin.Context) { return } + // Advanced actions work globally, not per server server := config.Fail2banServer{} - if req.ServerID != "" { - if srv, ok := config.GetServerByID(req.ServerID); ok { - server = srv - } else { - c.JSON(http.StatusBadRequest, gin.H{"error": "server not found"}) - return - } - } // Check if IP is already blocked before attempting action (for block action only) skipLoggingIfAlreadyBlocked := false diff --git a/pkg/web/static/fail2ban-ui.css b/pkg/web/static/fail2ban-ui.css index 721ff2b..706ed29 100644 --- a/pkg/web/static/fail2ban-ui.css +++ b/pkg/web/static/fail2ban-ui.css @@ -314,7 +314,7 @@ mark { } } -#advancedMikrotikFields, #advancedPfSenseFields { +#advancedMikrotikFields, #advancedPfSenseFields, #advancedOPNsenseFields { padding: 10px; } diff --git a/pkg/web/templates/index.html b/pkg/web/templates/index.html index d01a6c5..d8ecbc5 100644 --- a/pkg/web/templates/index.html +++ b/pkg/web/templates/index.html @@ -274,6 +274,7 @@ +
Choose where permanent bans should be synchronized.
@@ -309,19 +310,21 @@Requires the pfSense API package. Enter the API credentials and alias to manage.
+Requires the pfSense REST API package. Enter the API key and alias to manage.
+Generate in System > REST API > Keys in pfSense webConfigurator
Enter the OPNsense API credentials and alias to manage.
+ +Generate in System > Access > Users > API Keys
+Generate together with API key
+