sirius-press: let the published port be bound to localhost

A stack with TLS in front of it was still publishing nginx on 0.0.0.0,
so the admin stayed reachable over plain HTTP on a raw port while the
proxy served the same site over HTTPS beside it. On this project's own
instance that meant wp-login.php answering 200 to the open internet.

There is no password to steal — the account is a key — but the session
cookie is the account, and a cookie read off the wire is a logged-in
administrator.

HTTP_BIND defaults to 0.0.0.0, so a stack with nothing in front behaves
exactly as before. The records doc now also explains the pinned
self-signed certificate route, which is how a BCNR name gets HTTPS
without a certificate authority — and why an editor needs it at all:
the publishing key is derived in the page, and crypto.subtle does not
exist outside a secure context.
This commit is contained in:
Local Dev 2026-10-03 16:49:23 +02:00
parent 373394b534
commit 582cc81ffe
3 changed files with 53 additions and 1 deletions

View file

@ -21,6 +21,12 @@ SITE_URL=
# owns 80 and you are running a reverse proxy in front.
HTTP_PORT=80
# Which address that port is published on. Leave it alone for a stack that
# faces the internet directly. Set it to 127.0.0.1 when a reverse proxy on
# this machine terminates TLS — the proxy can still reach it, nobody else
# can, and the admin stops being available over plain HTTP to the world.
#HTTP_BIND=127.0.0.1
WP_DEBUG=false
# --- the publishing key ------------------------------------------------------

View file

@ -74,7 +74,13 @@ services:
depends_on:
- app
ports:
- "${HTTP_PORT:-80}:80"
# HTTP_BIND exists for the case where something else terminates TLS in
# front of this stack. Set it to 127.0.0.1 and the port stops being
# reachable from outside the machine, which matters more here than on a
# normal site: wp-admin is the whole account, and a session cookie
# crossing the internet in clear text hands it over. Default is
# unchanged, so a stack with nothing in front still works.
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
# nginx serves static files directly and only proxies PHP, so it needs

View file

@ -36,6 +36,46 @@ else's namespace, and whoever controls that namespace can redirect your
visitors. If you are using BCNR to get out from under ICANN, pointing your name
back into it is worth doing deliberately rather than by default.
### Put TLS in front of it, with `ip` + `tls`
An `ip` record on its own is served over plain HTTP, and for a site with an
editor that is not good enough. Two reasons, and the second is the one people
miss:
1. **The session cookie is the account.** There is no password to steal here,
but a cookie read off the wire is a logged-in admin, and a BCNR name does
not stop a network between you and the server.
2. **Manual publishing does not work without it.** The key that signs an
upload is derived in the page, and `crypto.subtle` does not exist outside a
secure context. Over plain HTTP the Publishing screen can queue work and
never sign it.
You do not need a certificate authority. Put a reverse proxy in front with a
**self-signed certificate**, and record its fingerprint on chain:
```bash
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
-keyout server.key -out server.crt -days 3650 \
-subj "/CN=yourname.x" -addext "subjectAltName=DNS:yourname.x,DNS:*.yourname.x"
# the value for the `tls` record
openssl x509 -in server.crt -noout -fingerprint -sha256 \
| sed 's/.*=//; s/://g' | tr 'A-Z' 'a-z'
```
Set `ip` to the server's address and `tls` to that fingerprint. Theseus and
Ariadne both refuse any certificate whose DER SHA-256 does not match — which
is a narrower claim than a CA makes, and it expires on your schedule rather
than someone else's. Rotating the certificate is one record update.
Then stop publishing the container's port to the world, since the proxy is now
the only thing that should reach it:
```ini
# .env
HTTP_BIND=127.0.0.1
```
---
## Telling WordPress its own address