Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d7a489f58 |
@@ -215,11 +215,12 @@ has its forwarded headers ignored, and the direct peer is logged and
|
|||||||
rate-limited instead. From a direct peer inside the set, the client address is
|
rate-limited instead. From a direct peer inside the set, the client address is
|
||||||
the right-most `X-Forwarded-For` entry that is not in the set: each proxy
|
the right-most `X-Forwarded-For` entry that is not in the set: each proxy
|
||||||
appends the address it took the request from, so the entries further left were
|
appends the address it took the request from, so the entries further left were
|
||||||
written by the client and are not used. If the search from the right finds only
|
written by the client and are not used. An entry written as an address with a
|
||||||
addresses in the set, or meets an entry that is not an IP address, the direct
|
port, such as `198.51.100.7:5000` or `[2001:db8::1]:443`, is read as that
|
||||||
peer is used. `X-Real-IP` is read only when `X-Forwarded-For` is missing or
|
address. If the search from the right finds only addresses in the set, or meets
|
||||||
empty. The container image does not use this default; see
|
an entry that is not an address at all, such as `unknown`, the direct peer is
|
||||||
[Deployment](#deployment).
|
used. `X-Real-IP` is read only when `X-Forwarded-For` is missing or empty. The
|
||||||
|
container image does not use this default; see [Deployment](#deployment).
|
||||||
|
|
||||||
A variable set to a value the server cannot use, such as `PORT=abc`,
|
A variable set to a value the server cannot use, such as `PORT=abc`,
|
||||||
`DEBUG=maybe` or a `BIND_ADDRESS` that is not an IP address, stops it from
|
`DEBUG=maybe` or a `BIND_ADDRESS` that is not an IP address, stops it from
|
||||||
|
|||||||
@@ -26,13 +26,15 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
|||||||
([#144](https://git.eeqj.de/sneak/netwatch/issues/144)). It took the left-most
|
([#144](https://git.eeqj.de/sneak/netwatch/issues/144)). It took the left-most
|
||||||
entry, which the client writes itself behind a proxy that appends to the
|
entry, which the client writes itself behind a proxy that appends to the
|
||||||
header, so a client could choose the address the rate limit counts and the
|
header, so a client could choose the address the rate limit counts and the
|
||||||
request log records. With only trusted addresses in the header, or an entry
|
request log records. An entry written as an address with a port is read as
|
||||||
that is not an IP address met on the way, the direct peer is used. `X-Real-IP`
|
that address. With only trusted addresses in the header, or an entry that is
|
||||||
is read only without `X-Forwarded-For`, and every `X-Forwarded-For` header
|
not an address at all (such as `unknown`) met on the way, the direct peer is
|
||||||
line is read, not only the first. The image's nginx, and the
|
used. `X-Real-IP` is read only without `X-Forwarded-For`, and every
|
||||||
`TRUSTED_PROXIES=127.0.0.1/32` it starts the backend with, are unchanged. Unit
|
`X-Forwarded-For` header line is read, not only the first. The image's nginx,
|
||||||
tests resolve the address from chains ending in an untrusted address, in
|
and the `TRUSTED_PROXIES=127.0.0.1/32` it starts the backend with, are
|
||||||
trusted ones, of only trusted ones, and split over two header lines
|
unchanged. Unit tests resolve the address from chains ending in an untrusted
|
||||||
|
address, in trusted ones, of only trusted ones, with ports, with an entry that
|
||||||
|
is not an address, and split over two header lines
|
||||||
- 2026-10-18: after an interval change, the summary's Reachable slot reads
|
- 2026-10-18: after an interval change, the summary's Reachable slot reads
|
||||||
`--/--` in white, as at page load, until the second round since the change
|
`--/--` in white, as at page load, until the second round since the change
|
||||||
ends ([#140](https://git.eeqj.de/sneak/netwatch/issues/140)). It used to read
|
ends ([#140](https://git.eeqj.de/sneak/netwatch/issues/140)). It used to read
|
||||||
|
|||||||
@@ -139,10 +139,12 @@ func ipFromHostPort(hostPort string) string {
|
|||||||
// request from, so the entries on the right are written by the
|
// request from, so the entries on the right are written by the
|
||||||
// trusted proxies and those further left by the client. The
|
// trusted proxies and those further left by the client. The
|
||||||
// client's address is therefore the right-most entry that is not
|
// client's address is therefore the right-most entry that is not
|
||||||
// a trusted proxy. When every entry is a trusted proxy, or the
|
// a trusted proxy. An entry written as an address with a port is
|
||||||
// walk from the right meets an entry that is not an address, the
|
// read as that address, both for the trust check and as the
|
||||||
// peer is used. X-Real-IP is read only when there is no
|
// result. When every entry is a trusted proxy, or the walk from
|
||||||
// X-Forwarded-For entry at all.
|
// the right meets an entry that is not an address at all (such as
|
||||||
|
// "unknown"), the peer is used. X-Real-IP is read only when there
|
||||||
|
// is no X-Forwarded-For entry at all.
|
||||||
func clientIP(
|
func clientIP(
|
||||||
remoteAddr string,
|
remoteAddr string,
|
||||||
header http.Header,
|
header http.Header,
|
||||||
@@ -180,7 +182,10 @@ func clientIP(
|
|||||||
// forwardedFor returns the entries of X-Forwarded-For in order,
|
// forwardedFor returns the entries of X-Forwarded-For in order,
|
||||||
// leaving out blank ones. A proxy may add its entry as a header
|
// leaving out blank ones. A proxy may add its entry as a header
|
||||||
// line of its own rather than append it to the line the client
|
// line of its own rather than append it to the line the client
|
||||||
// sent, so every line is read, not only the first.
|
// sent, so every line is read, not only the first. Some proxies
|
||||||
|
// write an entry as an address with a port, such as
|
||||||
|
// 198.51.100.7:5000 or [2001:db8::1]:443; the port is removed so
|
||||||
|
// that the entry is read as its address.
|
||||||
func forwardedFor(header http.Header) []string {
|
func forwardedFor(header http.Header) []string {
|
||||||
value := strings.Join(header.Values("X-Forwarded-For"), ",")
|
value := strings.Join(header.Values("X-Forwarded-For"), ",")
|
||||||
|
|
||||||
@@ -189,7 +194,7 @@ func forwardedFor(header http.Header) []string {
|
|||||||
for part := range strings.SplitSeq(value, ",") {
|
for part := range strings.SplitSeq(value, ",") {
|
||||||
entry := strings.TrimSpace(part)
|
entry := strings.TrimSpace(part)
|
||||||
if entry != "" {
|
if entry != "" {
|
||||||
entries = append(entries, entry)
|
entries = append(entries, ipFromHostPort(entry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,9 +106,27 @@ func clientIPCases() []clientIPCase {
|
|||||||
want: loopbackIP,
|
want: loopbackIP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "entry that is not an address uses peer",
|
name: "entry with a port is read as its address",
|
||||||
remoteAddr: loopbackPeer,
|
remoteAddr: loopbackPeer,
|
||||||
xff: "203.0.113.9, 198.51.100.7:5000",
|
xff: "203.0.113.9, 198.51.100.7:5000",
|
||||||
|
want: "198.51.100.7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6 entry with a port is read as its address",
|
||||||
|
remoteAddr: loopbackPeer,
|
||||||
|
xff: "203.0.113.9, [2001:db8::1]:443",
|
||||||
|
want: "2001:db8::1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trusted proxy entry with a port is skipped",
|
||||||
|
remoteAddr: loopbackPeer,
|
||||||
|
xff: "203.0.113.9, 10.0.0.5:5000",
|
||||||
|
want: "203.0.113.9",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "entry that is not an address uses peer",
|
||||||
|
remoteAddr: loopbackPeer,
|
||||||
|
xff: "203.0.113.9, unknown",
|
||||||
want: loopbackIP,
|
want: loopbackIP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user