Author SHA1 Message Date
clawbot 20f99b2cdd deploy/gitea: Gitea behind traefik, rproxy and servewaf, with its test (closes #178)
check / check (push) Successful in 4m18s
deploy/gitea/compose.yaml runs traefik, stock Gitea 1.27.3, rproxy and
servewaf on one network with a fixed subnet. Its containers skip
servewaf's checks; the network's gateway is left out of that range.
rproxy takes uploads of 2 GiB and 30 minutes, and cuts responses at 15
minutes. script/gitea-setup (make gitea-setup) brings the setup up and
checks it. docs/gitea.md is rewritten around the setup.

Judgement call: the Core Rule Set runs in detect mode, so it refuses nothing.
Judgement call: traefik serves its own certificate; no certificate resolver.
Partially verified: the image build failed three runs on Ubuntu's snapshot
service, so the checks ran only against a stand-in image with the same binary.

Model: deepseek-v4-pro
2026-11-05 22:35:23 +00:00
2 changed files with 31 additions and 21 deletions
+14 -8
View File
@@ -2,7 +2,8 @@
# docs/gitea.md says what each setting here does and why. Before starting it: # docs/gitea.md says what each setting here does and why. Before starting it:
# - set SMALLWEBWAF_IMAGE to the smallwebwaf image, named by digest; # - set SMALLWEBWAF_IMAGE to the smallwebwaf image, named by digest;
# - replace git.example.org with the site's host name; # - replace git.example.org with the site's host name;
# - replace each 10.99.0 with the first three numbers of a /24 subnet that # - replace each 10.99.0 with the first three numbers of a /24 subnet, and
# each fd10:99 with the first two hextets of a /64 subnet, using subnets
# no other network on the host uses. # no other network on the host uses.
# Then run `docker compose up --detach` in this directory. # Then run `docker compose up --detach` in this directory.
# script/gitea-setup checks this setup. # script/gitea-setup checks this setup.
@@ -54,9 +55,9 @@ services:
# Gitea starts at its defaults, with SQLite, instead of showing its # Gitea starts at its defaults, with SQLite, instead of showing its
# install page to whoever opens it first. # install page to whoever opens it first.
GITEA__security__INSTALL_LOCK: "true" GITEA__security__INSTALL_LOCK: "true"
# rproxy's address. Gitea then takes each visitor's address from # rproxy's addresses. Gitea then takes each visitor's address from
# the X-Real-Ip header traefik sets and rproxy passes on. # the X-Real-Ip header traefik sets and rproxy passes on.
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: 10.99.0.2 GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: 10.99.0.2,fd10:99::2
volumes: volumes:
- gitea-data:/data - gitea-data:/data
networks: networks:
@@ -82,6 +83,7 @@ services:
networks: networks:
shared: shared:
ipv4_address: 10.99.0.2 ipv4_address: 10.99.0.2
ipv6_address: fd10:99::2
logging: *logging logging: *logging
restart: unless-stopped restart: unless-stopped
@@ -92,7 +94,7 @@ services:
# The containers on the shared network skip the checks. Add the # The containers on the shared network skip the checks. Add the
# address of an automation fleet on another host here too; without # address of an automation fleet on another host here too; without
# that, the default request limits are sized for such a fleet. # that, the default request limits are sized for such a fleet.
SWWAF_ALLOW_NETS: 10.99.0.128/25 SWWAF_ALLOW_NETS: 10.99.0.128/25,fd10:99:0:0:8000::/65
# In block mode the Core Rule Set refuses everyday Gitea searches # In block mode the Core Rule Set refuses everyday Gitea searches
# and file names, and 31 refusals in a minute ban the address. # and file names, and 31 refusals in a minute ban the address.
SWWAF_WAF_MODE: detect SWWAF_WAF_MODE: detect
@@ -104,16 +106,20 @@ services:
restart: unless-stopped restart: unless-stopped
networks: networks:
# The network traefik and Gitea share. Its containers get addresses in its # The network traefik and Gitea share. Its containers get addresses in the
# upper half, which servewaf lets skip the checks. The gateway, the host's # upper half of each subnet, which servewaf lets skip the checks. Each
# own address on it, stays out of that half: requests docker passes on from # subnet's gateway, the host's own address on it, stays out of that half:
# a published port, such as those of visitors over IPv6, can come from it. # requests docker passes on from a published port can come from it.
shared: shared:
enable_ipv6: true
ipam: ipam:
config: config:
- subnet: 10.99.0.0/24 - subnet: 10.99.0.0/24
ip_range: 10.99.0.128/25 ip_range: 10.99.0.128/25
gateway: 10.99.0.1 gateway: 10.99.0.1
- subnet: fd10:99::/64
ip_range: fd10:99:0:0:8000::/65
gateway: fd10:99::1
volumes: volumes:
gitea-data: gitea-data:
+17 -13
View File
@@ -19,7 +19,8 @@ To start it:
1. In `deploy/gitea/compose.yaml`, replace `git.example.org` with the site's 1. In `deploy/gitea/compose.yaml`, replace `git.example.org` with the site's
host name. host name.
2. Replace each `10.99.0` with the first three numbers of a `/24` subnet that no 2. Replace each `10.99.0` with the first three numbers of a `/24` subnet, and
each `fd10:99` with the first two hextets of a `/64` subnet, using subnets no
other network on the host uses. other network on the host uses.
3. Set `SMALLWEBWAF_IMAGE` to the `smallwebwaf` image, named by digest. 3. Set `SMALLWEBWAF_IMAGE` to the `smallwebwaf` image, named by digest.
4. Run `docker compose up --detach` in `deploy/gitea`. 4. Run `docker compose up --detach` in `deploy/gitea`.
@@ -74,9 +75,9 @@ one minute pass the minute's limit of 4 GiB, and ban the address.
`SWWAF_WAF_MODE` is `detect`. The Core Rule Set then logs a match, with its rule `SWWAF_WAF_MODE` is `detect`. The Core Rule Set then logs a match, with its rule
ids, and refuses nothing. In `block` mode, the default, it refuses everyday ids, and refuses nothing. In `block` mode, the default, it refuses everyday
gitea searches and file names, which "What the Core Rule Set refuses in `block` gitea searches and file names. The section "What the Core Rule Set refuses in
mode" below lists. Each such refusal counts toward the error burst, so 31 in one `block` mode" below lists them. Each such refusal counts toward the error burst,
minute ban the address. so 31 in one minute ban the address.
Every other check stays at its default. A client may make 500 requests a minute, Every other check stays at its default. A client may make 500 requests a minute,
20,000 an hour and 300,000 a day. Archive downloads and blame and history pages 20,000 an hour and 300,000 a day. Archive downloads and blame and history pages
@@ -86,14 +87,17 @@ are what scrapers hammer, and these limits do most of the work there.
Containers on the shared network skip `servewaf`'s checks: Containers on the shared network skip `servewaf`'s checks:
- The shared network's subnet is `10.99.0.0/24`. Docker gives its containers - The shared network's subnets are `10.99.0.0/24` and `fd10:99::/64`. Docker
addresses in the upper half, `10.99.0.128/25`, which `SWWAF_ALLOW_NETS` names. gives its containers addresses in the upper half of each, `10.99.0.128/25` and
`fd10:99:0:0:8000::/65`, which `SWWAF_ALLOW_NETS` names.
- traefik is known on the shared network by the site's host name. So a container - traefik is known on the shared network by the site's host name. So a container
there that asks for the site reaches traefik from its own address. An there that asks for the site reaches traefik from its own address. An
automation fleet in such a container skips the checks. automation fleet in such a container skips the checks.
- The network's gateway, `10.99.0.1`, is the host's own address on it. Requests - Each subnet's gateway, `10.99.0.1` and `fd10:99::1`, is the host's own address
docker passes on from a published port can come from it, such as those of on it. Requests docker passes on from a published port can come from the
visitors over IPv6. So the gateway is left out of `SWWAF_ALLOW_NETS`. gateway, so the gateways are left out of `SWWAF_ALLOW_NETS`. The network has
IPv6, so a visitor over IPv6 reaches traefik from its own address, and
servewaf checks it as itself.
- A container on the shared network that asks for the site at the host's public - A container on the shared network that asks for the site at the host's public
address arrives from the gateway. It is checked like any visitor. address arrives from the gateway. It is checked like any visitor.
- A fleet on another host can have its address added to `SWWAF_ALLOW_NETS`. - A fleet on another host can have its address added to `SWWAF_ALLOW_NETS`.
@@ -106,10 +110,10 @@ Containers on the shared network skip `servewaf`'s checks:
## gitea's settings ## gitea's settings
- `REVERSE_PROXY_TRUSTED_PROXIES` names `rproxy`'s fixed address, `10.99.0.2`. - `REVERSE_PROXY_TRUSTED_PROXIES` names `rproxy`'s fixed addresses, `10.99.0.2`
gitea then takes each visitor's address from the `X-Real-Ip` header, which and `fd10:99::2`. gitea then takes each visitor's address from the `X-Real-Ip`
traefik sets and `rproxy` passes on. Without it, gitea logs every visitor as header, which traefik sets and `rproxy` passes on. Without it, gitea logs
`rproxy`. every visitor as `rproxy`.
- `INSTALL_LOCK` is `true`, as "The setup" above says. - `INSTALL_LOCK` is `true`, as "The setup" above says.
## Methods ## Methods