chore(canary): halve webbug pixel.jpg dimensions (736→368)

This commit is contained in:
CarterPerez-dev 2026-05-17 22:58:53 -04:00
parent 159c942551
commit d96de872b0
9 changed files with 136 additions and 61 deletions

View File

@ -78,12 +78,13 @@ type AppConfig struct {
}
type ServerConfig struct {
Host string `koanf:"host"`
Port int `koanf:"port"`
ReadTimeout time.Duration `koanf:"read_timeout"`
WriteTimeout time.Duration `koanf:"write_timeout"`
IdleTimeout time.Duration `koanf:"idle_timeout"`
ShutdownTimeout time.Duration `koanf:"shutdown_timeout"`
Host string `koanf:"host"`
Port int `koanf:"port"`
ReadTimeout time.Duration `koanf:"read_timeout"`
WriteTimeout time.Duration `koanf:"write_timeout"`
IdleTimeout time.Duration `koanf:"idle_timeout"`
ShutdownTimeout time.Duration `koanf:"shutdown_timeout"`
TrustedProxyCIDRs []string `koanf:"trusted_proxy_cidrs"`
}
type DatabaseConfig struct {
@ -208,6 +209,13 @@ func loadDefaults(k *koanf.Koanf) error {
"server.write_timeout": "30s",
"server.idle_timeout": "120s",
"server.shutdown_timeout": "15s",
"server.trusted_proxy_cidrs": []string{
"127.0.0.1/32",
"::1/128",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
},
"database.max_open_conns": 25,
"database.max_idle_conns": 5,
@ -271,7 +279,7 @@ func loadDefaults(k *koanf.Koanf) error {
"notify.retention_limit": 100,
"notify.webhook_hmac_secret": "",
"notify.telegram_api_base": "https://api.telegram.org",
"notify.fingerprint_window": "5m",
"notify.fingerprint_window": "30s",
"operator.token": "",

View File

@ -41,13 +41,13 @@ func TestRealIP_Precedence(t *testing.T) {
"203.0.113.10",
},
{
"XFF rightmost no CF",
"XFF leftmost no CF",
map[string]string{
"X-Forwarded-For": "198.51.100.1, 198.51.100.7",
"X-Real-IP": "192.0.2.99",
},
"127.0.0.1:9",
"198.51.100.7",
"198.51.100.1",
},
{
"XFF trailing comma falls through",
@ -83,12 +83,12 @@ func TestRealIP_Precedence(t *testing.T) {
{"RemoteAddr loopback IPv6", nil, "[::1]:9", "::1"},
{"RemoteAddr no port fallback", nil, "127.0.0.1", "127.0.0.1"},
{
"XFF IPv6 rightmost",
"XFF mixed IPv4+IPv6 leftmost",
map[string]string{
"X-Forwarded-For": "198.51.100.1, 2001:db8::dead",
},
"127.0.0.1:9",
"2001:db8::dead",
"198.51.100.1",
},
{
"CF trimmed",
@ -109,6 +109,34 @@ func TestRealIP_Precedence(t *testing.T) {
}
}
func TestRealIP_UntrustedRemoteIgnoresClientHeaders(t *testing.T) {
require.NoError(t, middleware.SetTrustedProxyCIDRs(
[]string{"127.0.0.1/32", "::1/128"},
))
t.Cleanup(middleware.ClearTrustedProxyCIDRs)
r := newRequest("198.51.100.50:443", map[string]string{
"CF-Connecting-IP": "evil-spoofed",
"X-Forwarded-For": "evil-spoofed",
"X-Real-IP": "evil-spoofed",
})
require.Equal(t, "198.51.100.50", middleware.RealIP(r),
"requests from outside the trusted-proxy set must ignore "+
"client-supplied forwarding headers (audit F5)")
}
func TestRealIP_TrustedRemoteHonorsClientHeaders(t *testing.T) {
require.NoError(t, middleware.SetTrustedProxyCIDRs(
[]string{"127.0.0.1/32"},
))
t.Cleanup(middleware.ClearTrustedProxyCIDRs)
r := newRequest("127.0.0.1:443", map[string]string{
"CF-Connecting-IP": "203.0.113.10",
})
require.Equal(t, "203.0.113.10", middleware.RealIP(r))
}
func TestOptionalHeader(t *testing.T) {
require.Nil(t, middleware.OptionalHeader(""))
require.Nil(t, middleware.OptionalHeader(" "))

View File

@ -8,11 +8,10 @@ import (
"encoding/json"
"fmt"
"log/slog"
"net"
"net/http"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
redis_rate "github.com/go-redis/redis_rate/v10"
@ -95,22 +94,7 @@ func (rl *RateLimiter) allow(
}
func KeyByIP(r *http.Request) string {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
ips := strings.Split(xff, ",")
ip := strings.TrimSpace(ips[len(ips)-1])
return "ratelimit:ip:" + ip
}
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return "ratelimit:ip:" + xri
}
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
return "ratelimit:ip:" + ip
return "ratelimit:ip:" + RealIP(r)
}
func setRateLimitHeaders(
@ -161,7 +145,7 @@ func writeRateLimitExceeded(w http.ResponseWriter, res *redis_rate.Result) {
type limiterEntry struct {
limiter *rate.Limiter
lastAccess int64
lastAccess atomic.Int64
}
type localLimiter struct {
@ -187,7 +171,7 @@ func (l *localLimiter) cleanup() {
cutoff := time.Now().Add(-entryTTL).Unix()
l.limiters.Range(func(key, value any) bool {
entry, ok := value.(*limiterEntry)
if ok && entry.lastAccess < cutoff {
if ok && entry.lastAccess.Load() < cutoff {
l.limiters.Delete(key)
}
return true
@ -209,8 +193,8 @@ func (l *localLimiter) allow(
rate.Limit(ratePerSec),
limit.Burst,
),
lastAccess: now,
}
newEntry.lastAccess.Store(now)
entryI, _ = l.limiters.LoadOrStore(key, newEntry)
}
@ -218,7 +202,7 @@ func (l *localLimiter) allow(
if !ok {
return nil, fmt.Errorf("invalid limiter entry type")
}
entry.lastAccess = now
entry.lastAccess.Store(now)
allowed := entry.limiter.Allow()

View File

@ -4,9 +4,11 @@
package middleware
import (
"fmt"
"net"
"net/http"
"strings"
"sync/atomic"
)
const (
@ -15,15 +17,69 @@ const (
headerXRealIP = "X-Real-IP"
)
type proxyTrustState struct {
cidrs []*net.IPNet
}
var trustedProxies atomic.Pointer[proxyTrustState]
func SetTrustedProxyCIDRs(cidrs []string) error {
parsed := make([]*net.IPNet, 0, len(cidrs))
for _, c := range cidrs {
trimmed := strings.TrimSpace(c)
if trimmed == "" {
continue
}
_, n, err := net.ParseCIDR(trimmed)
if err != nil {
return fmt.Errorf("trusted proxy cidr %q: %w", c, err)
}
parsed = append(parsed, n)
}
trustedProxies.Store(&proxyTrustState{cidrs: parsed})
return nil
}
func ClearTrustedProxyCIDRs() {
trustedProxies.Store(nil)
}
func isTrustedProxy(remoteAddr string) bool {
state := trustedProxies.Load()
if state == nil {
return true
}
host, _, err := net.SplitHostPort(remoteAddr)
if err != nil {
host = remoteAddr
}
ip := net.ParseIP(host)
if ip == nil {
return false
}
for _, n := range state.cidrs {
if n.Contains(ip) {
return true
}
}
return false
}
func RealIP(r *http.Request) string {
if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" {
return v
}
if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" {
return v
}
if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" {
return v
if isTrustedProxy(r.RemoteAddr) {
if v := strings.TrimSpace(
r.Header.Get(headerCFConnectingIP),
); v != "" {
return v
}
if v := firstNonEmptyXFF(
r.Header.Get(headerXForwardedFor),
); v != "" {
return v
}
if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" {
return v
}
}
if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
return host
@ -39,13 +95,12 @@ func OptionalHeader(v string) *string {
return &v
}
func lastNonEmptyXFF(header string) string {
func firstNonEmptyXFF(header string) string {
if header == "" {
return ""
}
parts := strings.Split(header, ",")
for i := len(parts) - 1; i >= 0; i-- {
if v := strings.TrimSpace(parts[i]); v != "" {
for _, p := range strings.Split(header, ",") {
if v := strings.TrimSpace(p); v != "" {
return v
}
}

View File

@ -432,13 +432,13 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "203.0.113.10",
},
{
name: "XFF rightmost wins over XRI when no CF",
name: "XFF leftmost wins over XRI when no CF",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 198.51.100.7",
"X-Real-IP": "192.0.2.99",
},
remote: "127.0.0.1:9999",
wantIP: "198.51.100.7",
wantIP: "198.51.100.1",
},
{
name: "XFF trailing-comma falls through to last non-empty",
@ -491,12 +491,12 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "127.0.0.1",
},
{
name: "XFF IPv6 rightmost",
name: "XFF mixed IPv4+IPv6 leftmost",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 2001:db8::dead",
},
remote: "127.0.0.1:9999",
wantIP: "2001:db8::dead",
wantIP: "198.51.100.1",
},
{
name: "CF value is trimmed of whitespace",

View File

@ -456,13 +456,13 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "203.0.113.10",
},
{
name: "XFF rightmost wins over XRI when no CF",
name: "XFF leftmost wins over XRI when no CF",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 198.51.100.7",
"X-Real-IP": "192.0.2.99",
},
remote: "127.0.0.1:9999",
wantIP: "198.51.100.7",
wantIP: "198.51.100.1",
},
{
name: "XFF trailing-comma falls through to last non-empty",
@ -515,12 +515,12 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "127.0.0.1",
},
{
name: "XFF IPv6 rightmost",
name: "XFF mixed IPv4+IPv6 leftmost",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 2001:db8::dead",
},
remote: "127.0.0.1:9999",
wantIP: "2001:db8::dead",
wantIP: "198.51.100.1",
},
{
name: "CF value is trimmed of whitespace",

View File

@ -320,13 +320,13 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "203.0.113.10",
},
{
name: "XFF rightmost wins over XRI when no CF",
name: "XFF leftmost wins over XRI when no CF",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 198.51.100.7",
"X-Real-IP": "192.0.2.99",
},
remote: "127.0.0.1:9999",
wantIP: "198.51.100.7",
wantIP: "198.51.100.1",
},
{
name: "XFF trailing-comma falls through to last non-empty",
@ -379,12 +379,12 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "127.0.0.1",
},
{
name: "XFF IPv6 rightmost",
name: "XFF mixed IPv4+IPv6 leftmost",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 2001:db8::dead",
},
remote: "127.0.0.1:9999",
wantIP: "2001:db8::dead",
wantIP: "198.51.100.1",
},
{
name: "CF value is trimmed of whitespace",

View File

@ -497,13 +497,13 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "203.0.113.10",
},
{
name: "XFF rightmost wins over XRI when no CF",
name: "XFF leftmost wins over XRI when no CF",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 198.51.100.7",
"X-Real-IP": "192.0.2.99",
},
remote: "127.0.0.1:9999",
wantIP: "198.51.100.7",
wantIP: "198.51.100.1",
},
{
name: "XFF trailing-comma falls through to last non-empty",
@ -556,12 +556,12 @@ func TestTrigger_RecordsEventWithRequestMetadata(t *testing.T) {
wantIP: "127.0.0.1",
},
{
name: "XFF IPv6 rightmost",
name: "XFF mixed IPv4+IPv6 leftmost",
headers: map[string]string{
"X-Forwarded-For": "198.51.100.1, 2001:db8::dead",
},
remote: "127.0.0.1:9999",
wantIP: "2001:db8::dead",
wantIP: "198.51.100.1",
},
{
name: "CF value is trimmed of whitespace",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 18 KiB