βββββββ βββββββ ββββββββ ββββββββββββββββββ βββββββββββ βββ ββββββ βββ βββ
ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ ββββββββββββββ βββ
ββββββββββββββββββββββββ ββββββ βββββββββββββββββ βββ ββ ββββββββββββββ βββ
ββββββββββββββββββββββββ ββββββ βββββββββββββββββ βββββββββββββββββββββ βββ
ββββββββββββββββββββββββ βββ ββββββ ββββββββββββββββββββββββ βββββββββββββββββββ
βββββββ βββββββ ββββββββ βββ ββββββ βββββββββββ ββββββββ βββ βββββββββββββββββββ
A lightweight TCP proxy firewall for BBS telnet connections, built with Node.js.
By Sysop Network β https://github.com/SysopNetwork/BBSFirewall
| Feature | Description | |
|---|---|---|
| π | TCP Proxy | Forwards telnet connections to a backend BBS server |
| π | SSH Server | Encrypted SSH access on any port, proxied to the telnet backend |
| π | HTTPS Redirect | Redirects both HTTP (port 80) and HTTPS (port 443) to a configured URL |
| π | Let's Encrypt | Built-in cert setup script with auto-renewal, no downtime required |
| π‘ | PROXY Protocol v1 | Passes the real client IP to the backend BBS (requires compatible backend) |
| π | Country Blocking | Block connections by country using a local GeoIP database |
| β | IP Whitelist | Trusted IPs that bypass all firewall rules |
| π« | IP Blocklist | Permanently block specific IPs or CIDR ranges |
| β‘ | Rate Limiting | Automatic flood protection with configurable temporary blocks |
| π | Per-IP Connection Limit | Cap simultaneous connections from a single IP |
| π₯οΈ | Encoding Detection | Automatic UTF-8/CP437 detection with separate backend routing |
| π | Graceful Shutdown | Clean shutdown on SIGTERM/SIGINT, active sessions drain properly |
| βοΈ | PM2 Ready | Includes ecosystem.config.js for process management |
- Node.js 14 or higher
- Root or
CAP_NET_BIND_SERVICEto bind ports below 1024 (23, 80, 443) - Linux recommended for production; runs anywhere Node.js runs
# 1. Clone the repo
git clone https://github.com/SysopNetwork/BBSFirewall.git
cd BBSFirewall
# 2. Install dependencies
npm install
# 3. Create your config
cp .env.example .env
nano .env # set BACKEND_HOST, BACKEND_PORT at minimum
# 4. Start it
npm startnpm install -g pm2
pm2 start ecosystem.config.js
pm2 save
pm2 startup # follow the printed command to enable auto-start on bootPM2 commands:
pm2 start ecosystem.config.js # start
pm2 stop bbsfirewall # stop
pm2 restart bbsfirewall # restart
pm2 logs bbsfirewall # tail logs
pm2 list # statusAll settings live in .env. Copy .env.example to get started β every option is documented there.
| Variable | Description | Default |
|---|---|---|
LISTEN_PORT | Port to listen on for incoming telnet connections | 23 |
BACKEND_HOST | Backend BBS server hostname or IP | 127.0.0.1 |
BACKEND_PORT | Backend BBS server port | 23 |
ENCODING_DETECTION | Enable automatic UTF-8/CP437 encoding detection | false |
BACKEND_PORT_CP437 | Backend port for CP437 (DOS/ANSI) clients | 2323 |
BACKEND_PORT_UTF8 | Backend port for UTF-8 (Unicode) clients | 2423 |
| Variable | Description | Default |
|---|---|---|
MAX_CONNECTIONS | Maximum total simultaneous connections | 100 |
MAX_CONNECTIONS_PER_IP | Max simultaneous connections from a single IP (0 = unlimited) | 0 |
CONNECTION_TIMEOUT | Connection timeout in milliseconds (0 to disable) | 300000 |
| Variable | Description | Default |
|---|---|---|
BLOCKED_COUNTRIES | Comma-separated ISO country codes to block (e.g. CN,RU,KP) | (empty) |
BLOCK_UNKNOWN_COUNTRIES | Block connections with undetermined country | false |
| Variable | Description | Default |
|---|---|---|
WHITELIST_PATH | Path to IP whitelist file (bypasses all firewall rules) | (empty) |
BLOCKLIST_PATH | Path to IP blocklist file (permanent blocks) | (empty) |
| Variable | Description | Default |
|---|---|---|
RATE_LIMIT_ENABLED | Enable connection flood protection | true |
MAX_CONNECTIONS_PER_WINDOW | Max connection attempts per IP per time window | 10 |
RATE_LIMIT_WINDOW_MS | Time window in milliseconds | 60000 |
RATE_LIMIT_BLOCK_DURATION_MS | Temporary block duration in milliseconds | 300000 |
| Variable | Description | Default |
|---|---|---|
PROXY_PROTOCOL_ENABLED | Prepend real client IP to backend TCP stream | false |
| Variable | Description | Default |
|---|---|---|
WEB_REDIRECT_ENABLED | Redirect HTTP traffic on port 80 | false |
WEB_REDIRECT_URL | Destination URL for redirects (used by both HTTP and HTTPS) | (empty) |
| Variable | Description | Default |
|---|---|---|
HTTPS_REDIRECT_ENABLED | Redirect HTTPS traffic on port 443 | false |
HTTPS_REDIRECT_PORT | Port to listen on for HTTPS | 443 |
HTTPS_CERT_PATH | Path to TLS certificate (fullchain) | ./certs/fullchain.pem |
HTTPS_KEY_PATH | Path to TLS private key | ./certs/privkey.pem |
ACME_WEBROOT | Directory for Let's Encrypt challenge files | ./certs/webroot |
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL | Log level: debug, info, warn, error | info |
| Variable | Description | Default |
|---|---|---|
SSH_ENABLED | Enable the SSH server | false |
SSH_LISTEN_PORT | Port to listen on for SSH connections | 2222 |
SSH_HOST_KEY | Path to SSH host private key file | ./ssh_host_key |
SSH_CIPHERS | Comma-separated list of allowed SSH ciphers | (see below) |
BBSFirewall includes an optional SSH server that accepts any username and password and proxies the session to the backend BBS via telnet. This lets users connect with a modern SSH client instead of a raw telnet client.
Generate an SSH host key (only needed once):
ssh-keygen -t rsa -b 4096 -f ssh_host_key -N "" -m PEMEnable in .env:
SSH_ENABLED=true
SSH_LISTEN_PORT=22
SSH_HOST_KEY=./ssh_host_keyConnect from any SSH client β any username and password works:
ssh yourserver.example.comIncludes both modern and legacy ciphers for old terminal clients:
aes128-gcm@openssh.com,aes256-gcm@openssh.comaes128-ctr,aes192-ctr,aes256-ctraes128-cbc,aes192-cbc,aes256-cbc3des-cbc(for very old clients)
Note: Binary file transfers (Zmodem, Ymodem, etc.) do not work reliably over SSH due to PTY character processing. Use the telnet connection for file transfers and SSH for interactive browsing.
BBSFirewall can redirect web browsers that hit your firewall's IP address to your BBS website.
WEB_REDIRECT_ENABLED=true
WEB_REDIRECT_URL=https://yourbbs.example.comRequires a TLS certificate. Run setup-certs.sh to get a free one from Let's Encrypt (see below).
HTTPS_REDIRECT_ENABLED=true
HTTPS_REDIRECT_PORT=443
HTTPS_CERT_PATH=./certs/fullchain.pem
HTTPS_KEY_PATH=./certs/privkey.pemWEB_REDIRECT_URL is used as the destination for both HTTP and HTTPS redirects.
Note: On Linux, binding ports 80 and 443 requires root or the
CAP_NET_BIND_SERVICEcapability.
BBSFirewall includes setup-certs.sh to get a free TLS certificate from Let's Encrypt using certbot's webroot method. The HTTP server stays running the whole time β no downtime.
- Port 80 must be reachable from the internet
WEB_REDIRECT_ENABLED=truein your.env- BBSFirewall must be running before you run the script
- DNS must already point your domain to this server
bash setup-certs.sh yourdomain.com --email you@example.comThe script will:
- Install certbot if it's not already there
- Verify port 80 is listening
- Run certbot in webroot mode (no service interruption)
- Copy the certificates to
./certs/ - Install a renewal hook that auto-copies new certs and restarts BBSFirewall
- Print the
.envlines to add
bash setup-certs.sh yourdomain.com --renewRenewal is also automatic via certbot's built-in systemd timer β you don't have to think about it.
BBSFirewall can prepend a PROXY Protocol v1 header to every backend connection so the destination BBS can see the real client IP instead of the firewall's IP.
PROXY_PROTOCOL_ENABLED=trueWhen enabled, every connection to the backend starts with:
PROXY TCP4 203.0.113.45 192.0.2.1 56324 23
Fields: protocol, real client IP, proxy IP, client port, proxy port.
This works for both telnet and SSH connections.
β οΈ Important: The backend BBS software must support PROXY Protocol, or have a module/plugin that reads and strips the header before the BBS sees it. Enabling this against an incompatible backend will break all connections β the BBS will receive the header line as garbage data at the start of every session.
Compatible backends include HAProxy, Nginx, Synchronet, WWIV, Mystic, and any software with a PROXY Protocol module. Standard MajorBBS/Worldgroup requires a companion MBBS module to handle the header.
Full spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
Block connections from specific countries using a local MaxMind GeoLite2 database. No external API calls β the lookup happens entirely on your server.
npm run setup-geoipFollow the on-screen instructions. A free MaxMind account is required at maxmind.com.
BLOCKED_COUNTRIES=CN,RU,KP,IR
BLOCK_UNKNOWN_COUNTRIES=falseUse ISO 3166-1 alpha-2 two-letter country codes.
IPs in the whitelist bypass all firewall rules β country blocking, rate limiting, blocklist, per-IP limits. Use this for trusted admin IPs.
cp whitelist.txt.example whitelist.txt
# Add your trusted IPs β one per line, CIDR ranges supportedWHITELIST_PATH=./whitelist.txtPermanently blocked IPs. Rejected immediately on connect regardless of any other setting.
cp blocklist.txt.example blocklist.txt
# Add IPs to block β one per line, CIDR ranges supportedBLOCKLIST_PATH=./blocklist.txtAutomatically blocks IPs that hammer the connection too frequently. Temporary blocks expire after RATE_LIMIT_BLOCK_DURATION_MS.
RATE_LIMIT_ENABLED=true
MAX_CONNECTIONS_PER_WINDOW=10
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_BLOCK_DURATION_MS=300000Every incoming connection goes through these checks in order:
| Step | Check | Action |
|---|---|---|
| 1 | β Whitelist | Matched IPs are allowed immediately β all other checks skipped |
| 2 | π« Blocklist | Permanent block |
| 3 | β‘ Rate limit | Temporary block if threshold exceeded |
| 4 | π Per-IP connection limit | Reject if over MAX_CONNECTIONS_PER_IP |
| 5 | π GeoIP country check | Block if country is in BLOCKED_COUNTRIES |
| 6 | π Forward | Connect to backend BBS |
BBSFirewall can detect whether a connecting SSH client prefers UTF-8 or CP437 and route them to separate backend ports. Useful if your BBS software runs separate instances for each encoding.
ENCODING_DETECTION=true
BACKEND_PORT_CP437=2323
BACKEND_PORT_UTF8=2423Detection for SSH clients is based on the client's LANG/LC_ALL environment variables and terminal type. Telnet connections always default to CP437 β there's no reliable way to detect encoding before the connection is established.
BBSFirewall/
βββ server.js # Main entry point, connection manager, graceful shutdown
βββ proxy.js # Bidirectional TCP proxy handler
βββ ssh.js # SSH server β accepts any credentials, proxies to telnet
βββ web-redirect.js # HTTP + HTTPS redirect server with ACME challenge support
βββ proxy-protocol.js # PROXY Protocol v1 header builder
βββ config.js # Configuration loading and validation
βββ logger.js # Log level filtering
βββ geoip.js # MaxMind GeoLite2 country lookup
βββ ipfilter.js # IP lists, rate limiting, per-IP connection tracking
βββ encoding-detector.js # UTF-8/CP437 detection logic
βββ download-geoip.js # GeoIP database setup helper
βββ setup-certs.sh # Let's Encrypt certificate setup script
βββ ecosystem.config.js # PM2 process config
βββ package.json
βββ .env.example # Documented example configuration
βββ whitelist.txt.example
βββ blocklist.txt.example
βββ data/ # GeoIP database (not included, run setup-geoip)
BBSFirewall is developed and maintained by Mark Laudenbach at Sysop Network.
Built on the foundation of bbsfw by Ryan Fantus. Solid starting point β thanks for putting that together.
MIT β Copyright (c) 2026 Sysop Network