rproxy and servewaf replace the one-process smallwebwaf (closes #172, closes #165)
check / check (push) Failing after 14m44s
check / check (push) Failing after 14m44s
`smallwebwaf` with no command now prints the commands and exits with status 2. The image runs rproxy, or servewaf with SMALLWEBWAF_COMMAND=servewaf. The passing-on code, its settings and its log fields and metric go. internal/proxy keeps the checks, answers, reports and own endpoints; its tests run through the real rproxy. The documents describe the pair. Judgement call: clients.json keeps token_refused, since state files refuse unknown fields. Deviation: script/gitea-requests sets SWWAF_LOG_REQUEST_HEADERS, to label requests. Partially verified: make example-app and make gitea-requests never built the image, as Ubuntu's snapshot service answered 5xx. Model: opus-5-5
This commit was merged in pull request #184.
This commit is contained in:
+11
-41
@@ -9,7 +9,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -50,16 +49,13 @@ func (rq *request) answerHealth() {
|
||||
_, _ = io.WriteString(rq.out, "ok\n")
|
||||
}
|
||||
|
||||
// answerAdmin answers a request for smallwebwaf itself, under
|
||||
// /_smallwebwaf/, once it has passed the checks. Each endpoint needs a
|
||||
// token, sent as Authorization: Bearer <token>: the metrics
|
||||
// SWWAF_METRICS_TOKEN, the others SWWAF_ADMIN_TOKEN. A request without
|
||||
// it is refused with 401, which counts toward the error burst. An
|
||||
// endpoint whose token is unset answers 404, as any other request under
|
||||
// /_smallwebwaf/ does.
|
||||
// answerAdmin answers a request for one of servewaf's own endpoints. Each
|
||||
// needs a token, sent as Authorization: Bearer <token>: the metrics
|
||||
// SWWAF_METRICS_TOKEN, the others SWWAF_ADMIN_TOKEN. A request without it is
|
||||
// refused with 401. An endpoint whose token is unset answers 404, as any other
|
||||
// path does.
|
||||
func (rq *request) answerAdmin() {
|
||||
rq.line.Action = requestlog.ActionAdmin
|
||||
rq.startClientResponseTimeout()
|
||||
|
||||
token, answer := rq.endpoint()
|
||||
|
||||
@@ -67,12 +63,8 @@ func (rq *request) answerAdmin() {
|
||||
case token == "":
|
||||
http.Error(rq.out, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
case !hasToken(rq.in, token):
|
||||
rq.tokenRefused = true
|
||||
rq.out.Header().Set("WWW-Authenticate", "Bearer")
|
||||
rq.answer(refusal{
|
||||
status: http.StatusUnauthorized,
|
||||
action: requestlog.ActionAdmin,
|
||||
})
|
||||
http.Error(rq.out, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
default:
|
||||
answer()
|
||||
}
|
||||
@@ -129,26 +121,7 @@ type banToAdd struct {
|
||||
// an admin, from now for the duration the body gives, with its reason,
|
||||
// and answers with that ban.
|
||||
func (rq *request) addBan() {
|
||||
// The body must arrive within SWWAF_CLIENT_REQUEST_TIMEOUT, as any
|
||||
// other request's must.
|
||||
rq.stopReadingBody(rq.clientRequestDeadline())
|
||||
|
||||
toAdd, err := rq.readBanToAdd()
|
||||
if refused := rq.refused.Load(); refused != nil {
|
||||
rq.answer(*refused) // the body is over SWWAF_REQUEST_MAX_BYTES
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
rq.answer(refusal{
|
||||
status: http.StatusRequestTimeout,
|
||||
action: requestlog.ActionTimedOut,
|
||||
limit: "SWWAF_CLIENT_REQUEST_TIMEOUT",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
netblock netip.Prefix
|
||||
@@ -177,12 +150,9 @@ func (rq *request) addBan() {
|
||||
// readBanToAdd reads the body of POST BansPath: a JSON object with
|
||||
// nothing but whitespace after it, in at most banBodyMaxBytes.
|
||||
func (rq *request) readBanToAdd() (banToAdd, error) {
|
||||
var body io.ReadCloser = http.NoBody
|
||||
if rq.body != nil {
|
||||
body = rq.body
|
||||
}
|
||||
data, err := io.ReadAll(http.MaxBytesReader(nil, rq.in.Body, banBodyMaxBytes))
|
||||
rq.requestBytes = int64(len(data))
|
||||
|
||||
data, err := io.ReadAll(http.MaxBytesReader(nil, body, banBodyMaxBytes))
|
||||
if err != nil {
|
||||
return banToAdd{}, fmt.Errorf("%w: %w", errNotBanToAdd, err)
|
||||
}
|
||||
@@ -280,9 +250,9 @@ type clientAnswer struct {
|
||||
Bans []state.BanEntry `json:"bans"`
|
||||
}
|
||||
|
||||
// showClient answers GET ClientsPath<ip> with what smallwebwaf knows of
|
||||
// the client: its counters, its history, which holds its country as last
|
||||
// looked up and its offences, and its bans with their notes.
|
||||
// showClient answers GET ClientsPath<ip> with what servewaf knows of the client:
|
||||
// its counters, its history, which holds its country as last looked up and its
|
||||
// offences, and its bans with their notes.
|
||||
func (rq *request) showClient() {
|
||||
addr, err := pathAddress(rq.in.URL.Path, ClientsPath)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user