Ubuntu 24.04 (Minimal Server) — VM Firewall Hardening for Stoat

Ubuntu 24.04 VM Firewall Hardening (Stoat / Revolt, Option A2)

Target: Ubuntu 24.04 minimal server VM, Caddy is the public edge for revolt.yaws.com.
Goal: expose only 443/tcp (and optionally 80/tcp for redirect), lock down SSH, and ensure Docker services remain private.

Before you start

  • Confirm you have console access (or a second SSH session) in case you lock yourself out.
  • Decide whether you want HTTP (80) open for redirects. If not, skip 80 entirely.
  • Decide your trusted admin IP for SSH allow-listing (recommended).

1) Confirm what is listening (baseline)

On the VM, confirm only the expected public ports exist (typically 80, 443, and 22).

Check listening ports
ss -ltnp

You should not see internal Stoat ports (14702–14706, 27017, 9000, 5672, etc.) bound to 0.0.0.0.
In your compose file, only Caddy publishes ports, which is correct.

2) Use UFW (Recommended on Ubuntu 24.04)

Your VM has ufw installed. This is the simplest and most maintainable approach.

2.1 Set safe defaults

Defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing

2.2 Allow HTTPS (required)

Allow 443
sudo ufw allow 443/tcp

2.3 Allow HTTP (optional — only for redirect)

If you configured Caddy to redirect HTTP → HTTPS, you may choose to allow port 80.
If you do not need redirects, skip this and keep 80 closed.

Allow 80 (optional)
sudo ufw allow 80/tcp

2.4 Lock down SSH (recommended)

Best practice is to allow SSH only from your trusted admin IP. Replace YOUR.IP.ADDRESS below with your real public IP.
(If you are behind changing IPs, use a VPN or a small allow-list of trusted IPs.)

SSH allow-list
sudo ufw allow from YOUR.IP.ADDRESS to any port 22 proto tcp

If you already enabled a broad SSH allow rule previously (e.g., “OpenSSH”), you can remove it after your IP rule exists:

Remove broad SSH allow (optional)
sudo ufw status numbered
sudo ufw delete <RULE_NUMBER>

2.5 Enable UFW

Enable
sudo ufw enable

2.6 Verify active rules

Verify
sudo ufw status verbose

3) IPv6 Exposure (Decide intentionally)

Your VM is currently listening on IPv6 ([::]:80 and [::]:443).
If you do not actively use IPv6, disabling it for UFW reduces surface area.

Disable IPv6 in UFW (optional)
sudo nano /etc/default/ufw
# Set:
# IPV6=no
sudo ufw disable
sudo ufw enable

If you need IPv6, keep it enabled and ensure UFW rules apply to v6 as well (UFW can manage both when IPV6=yes).

4) Docker Reality Check (Make sure only Caddy is exposed)

Docker manipulates iptables/nft rules automatically. Your compose file only publishes ports for the caddy service,
which is correct. Confirm no other service is reachable externally.

Confirm published ports
docker ps --format "table {{.Names}}\t{{.Ports}}"
ss -ltnp
Red flags

  • Any of: 14702–14706, 27017, 9000, 5672 listening on 0.0.0.0
  • Any Docker-published ports other than 80/443 for this stack

5) External Verification (Scan from outside)

From a machine not on the VM (your workstation or another host), verify only intended ports are open:

External scan
nmap -Pn -p 22,80,443 revolt.yaws.com
  • 443/tcp should be open
  • 80/tcp open only if you chose redirect
  • 22/tcp should be filtered or open only from your admin IP
Next hardening items (recommended)

After firewall: (1) enable invite-only registration server-side, (2) replace weak RabbitMQ/MinIO credentials,
(3) set [files].encryption_key, and (4) consider fail2ban against Caddy logs.