diff --git a/AppImage/components/security.tsx b/AppImage/components/security.tsx index dccbab59..6aaad67f 100644 --- a/AppImage/components/security.tsx +++ b/AppImage/components/security.tsx @@ -133,6 +133,7 @@ export function Security() { maxretry: "", bantime: "", findtime: "", permanent: false, }) const [f2bSavingConfig, setF2bSavingConfig] = useState(false) + const [f2bApplyingJails, setF2bApplyingJails] = useState(false) // SSL/HTTPS state const [sslEnabled, setSslEnabled] = useState(false) @@ -242,6 +243,29 @@ export function Security() { } } + const handleApplyMissingJails = async () => { + setF2bApplyingJails(true) + setError("") + setSuccess("") + try { + const data = await fetchApi("/api/security/fail2ban/apply-jails", { + method: "POST", + }) + if (data.success) { + setSuccess(data.message || "Missing jails applied successfully") + // Reload to see the new jails + await loadFail2banDetails() + loadSecurityTools() + } else { + setError(data.message || "Failed to apply missing jails") + } + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to apply missing jails") + } finally { + setF2bApplyingJails(false) + } + } + const openJailConfig = (jail: JailDetail) => { const bt = parseInt(jail.bantime, 10) const isPermanent = bt === -1 @@ -2098,6 +2122,50 @@ export function Security() { + {/* Missing jails warning */} + {(() => { + const expectedJails = ["sshd", "proxmox", "proxmenux"] + const currentNames = f2bDetails.jails.map(j => j.name.toLowerCase()) + const missing = expectedJails.filter(j => !currentNames.includes(j)) + if (missing.length === 0) return null + + const jailLabels: Record = { + sshd: "SSH (sshd)", + proxmox: "Proxmox UI (port 8006)", + proxmenux: "ProxMenux Monitor (port 8008)", + } + + return ( +
+
+
+ +
+

Missing protections detected

+

+ The following jails are not configured:{" "} + {missing.map(j => jailLabels[j] || j).join(", ")} +

+
+
+ +
+
+ ) + })()} + {/* Tab switcher - redesigned with border on inactive */}