You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Host firewall (UFW) rule management (Tools tab, Global Admin only, off by
default). Preview and then manually apply tagged ufw rules built from your own
settings — public listeners open to anyone, the config editor and admin SSH port
limited to Trusted Hosts. Apply is refused if it would leave the admin SSH port
without an allow rule. BBSFirewall never turns ufw itself on or off. Also shows
the host's own ufw drop log.
Kernel-level block push (UFW_PUSH_BLOCKS, off by default). Blocklist
entries and trigger auto-blocks are mirrored into ufw as deny from rules, so
blocked callers are dropped by the kernel even while BBSFirewall restarts.
Syncs automatically; temporary trigger blocks are removed when they expire.
Never pushes anything overlapping Trusted Hosts, the Whitelist, or loopback, and
refuses to push at all with an empty Trusted Hosts list. Capped by UFW_BLOCK_MAX_RULES (default 1000). Status, "Sync now" and "Remove pushed
rules" live in the Tools tab.
UFW auto-apply (UFW_AUTO_APPLY, off by default). Applies the host firewall
rule changes automatically at startup and after each Save, so adding or removing
a Trusted Host updates ufw right away instead of waiting for Preview + Apply.
Uses the same checks as Apply, and is stricter: if it can't tell which ports sshd
listens on, or the change would remove more than half of the rules, it changes
nothing and the Tools tab shows why. The last run's result is shown in the Tools tab.
Log search (Logs tab and GET /api/logs/search). Search every log file at
once by text, type, level and date range — every word must match, "quotes"
for a phrase, -word to exclude. Newest matches first, terms highlighted;
click a result to open that file at that exact line, with the lines around it.
Download all results as a text file.
Better log viewer: line numbers, colour-coded levels with on/off chips per
level, a live filter box, "Load older / newer lines" to page through files of
any size (it used to stop at the last 512 KB), and Follow (live) on today's
file to watch new lines arrive like tail -f.
Download log files from the file list or the viewer — the whole file, no
size limit (GET /api/logs/download) — or every log file at once as one .zip with "Download all (.zip)" (GET /api/logs/download-all). Streamed, so
a big log folder doesn't use extra memory.
🔒 Security
New security guide for sysops: locking down the admin editor,
accounts and MFA, the Management API, SSH modes, PROXY Protocol, ufw and the
server itself, plus how to report a vulnerability privately.
Firewall Admin accounts can no longer change who can reach the editor. The
Config Editor, Management API and Host Firewall (UFW) settings, every list-file
path, and the Trusted Hosts / API / Status allowlists are now Global Admin only —
shown read-only to a Firewall Admin and refused by the server. A Firewall Admin
could previously set their own API key (which outlives their account) or lock
the Global Admin out.
List-file paths and LOG_DIR must stay inside the BBSFirewall folder, and
list files must be .txt. A changed path outside it is refused — it previously
let one Save write any file on the host as root.
Turning MFA on asks for your current password, and MFA setup is refused
while MFA is already on (disable it first to replace an authenticator). A stolen
session cookie could previously swap in its own authenticator.
Login and MFA lockouts now count attempts that arrive at the same moment; the
password and backup-code checks no longer pause every connected caller.
Caller-supplied text (e.g. an SSH username) can no longer forge lines in the log
files or console output.
A role name that isn't recognised now gets the least access, not the most.
Trigger regexes with repeated alternation (a|aa)+ or nested groups ((a+))+
are now refused as catastrophic-backtracking risks, like (a+)+ already was.
Installing updates, rolling back, deleting log files and replacing the SSH
host key are now Global Admin only. A Firewall Admin could install an older
release that didn't have the role rules above, and get around them that way.
SSH (terminate mode) now checks callers the moment they connect. The
blocklist, rate limit, per-IP and global caps, country block and idle timeout
used to apply only once a caller had sent its SSH banner, so a caller that
connected and stayed silent was never checked or timed out. Blocked callers
are now dropped straight away, and every caller must finish logging in
within 2 minutes.
SSH (terminate mode): a caller that trips an auto-block trigger is now
disconnected completely. Before, only that shell closed and the same
connection could open a new one straight to the BBS.
The SSH banner no longer names the SSH library and its version, and a
caller can no longer send an unlimited number of environment variables or
open more than 4 session channels at once on one connection.
With UFW management on, a public port can no longer share the admin SSH
port. Setting e.g. HTTPS_REDIRECT_PORT to the admin SSH port made the ufw
rules open that port to everyone — and a Firewall Admin can change those
ports. Such a setting is now refused, and the admin SSH port is never opened
to Anywhere.
Admin logins and admin changes are now always in the log file.
Successful logins, saved settings, GeoIP downloads, SSH host key changes,
ufw block syncs and "Whitelist my IP" used to be logged only at the info
level, so by default there was no record of who logged in or changed what.
🐛 Fixed
The Tools tab's update Backups list (with Roll back to this) is now
always shown to a Global Admin, with a note when there are no backups yet —
one is saved each time Update now runs. It also no longer disappears
when the GitHub update check fails.
Saving no longer drops auto-blocks. Save used to rewrite every list file
from what the page loaded, silently deleting trigger auto-blocks (and "Whitelist
my IP") added since. Only lists you actually edited are written now, keeping any
lines added on disk meanwhile; if a list changed on disk any other way, nothing
is saved and you're asked to reload.
Settings containing \ or " (e.g. an API key) were saved in a form the
firewall read back differently from what was typed. Values now load exactly as
entered.
A blocked caller that never hung up kept a connection slot until CONNECTION_TIMEOUT (forever at 0); refused connections are now dropped at once.
SSH (terminate mode) now counts against MAX_CONNECTIONS, honours CONNECTION_TIMEOUT (idle = no session data; SSH keepalives don't count), and allows one BBS shell per SSH connection — one caller
could previously open unlimited BBS sessions over a single connection.
SSH (terminate mode) now acts as a proper telnet client toward the BBS: the
backend's option negotiation no longer shows up as stray characters (e.g. √♥ √☺ √ ² before the first screen), and 0xFF bytes are escaped/unescaped
correctly in both directions instead of being corrupted.
Self-update and rollback no longer freeze every telnet/SSH session while npm install runs. A rollback no longer restores an old status-trustedhosts.txt
or copies admin-account backups around, and a pre-release tag (v1.5.0-beta.9)
can be pinned.
An interrupted "Download all (.zip)" no longer leaves the download stuck — two of
them used to block all further zip downloads until a restart.
IPv6 whitelist/blocklist entries now match however they're written
(2001:DB8:0::1 = 2001:db8::1).
Trigger scanning no longer re-scans everything received so far on every
packet (about 2 s of CPU per caller at a 64 KB TRIGGER_SCAN_BYTES).
Kernel block push never denies an address in the Status or API allowlists.
UFW Apply refuses to remove the only rule for a port sshd is listening on (e.g.
after changing Admin SSH port before moving sshd).
Changing a list's path now starts the new file with the current list, and
editing that list in the same Save is refused (it went to the old file).
System Stats, log Follow and UFW sync polling no longer keep an idle session
signed in forever.
The plain-HTTP redirect only echoes a well-formed Host header; a log file
deleted mid-download returns 404 instead of an error.
After "Restart firewall" (and self-update / reboot), the editor could reload
while the old process was still shutting down and come up empty — no settings,
no version — until a manual refresh. It now waits for the new process before
reloading, and retries loading its settings instead of giving up on the first
failed request. The exiting process also tells browsers to drop their
connection, so on a host running ufw the page no longer stalls for up to a
minute on a connection to the old process.
Host firewall (UFW) Preview/Apply compared Trusted Hosts entries as typed
(1.2.3.4/32, 0.0.0.0/0) against ufw's own spelling (1.2.3.4, Anywhere),
so such entries showed a change that never went away — and Apply would have
deleted the matching rules. Rules are now always compared in ufw's form, and
"Anywhere" rules are added/removed one address family at a time (removing the
IPv4 one no longer silently removes the IPv6 one too).
The startup banner now shows the version at every LOG_LEVEL.
Host firewall (UFW) rules for the config editor port only admitted Trusted
Hosts, silently blocking uptime monitors listed only in Status Trusted Hosts and
Management API callers. The rule now admits all three lists (and any IP when the
API is on with an empty API allowlist, matching the app itself).
Logs tab: the search form and the per-category file tables now lay out
identically on every box — they used to shift depending on which log types
existed and how long their names were.
The self-updater compared versions by splitting on dots only, so a pre-release
like 1.5.0-beta.1 counted as newer than 1.5.0 and would never have been
offered the final release. Pre-release tags now sort before their release.
Saving only a list (Trusted Hosts, Blocklist, …) no longer rewrites .env or makes
a .env backup. Each such save used to add an identical backup, pushing out the
older ones worth keeping. The Save message now asks for a restart only when a
setting actually changed; list changes take effect immediately.
🗑️ Removed
UFW_LIMIT_ADMIN_SSH (rate-limit the admin SSH port with ufw limit). The
admin SSH port is already limited to Trusted Hosts, and ufw's fixed limit of
6 connections in 30 seconds locked admins out during ordinary reconnects. A
leftover UFW_LIMIT_ADMIN_SSH=true is ignored with a warning at startup, and
the admin port gets plain allow rules at the next Apply.