1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package main
import (
"net"
"net/http"
"strings"
"sync"
"time"
)
// Limiter is a token bucket per key (the client IP): `burst` requests at
// once, refilled at `burst` per `per`. In memory: a restart forgets it.
type Limiter struct {
burst float64
rate float64 // tokens per second
mu sync.Mutex
buckets map[string]*bucket
now func() time.Time
}
type bucket struct {
tokens float64
last time.Time
}
func NewLimiter(burst int, per time.Duration) *Limiter {
return &Limiter{burst: float64(burst), rate: float64(burst) / per.Seconds(),
buckets: map[string]*bucket{}, now: time.Now}
}
func (l *Limiter) Allow(key string) bool {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
b, ok := l.buckets[key]
if !ok {
b = &bucket{tokens: l.burst, last: now}
l.buckets[key] = b
}
b.tokens += now.Sub(b.last).Seconds() * l.rate
if b.tokens > l.burst {
b.tokens = l.burst
}
b.last = now
if b.tokens < 1 {
return false
}
b.tokens--
return true
}
// Prune drops the buckets that are full again: they behave as new ones.
func (l *Limiter) Prune() {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
for k, b := range l.buckets {
if b.tokens+now.Sub(b.last).Seconds()*l.rate >= l.burst {
delete(l.buckets, k)
}
}
}
// ClientIP trusts X-Forwarded-For only when the connection comes from the
// proxy (Nginx Proxy Manager); anyone else could write that header.
type ClientIP struct {
trusted []*net.IPNet
}
func NewClientIP(spec string) (*ClientIP, error) {
c := &ClientIP{}
for _, part := range strings.Split(spec, ",") {
part = strings.TrimSpace(part)
if part == "" {
continue
}
if !strings.Contains(part, "/") {
if strings.Contains(part, ":") {
part += "/128"
} else {
part += "/32"
}
}
_, n, err := net.ParseCIDR(part)
if err != nil {
return nil, err
}
c.trusted = append(c.trusted, n)
}
return c, nil
}
func (c *ClientIP) isTrusted(ip net.IP) bool {
for _, n := range c.trusted {
if n.Contains(ip) {
return true
}
}
return false
}
func (c *ClientIP) Of(r *http.Request) string {
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
host = r.RemoteAddr
}
peer := net.ParseIP(host)
if peer == nil || !c.isTrusted(peer) {
return host
}
// Rightmost address that is not a trusted proxy: what the client put
// further left could be invented.
hops := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
for i := len(hops) - 1; i >= 0; i-- {
ip := net.ParseIP(strings.TrimSpace(hops[i]))
if ip == nil {
break
}
if !c.isTrusted(ip) {
return ip.String()
}
}
if real := net.ParseIP(strings.TrimSpace(r.Header.Get("X-Real-IP"))); real != nil {
return real.String()
}
return host
}
|