Repository navigation
WolfStack v23.8.0
v23.8.0: brute-force lockout — kernel-level IP blocking with fleet propagation, unified across WolfStack UI / SSH / Proxmox
Triggered by a real fleet compromise — attacker logged into the WolfStack
web UI (port 8553 exposed publicly) using a leaked root password, used
the in-browser console to spawn a shell, ran passwd, then SSH'd in
with the new password. The operator wants WolfStack to handle this
itself rather than relying on Hetzner Robot firewalls.
What's in this release
Configurable per-IP lockout with operator-tunable policy:
- Default: 10 failures within 5 minutes triggers a 48-hour block
- Trusted IPs / CIDRs (v4 + v6) bypass entirely — operator can never
lock themselves out from declared admin networks - Master enable/disable switch for debugging
- All settings persisted to /etc/wolfstack/auth-lockout.json and
editable from the Security UI
Kernel-level blocking via iptables / ip6tables:
- When threshold hits, an INPUT chain DROP rule for the source IP is
installed at row 1 — TCP packets from that IP are silently
discarded by the kernel. No SYN-ACK, no RST, no HTTP response.
From the attacker's side the server appears completely offline. - Blocks ALL ports from that IP, not just the WolfStack port — the
operator's intent was "act like the server isn't there." - Active lockouts persisted to /etc/wolfstack/auth-active-lockouts.json
so kernel rules can be restored after a WolfStack restart. - Periodic cleanup tick removes expired iptables rules — they don't
accumulate over time.
Fleet-wide propagation:
- When ONE node detects a brute-force attack, it fans out the kernel
block to every other WolfStack-managed peer in the cluster via the
existing X-WolfStack-Secret federation channel. - Each receiver re-validates against its own trusted-IP list before
applying — an attacker can't trick a peer into blocking your admin
IP just because it's not trusted on their node. - Unblock propagates the same way — operator unblocking on one node
clears the block on all 14.
Unified coverage of three attack surfaces:
- WolfStack web UI (port 8553) — login handler feeds the limiter
directly. - SSH (port 22) — log_monitor tails journalctl _COMM=sshd, parses
"Failed password for" + "Invalid user" lines, dispatches to the
same limiter. - Proxmox web UI (port 8006) — log_monitor tails _COMM=pvedaemon,
parses "authentication failure; rhost=" lines.
All three feed record_failure_with() so kernel-block + fleet
propagation behave identically regardless of source.
Audit log in journald + in-memory bounded buffer (last 500 entries)
exposed via /api/security/auth-lockouts. Each entry: timestamp, IP,
username, success/fail, plain-English reason. Surfaced in the new
Security panel as a scrolling table.
Operator UI on the Security page:
- Edit policy (failures, window, lockout duration, trusted IPs,
enable toggle) - View currently-blocked IPs with one-click Unblock
- View recent auth attempts (success + fail + blocked) with source
IP, username, and reason - Configuration takes effect immediately, no service restart
Recovery paths if you accidentally lock yourself out
- SSH from a non-blocked IP and run
iptables -D INPUT -s <IP> -j DROP - SSH from a non-blocked IP, edit /etc/wolfstack/auth-lockout.json
to add your IP to trusted_ips, then restart wolfstack - Use the Security UI from a different non-blocked IP / browser
- Wait the lockout duration (default 48 hours)
Tests
885/885 pass. 8 new lockout safety tests (threshold trigger, trusted v4
bypass, trusted v6 CIDR bypass, disabled config skip, unblock clears,
audit log size cap, propagation hook fires exactly once at threshold).
6 new log_monitor parser tests covering sshd "Failed password",
"Invalid user", pvedaemon "authentication failure", success-skip, and
garbage-skip patterns.
Known limitations
- Kernel block requires iptables / ip6tables to be installed. If
they're missing, the lockout is still recorded in memory (HTTP-level
rejection still works) but the kernel-level "look offline" effect
is absent. - Behind a reverse proxy, peer_addr is the proxy IP — all attackers
appear as the same source. Operators with this topology should
configure the proxy to set X-Forwarded-For and we should respect
it (future v23.8.x). - Existing TCP connections from a blocked IP keep working briefly
until the next ACK fails.
Verifying this release
Each binary is signed via cosign keyless OIDC (no key distribution — signing identity is the GitHub Actions workflow itself, anchored to the Sigstore Fulcio CA and the Rekor transparency log) and ships with a SLSA build provenance attestation.
Verify the cosign signature:
cosign verify-blob \
--bundle wolfstack-x86_64.cosign.bundle \
--certificate-identity-regexp 'https://github.com/wolfsoftwaresystemsltd/WolfStack/\.github/workflows/release\.yml@.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
wolfstack-x86_64
Verify the build provenance:
gh attestation verify wolfstack-x86_64 --repo wolfsoftwaresystemsltd/WolfStack
Verify the SHA-256 checksum:
sha256sum -c SHA256SUMS
Artifacts
wolfstack-x86_64/wolfstack-aarch64— static musl binaries (Linux x86_64 and ARM64 / Raspberry Pi 4+).wolfstack-<arch>.cosign.bundle— cosign signature bundle (cert + signature + Rekor entry).SHA256SUMS— checksums for both binaries.
For per-version history see CHANGELOG.md.