#!/bin/sh # script/gitea-requests: build the image and run gitea 1.27.3 behind rproxy # and servewaf, three containers on one network. Every setting is at its # default but these: SWRP_UPSTREAM_URL names gitea's container, SWRP_WAF_BACKEND # names servewaf's, and SWWAF_LOG_REQUEST_HEADERS adds the header that labels a # request. Then send through rproxy the gitea requests that reading gitea's # source found the Core Rule Set refuses, which docs/gitea.md records, and a # short sample of gitea's ordinary use: signing in, pages of files and commits, # a diff, the API, a push, clone and fetch over HTTP, and two container image # pushes. Last, print each request servewaf logged, with what it did with it and # the ids of the Core Rule Set's rules that matched it. # Every request comes from one client, docker's gateway, so more than # SWWAF_ERROR_BURST_THRESHOLD, 30, refusals in a minute would ban it, and # the requests after would show as banned. The containers, the network, # the images and the files the script makes are removed however it ends; # gitea's own image stays, as the base images of a build do. It needs # network access, and a docker daemon that pushes to a registry on # localhost over plain HTTP, as docker does by default. script/check does # not run this. set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" # gitea 1.27.3, 2026-08-29 GITEA_IMAGE="gitea/gitea@sha256:87a67ee09d3ae0d1df5fda5dcda3e2a1f9236a45b0a59025d6e00e46adc43bef" # Named after this run, so that runs in other clones on the same host # never touch each other's. NAME="$("$SCRIPT_DIR/projectname")-gitea-$$" IMAGE="$NAME-image" NETWORK="$NAME" RPROXY="$NAME-rproxy" SERVEWAF="$NAME-servewaf" GITEA="$NAME-gitea" # gitea's one user, who owns the repository and the container images. PASSWORD="owner-password" BROWSER="Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0" # The header that labels a request in the report. rproxy makes every request's # id itself, so a label cannot be the X-Request-ID. LABEL_HEADER="X-Case" # The default of SWWAF_LOG_REQUEST_HEADERS, and the header above. LOGGED_HEADERS="accept,accept-language,accept-encoding,content-type,origin,range,x-case" # The files the script makes, and rproxy's address once it runs, localhost and # the port docker publishes it on: a host name, since the Core Rule Set takes a # Host that is an IP address for a warning (920350). WORK="" address="" cleanup() { # So that a signal cannot stop it partway through. trap '' HUP INT TERM docker rm --force --volumes "$RPROXY" "$SERVEWAF" "$GITEA" >/dev/null 2>&1 || true docker network rm "$NETWORK" >/dev/null 2>&1 || true docker rmi --force "$IMAGE" >/dev/null 2>&1 || true if [ -n "$address" ]; then docker rmi --force "$address/owner/python3-app:1" "$address/owner/app:1" \ >/dev/null 2>&1 || true fi rm -rf "$WORK" } fail() { echo "gitea-requests: $*" >&2 report exit 1 } # wait_for ...: run the command every second until # it succeeds, for at most a minute. wait_for() { failure="$1" shift tries=0 until "$@"; do tries=$((tries + 1)) [ "$tries" -lt 60 ] || fail "$failure" sleep 1 done } # healthy : docker says the container's health check passes. healthy() { status="$(docker inspect --format '{{.State.Health.Status}}' "$1")" [ "$status" = healthy ] } # logged ...: a line of servewaf's output holds every text, in any order. logged() { lines="$(docker logs "$SERVEWAF" 2>&1)" for text in "$@"; do lines="$(printf '%s\n' "$lines" | grep -F "$text")" || return 1 done } # send