Deploy model: listen port, token files, state directory owner #41

Merged
clawbot merged 1 commits from issue-33-deploy-open-points into next 2026-10-04 01:42:45 +02:00
2 changed files with 42 additions and 21 deletions
+6 -1
View File
@@ -313,7 +313,12 @@ exec chpst -u app:app /usr/local/bin/app \
- Port 8080 is the only one the app must leave free: the health check, the - Port 8080 is the only one the app must leave free: the health check, the
metrics and ban management are all on it, under `/_smallwebwaf/`. The image's metrics and ban management are all on it, under `/_smallwebwaf/`. The image's
health check passes while `smallwebwaf` answers and the app accepts health check passes while `smallwebwaf` answers and the app accepts
connections. connections. `SWWAF_LISTEN_ADDR` can move `smallwebwaf` to another port, which
the app then leaves free instead; the health check follows it, and traefik's
labels must point at it. The address part of `SWWAF_LISTEN_ADDR` stays empty
(for example `:9000`, never `127.0.0.1:9000`), so `smallwebwaf` keeps
listening on every address: traefik reaches it on the container's address, and
the health check on `127.0.0.1`.
- `smallwebwaf` keeps its state files in `/var/lib/smallwebwaf`. Mount a volume - `smallwebwaf` keeps its state files in `/var/lib/smallwebwaf`. Mount a volume
there to keep bans and client history when a deploy replaces the container; there to keep bans and client history when a deploy replaces the container;
without one, it still starts. without one, it still starts.
+36 -20
View File
@@ -1170,8 +1170,9 @@ The image holds:
- The `smallwebwaf` binary, and a user of its own, `smallwebwaf` (uid and gid - The `smallwebwaf` binary, and a user of its own, `smallwebwaf` (uid and gid
65532). 65532).
- The service directory `/etc/service/smallwebwaf`, whose `run` script waits one - The service directory `/etc/service/smallwebwaf`, whose `run` script waits one
second (`sleep 1`), makes `SWWAF_STATE_DIR` belong to the `smallwebwaf` user, second (`sleep 1`), makes `SWWAF_STATE_DIR` and every file in it belong to the
and starts `smallwebwaf` as that user with runit's `chpst`. `smallwebwaf` user, and starts `smallwebwaf` as that user with runit's
`chpst`.
- The directory `/var/lib/smallwebwaf` for the state files, and - The directory `/var/lib/smallwebwaf` for the state files, and
`/etc/smallwebwaf/rules.d` with the default rule file (see "Rule files"). `/etc/smallwebwaf/rules.d` with the default rule file (see "Rule files").
- Port 8080 declared (`EXPOSE 8080`), and the health check described below - Port 8080 declared (`EXPOSE 8080`), and the health check described below
@@ -1261,18 +1262,24 @@ The two processes:
status into its directory under `/etc/service`. status into its directory under `/etc/service`.
The health check: the image's `HEALTHCHECK` passes while `smallwebwaf` answers The health check: the image's `HEALTHCHECK` passes while `smallwebwaf` answers
`GET /_smallwebwaf/healthz` on `127.0.0.1:8080` and the app accepts connections `GET /_smallwebwaf/healthz` on `127.0.0.1`, at the port in `SWWAF_LISTEN_ADDR`,
at the address in `SWWAF_UPSTREAM_URL`, and fails when either does not. The and the app accepts connections at the address in `SWWAF_UPSTREAM_URL`, and
container therefore shows as healthy only while both processes are up. An app fails when either does not. The container therefore shows as healthy only while
with a health check of its own can replace the image's `HEALTHCHECK` with one both processes are up. An app with a health check of its own can replace the
that checks both. image's `HEALTHCHECK` with one that checks both.
Ports: `smallwebwaf` listens on port 8080 on every address and on no other port; Ports: `smallwebwaf` listens on port 8080 on every address and on no other port;
its health check, metrics and ban management are all on that listener, under its health check, metrics and ban management are all on that listener, under
`/_smallwebwaf/` (see "Admin endpoints"). The app must leave port 8080 free. It `/_smallwebwaf/` (see "Admin endpoints"). `SWWAF_LISTEN_ADDR` may set another
listens on `127.0.0.1:8081` only, so that nothing outside the container reaches port: the image's health check takes its port from that setting, and traefik's
it except through `smallwebwaf`: an app that listens on every address can be port label (`traefik.http.services.<name>.loadbalancer.server.port`) must name
reached around `smallwebwaf` by anything that reaches the container. the same port, and the app must leave that port free. The address part of
`SWWAF_LISTEN_ADDR` stays empty (for example `:9000`, never `127.0.0.1:9000`),
so `smallwebwaf` keeps listening on every address: traefik reaches it on the
container's address, and the health check on `127.0.0.1`. The app listens on
`127.0.0.1:8081` only, so that nothing outside the container reaches it except
through `smallwebwaf`: an app that listens on every address can be reached
around `smallwebwaf` by anything that reaches the container.
State: `smallwebwaf` keeps its state files in `/var/lib/smallwebwaf` State: `smallwebwaf` keeps its state files in `/var/lib/smallwebwaf`
(`SWWAF_STATE_DIR`), a directory of its own beside the app's data, which the app (`SWWAF_STATE_DIR`), a directory of its own beside the app's data, which the app
@@ -1280,13 +1287,21 @@ keeps in directories of its own, such as `/var/lib/app`. Without a volume there,
the files live in the container: they survive a restart of the container and are the files live in the container: they survive a restart of the container and are
lost when a deploy replaces it. A volume mounted at `/var/lib/smallwebwaf`, lost when a deploy replaces it. A volume mounted at `/var/lib/smallwebwaf`,
named or a host directory, keeps them across deploys; the `run` script of named or a host directory, keeps them across deploys; the `run` script of
`smallwebwaf` makes it belong to the `smallwebwaf` user, so a host directory `smallwebwaf` makes it and every file in it belong to the `smallwebwaf` user, so
mounted there needs no change of owner. It is a volume of its own, separate from a host directory mounted there needs no change of owner, and files an earlier
the app's, holds a few tens of MiB at most with the defaults (see "Persistent owner left in it can be read and replaced. It is a volume of its own, separate
state"), and needs no backup beyond whatever the host already does. The image from the app's, holds a few tens of MiB at most with the defaults (see
declares no volume, since every app image built on it would inherit it. "Persistent state"), and needs no backup beyond whatever the host already does.
Milestone 2 (https://git.eeqj.de/sneak/smallwebwaf/issues/14) writes no state The image declares no volume, since every app image built on it would inherit
files and needs no volume. it. Milestone 2 (https://git.eeqj.de/sneak/smallwebwaf/issues/14) writes no
state files and needs no volume.
Tokens: a token given as a file (`SWWAF_ADMIN_TOKEN_FILE`,
`SWWAF_METRICS_TOKEN_FILE`) is out of the app's reach only while the
`smallwebwaf` user alone can read the file. The operator makes the file on the
host, owned by uid 65532, the `smallwebwaf` user, with mode `0400`, and mounts
the directory that holds it into the container read-only; the container sees the
same owner and mode.
Forwarded headers: the app's TCP peer is `smallwebwaf` on `127.0.0.1`, and the Forwarded headers: the app's TCP peer is `smallwebwaf` on `127.0.0.1`, and the
`X-Forwarded-For` the app receives ends with traefik's address, which `X-Forwarded-For` the app receives ends with traefik's address, which
@@ -1305,7 +1320,8 @@ and runs the one container as it runs any app, with the app's traefik labels,
environment variables and volumes. The labels route to port 8080 environment variables and volumes. The labels route to port 8080
(`traefik.http.services.<name>.loadbalancer.server.port=8080`), any `SWWAF_` (`traefik.http.services.<name>.loadbalancer.server.port=8080`), any `SWWAF_`
settings go with the app's environment variables, and the volume for settings go with the app's environment variables, and the volume for
`/var/lib/smallwebwaf` goes beside the app's own. `/var/lib/smallwebwaf` goes beside the app's own, as does the directory that
holds any token file.
- The endpoints of `smallwebwaf` are reached through traefik like any other - The endpoints of `smallwebwaf` are reached through traefik like any other
request, for example `https://app.example.invalid/_smallwebwaf/metrics` for a request, for example `https://app.example.invalid/_smallwebwaf/metrics` for a
@@ -1504,7 +1520,7 @@ settings go with the app's environment variables, and the volume for
stops the start. The app starts with the same environment variables as stops the start. The app starts with the same environment variables as
`smallwebwaf`, so it can read a token given as one; a token given as a file `smallwebwaf`, so it can read a token given as one; a token given as a file
that only the `smallwebwaf` user can read (`SWWAF_ADMIN_TOKEN_FILE`, that only the `smallwebwaf` user can read (`SWWAF_ADMIN_TOKEN_FILE`,
`SWWAF_METRICS_TOKEN_FILE`) is out of the app's reach. `SWWAF_METRICS_TOKEN_FILE`) is out of the app's reach (see "Deployment").
- GeoJS, the default lookup source: every new visitor's address goes to a third - GeoJS, the default lookup source: every new visitor's address goes to a third
party, and a swarm of fresh addresses, when lookups peak, is when GeoJS may party, and a swarm of fresh addresses, when lookups peak, is when GeoJS may
slow down or block `smallwebwaf`. Keeping answers for 7 days and asking about slow down or block `smallwebwaf`. Keeping answers for 7 days and asking about