-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathip_policy.go
More file actions
111 lines (96 loc) · 3.17 KB
/
ip_policy.go
File metadata and controls
111 lines (96 loc) · 3.17 KB
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
package gatekeeper
import (
"fmt"
"net"
"net/http"
"github.com/4nkitd/gatekeeper/internal/utils"
)
func newParsedIPPolicy(config *IPPolicyConfig) (*parsedIPPolicy, error) {
if config.Mode != ModeBlacklist && config.Mode != ModeWhitelist {
return nil, fmt.Errorf("invalid IPPolicy mode: %s", config.Mode)
}
parsed := &parsedIPPolicy{
config: config,
}
var err error
parsed.parsedIPs, parsed.parsedCIDRs, err = utils.ParseIPsAndCIDRs(config.IPs, config.CIDRs)
if err != nil {
return nil, fmt.Errorf("failed to parse IPs/CIDRs: %w", err)
}
if config.TrustProxyHeaders {
_, parsedTrustedProxiesSlice, err := utils.ParseIPsAndCIDRs(nil, config.TrustedProxies) // TrustedProxies are only CIDRs for simplicity now
if err != nil {
return nil, fmt.Errorf("failed to parse TrustedProxies CIDRs: %w", err)
}
parsed.parsedTrustedProxies = parsedTrustedProxiesSlice
}
if len(parsed.parsedIPs) == 0 && len(parsed.parsedCIDRs) == 0 {
return nil, fmt.Errorf("IPPolicy defined but no IPs or CIDRs provided")
}
return parsed, nil
}
// IPPolicy is a middleware that enforces IP blacklisting/whitelisting.
func (gk *Gatekeeper) IPPolicy(next http.Handler) http.Handler {
if gk.parsedIPPolicy == nil {
return next // No policy configured
}
p := gk.parsedIPPolicy
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIPNet, err := utils.GetClientIPFromRequest(r, p.config.TrustProxyHeaders, p.parsedTrustedProxies)
if err != nil {
gk.logger.Printf("Error getting client IP: %v. RemoteAddr: %s", err, r.RemoteAddr)
// Decide how to handle: block, or proceed with r.RemoteAddr?
// For security, if IP cannot be determined reliably, it might be best to block
// or log and allow depending on strictness. Let's block for now.
gk.blockRequest(w, r, http.StatusInternalServerError, "Internal Server Error", "Could not determine client IP")
return
}
clientIPStr := clientIPNet.String()
matched := false
// Check exact IP matches
if _, ok := p.parsedIPs[clientIPStr]; ok {
matched = true
}
// Check CIDR matches if not already matched
if !matched {
for _, cidr := range p.parsedCIDRs {
if cidr.Contains(clientIPNet) {
matched = true
break
}
}
}
block := false
reason := ""
if p.config.Mode == ModeBlacklist {
if matched {
block = true
reason = fmt.Sprintf("IP %s is blacklisted", clientIPStr)
}
} else { // ModeWhitelist
if !matched {
block = true
reason = fmt.Sprintf("IP %s is not in whitelist", clientIPStr)
}
}
if block {
gk.blockRequest(w, r, gk.config.DefaultBlockStatusCode, gk.config.DefaultBlockMessage, reason)
return
}
next.ServeHTTP(w, r)
})
}
// This replaces the placeholder in gatekeeper.go
func GetClientIP(r *http.Request, ipPolicy *parsedIPPolicy) string {
if ipPolicy == nil { // Should not happen if IPPolicy middleware is active
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
return ip
}
ip, err := utils.GetClientIPFromRequest(r, ipPolicy.config.TrustProxyHeaders, ipPolicy.parsedTrustedProxies)
if err != nil {
// log error, return r.RemoteAddr as fallback?
pip, _, _ := net.SplitHostPort(r.RemoteAddr)
return pip
}
return ip.String()
}