Skip to content

Security

MrJSA edited this page Nov 2, 2026 · 3 revisions

What you should do

  • Keep .env private (chmod 600 .env); it contains the database password and is never part of the repository.
  • Use a random database password (openssl rand -hex 24), set before the first start (see Installation). The backend warns in its log (SECURITY: …) when the password from .env.example, the old default or one shorter than 16 characters is used.
  • Set UPDATE_REQUIRE_SIGNED=true in .env, so only signed releases are installed (see Updates).
  • Do not open ports 8080, 4000 or 8081 in the firewall (see Installation, step 7). Keep PUBLISH_ADDR=127.0.0.1 when a reverse proxy serves the domains: ports published by Docker or Podman bypass ufw.
  • Restrict the admin domain further (VPN, IP allow-list or HTTP basic auth) if only a few people need it (examples in Installation with Podman#7-caddy-as-reverse-proxy and Own Reverse Proxy).
  • Backups contain customer data, password hashes and your payment/email keys. They are encrypted to your age public key; keep the private key in two places off the server (see Backups). Keep the backup repository private anyway and turn on two-factor authentication for your GitHub account.
  • Only restore backups you made yourself (see Restoring a Backup).

What the shop does by itself

Containers

  • The database is on its own internal network (db_net): only the backend can reach it, and it has no internet access. Storefront, admin and the proxy cannot even resolve its name.
  • Backend (user shop, uid 10001), storefront and admin (user node) run without root rights, with all capabilities dropped that they don't need. Log files are limited to 10 MB per file.
  • NO_NEW_PRIVILEGES=true additionally stops programs from gaining privileges through setuid binaries. Recommended with Docker; leave it false with Podman on AppArmor systems (Debian, Ubuntu), where it stops PostgreSQL from starting.

Logins and forms

  • Limits per visitor IP: 10 failed admin logins and 10 failed customer logins per 15 minutes; account registrations, password resets, verification e-mails and newsletter sign-ups are limited per hour. IPv6 visitors count per /64 network.
  • Failed logins on one account add a short, growing pause (1, 2, 4 … up to 60 seconds) instead of locking the account, so nobody can lock you out of your admin account by guessing wrong on purpose.
  • Password checks (bcrypt) run on a few CPU cores at most, so a login flood can't slow down the whole server.
  • The visitor IP comes from X-Forwarded-For only behind a reverse proxy on the same server (PUBLISH_ADDR=127.0.0.1); with the ports open to the network it is taken from the connection, because the header could be forged. TRUST_PROXY in .env overrides this (see Own Reverse Proxy).

Backups

  • Encrypted with age before they leave the server; only your private key opens them. Without a public key no backup is uploaded at all (see Backups).

Database

  • Every database migration runs in one transaction: a failing update can never leave a half-changed database.
  • Restoring a backup runs only SQL: psql runs in restricted mode, so a backup file that contains psql commands (which could start programs on the server) is refused and nothing is changed.

Updates

  • The updater service can control Docker/Podman on the host (that is how it rebuilds the shop). It has no published port, is only reachable by the backend over a private network and requires a token generated on first start. Remove the updater service from docker-compose.yml if you prefer updating manually.
  • It installs only releases signed with a key that the installed version trusts (deploy/updater/allowed_signers), backs up the database first (only root can read these dumps; the last 10 are kept) and goes back to the previous version when the shop does not come back healthy.
  • Files the updater changes in the checkout are owned by root; use sudo for manual git commands in /opt/rustwebshop.

Reporting a vulnerability

Please don't open a public issue for security problems: report them privately to the maintainer (github.com/MrJSA). Leave out passwords, keys and customer data (see Reporting Issues).

Clone this wiki locally