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
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"net/http"
|
|
"net/netip"
|
|
"slices"
|
|
)
|
|
|
|
// peerAddress is the address of the request's TCP peer.
|
|
func peerAddress(r *http.Request) netip.Addr {
|
|
addrPort, err := netip.ParseAddrPort(r.RemoteAddr)
|
|
if err != nil {
|
|
return netip.Addr{}
|
|
}
|
|
|
|
return addrPort.Addr().Unmap()
|
|
}
|
|
|
|
// requestIDHeader carries the request's id, from rproxy.
|
|
const requestIDHeader = "X-Request-ID"
|
|
|
|
// requestID is the request's id: the one a question gives, or for any other
|
|
// request a new random one.
|
|
func requestID(r *http.Request, isQuestion bool) string {
|
|
if isQuestion {
|
|
return r.Header.Get(requestIDHeader)
|
|
}
|
|
|
|
return rand.Text()
|
|
}
|
|
|
|
// scheme is how the visitor reached rproxy, or the trusted proxy in front of
|
|
// it, as a question says in X-Forwarded-Proto, or otherwise http.
|
|
func scheme(r *http.Request, isQuestion bool) string {
|
|
proto := r.Header.Get("X-Forwarded-Proto")
|
|
if !isQuestion || proto == "" {
|
|
return "http"
|
|
}
|
|
|
|
return proto
|
|
}
|
|
|
|
// clientGroup is the client a request is counted toward: its IPv4
|
|
// address, or its IPv6 group, the netblock its IPv6 address is in of the
|
|
// length SWWAF_IPV6_GROUP_PREFIX sets, a /64 by default, since one abuser
|
|
// usually holds a whole /64. An IPv4 address in IPv6 form counts as IPv4.
|
|
func (h *handler) clientGroup(addr netip.Addr) netip.Prefix {
|
|
addr = addr.Unmap()
|
|
if addr.Is6() {
|
|
return netip.PrefixFrom(addr, h.config.IPv6GroupPrefix).Masked()
|
|
}
|
|
|
|
return netip.PrefixFrom(addr, addr.BitLen())
|
|
}
|
|
|
|
// isInside reports whether addr is in one of the netblocks.
|
|
func isInside(addr netip.Addr, netblocks []netip.Prefix) bool {
|
|
return slices.ContainsFunc(netblocks, func(netblock netip.Prefix) bool {
|
|
return netblock.Contains(addr)
|
|
})
|
|
}
|