Skip to content

WolfStack v23.8.0

Choose a tag to compare

@github-actions github-actions released this 15 May 20:46
· 1772 commits to master since this release

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

  1. SSH from a non-blocked IP and run iptables -D INPUT -s <IP> -j DROP
  2. SSH from a non-blocked IP, edit /etc/wolfstack/auth-lockout.json
    to add your IP to trusted_ips, then restart wolfstack
  3. Use the Security UI from a different non-blocked IP / browser
  4. 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.