Gitea setup: Core Rule Set blocks attacks, not ordinary Gitea traffic (closes #198)
check / check (push) Successful in 3m20s
check / check (push) Successful in 3m20s
The Gitea setup runs the Core Rule Set in block mode. Rules that refused ordinary Gitea requests are switched off only where they misfired: - deploy/gitea/crs-exclusions.conf, loaded through the new setting SWWAF_CORE_RULE_SET_EXCLUSIONS_FILE, covers Gitea's search, file-name and branch-name fields, Referer, and file paths; - smallwebwaf's defaults gain a seventh change: the registry upload path's from parameter skips 932260. A file that cannot be read, does not parse or names an id from 900000 to 900999 stops servewaf at start. internal/waf's tests and script/gitea-setup send the recorded requests in internal/waf/testdata/gitea-requests.txt and three attacks. Model: opus-5-5
This commit is contained in:
1 parent
21cac70b50
commit
7e26fe37bc
17 files changed
+806
-239
No files matched your search
@@ -4,7 +4,8 @@
|
||||
# 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
|
||||
# source found the Core Rule Set refuses without the exclusions in
|
||||
# deploy/gitea/crs-exclusions.conf, 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
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
# rproxy, and that servewaf checks them;
|
||||
# - that gitea logs the visitor's address, not rproxy's;
|
||||
# - that a request from a container on the shared network skips the checks;
|
||||
# - that the Core Rule Set refuses none of the ordinary gitea requests in
|
||||
# internal/waf/testdata/gitea-requests.txt, nor a fetch, nor API calls with
|
||||
# JSON bodies;
|
||||
# - that it refuses SQL injection, path traversal and script injection in a
|
||||
# query string;
|
||||
# - that with servewaf stopped, gitea still answers through rproxy.
|
||||
# It publishes no port of traefik's, since this host's ports 80 and 443 may be
|
||||
# taken. It reaches traefik at its address on the shared network instead, so
|
||||
@@ -136,6 +141,65 @@ passed_through() {
|
||||
"\"client_ip\":\"$gateway\"" '"action":"forward"' '"counts":' '"waf_score":'
|
||||
}
|
||||
|
||||
# refused: print each request servewaf logged as refused, for any reason.
|
||||
refused() {
|
||||
compose logs --no-color --no-log-prefix servewaf 2>&1 |
|
||||
grep -F '"servewaf_line":"report"' | grep -v -F '"action":"forward"' || true
|
||||
}
|
||||
|
||||
# ordinary_requests: sign in as the owner, save a file and open an issue
|
||||
# through the API with JSON bodies, then send, signed in, each request in
|
||||
# internal/waf/testdata/gitea-requests.txt, with that request's own address as
|
||||
# its Referer, as a page opened from it has. One a second keeps them under the
|
||||
# default limit of 120 requests a minute, since a request refused for a limit is
|
||||
# never inspected. Fail if servewaf refuses any of them.
|
||||
ordinary_requests() {
|
||||
send /user/login --cookie-jar "$WORK/cookies" >/dev/null
|
||||
status="$(send /user/login --cookie "$WORK/cookies" --cookie-jar "$WORK/cookies" \
|
||||
--header "Origin: https://$SITE" --data-urlencode user_name=owner \
|
||||
--data-urlencode "password=$PASSWORD")"
|
||||
[ "$status" = 303 ] || fail "signing in was answered $status"
|
||||
|
||||
content="$(printf 'declare const env: Record<string, string>;\n' | base64)"
|
||||
status="$(send /api/v1/repos/owner/repo/contents/process.env.d.ts \
|
||||
--user "owner:$PASSWORD" --header "Content-Type: application/json" \
|
||||
--data "{\"message\":\"Add process.env.d.ts\",\"content\":\"$content\"}")"
|
||||
[ "$status" = 201 ] || fail "saving a file through the API was answered $status"
|
||||
status="$(send /api/v1/repos/owner/repo/issues --user "owner:$PASSWORD" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"title":"ls -la fails","body":"SELECT * FROM users; <img src=x>"}')"
|
||||
[ "$status" = 201 ] || fail "opening an issue through the API was answered $status"
|
||||
|
||||
grep -v -e '^#' -e '^$' "$ROOT/internal/waf/testdata/gitea-requests.txt" |
|
||||
while IFS= read -r target; do
|
||||
send "$target" --globoff --cookie "$WORK/cookies" \
|
||||
--header "Referer: https://$SITE$target" >/dev/null
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# The last request's report is logged once its answer has ended.
|
||||
send /api/v1/settings/api >/dev/null
|
||||
wait_for "servewaf logged no report of the last ordinary request" logged servewaf \
|
||||
'"servewaf_line":"report"' '"path":"/api/v1/settings/api"'
|
||||
refusals="$(refused)"
|
||||
[ -z "$refusals" ] || fail "servewaf refused ordinary requests:
|
||||
$refusals"
|
||||
}
|
||||
|
||||
# attacks: send SQL injection, path traversal and script injection in a query
|
||||
# string, and fail unless the Core Rule Set refuses each.
|
||||
attacks() {
|
||||
# The traversal climbs one directory: the default rule file refuses two or
|
||||
# more first, before the Core Rule Set runs.
|
||||
for query in "id=1'%20OR%20'1'%3D'1" "id=..%2Fcustom%2Fconf%2Fapp.ini" \
|
||||
"id=%3Cscript%3Ealert(1)%3C%2Fscript%3E"; do
|
||||
status="$(send "/owner/repo?$query")"
|
||||
[ "$status" = 403 ] || fail "the attack ?$query was answered $status"
|
||||
wait_for "servewaf logged no refusal of ?$query" logged servewaf \
|
||||
'"servewaf_line":"report"' '"action":"waf_blocked"' "\"query\":\"$query\""
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
trap cleanup EXIT
|
||||
@@ -204,6 +268,12 @@ main() {
|
||||
esac
|
||||
echo "gitea-setup: a request from the shared network skips the checks"
|
||||
|
||||
site_git -C "$WORK/clone" fetch --quiet || fail "the fetch failed"
|
||||
ordinary_requests
|
||||
echo "gitea-setup: the Core Rule Set refuses no ordinary gitea request"
|
||||
attacks
|
||||
echo "gitea-setup: the Core Rule Set refuses the attacks"
|
||||
|
||||
compose stop servewaf >/dev/null 2>&1 || fail "servewaf did not stop"
|
||||
status="$(send /owner/repo)"
|
||||
[ "$status" = 200 ] ||
|
||||
|
||||
Reference in new issue
Block a user