Honour forwarded headers only from trusted proxies (closes #259)
check / check (push) Failing after 8s

Before, the server took the client's address from X-Forwarded-For or X-Real-IP whoever sent them, so a direct client could get past the login rate limit and choose the address and hostname recorded for its session.

A new ClientIP middleware, run before request logging, resolves the address once: forwarded headers count only when the peer is in TRUSTED_PROXIES, which defaults to the RFC 1918 ranges. A set list replaces the default, the empty string trusts nothing, and an invalid entry stops startup. The request log, login rate limiter and session creation all use the resolved address.

A proxy on the same host as a server outside Docker connects from 127.0.0.1 or ::1, which the default does not trust; the README says to list it.

Model: opus-5-5
This commit was merged in pull request #264.
This commit is contained in:
clawbot committed 2026-11-10 22:41:50 +01:00
1 parent bc8aa6bb96
commit a3d9bbfd81
10 files changed
+670 -46

No files matched your search

+2 -18
View File
@@ -42,25 +42,9 @@ func (hdlr *Handlers) maxBodySize() int64 {
return defaultMaxBodySize
}
// clientIP extracts the connecting client's IP address
// from the request, checking X-Forwarded-For and
// X-Real-IP headers before falling back to RemoteAddr.
// clientIP returns the IP address in request.RemoteAddr, which the
// ClientIP middleware has set to the client's address.
func clientIP(request *http.Request) string {
if forwarded := request.Header.Get("X-Forwarded-For"); forwarded != "" {
// X-Forwarded-For can contain a comma-separated list;
// the first entry is the original client.
parts := strings.SplitN(forwarded, ",", 2) //nolint:mnd
ip := strings.TrimSpace(parts[0])
if ip != "" {
return ip
}
}
if realIP := request.Header.Get("X-Real-IP"); realIP != "" {
return strings.TrimSpace(realIP)
}
host, _, err := net.SplitHostPort(request.RemoteAddr)
if err != nil {
return request.RemoteAddr