-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleflare.go
140 lines (119 loc) · 3.8 KB
/
middleflare.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
// Package middleflare is a Traefik plugin that maps Cloudflare headers to standard headers.
package middleflare // import "github.com/neggles/middleflare"
import (
"context"
"net/http"
"net/netip"
"strings"
"github.com/neggles/middleflare/cfaddrs"
)
// Header name constants.
const (
XRealIP = "X-Real-IP"
XForwardedFor = "X-Forwarded-For"
XForwardedProto = "X-Forwarded-Proto"
XForwardedHost = "X-Forwarded-Host"
XTrustedProxy = "X-Trusted-Proxy"
CFConnectingIP = "CF-Connecting-IP"
CFVisitor = "CF-Visitor"
)
// Config the plugin configuration.
type Config struct {
TrustedProxies []string `json:"trustedProxies,omitempty"`
IncludeDefault bool `json:"includeDefault,omitempty"`
}
// CheckResult is the return from an IP trust check.
type CheckResult struct {
IsValid bool
IsTrusted bool
ProxyAddr netip.Addr
}
// CreateConfig creates the default plugin configuration.
func CreateConfig() *Config {
return &Config{
TrustedProxies: []string{},
IncludeDefault: true,
}
}
// CFHeaderWriter is a plugin that maps CF-Connecting-IP to X-Real-IP and X-Forwarded-For.
type CFHeaderWriter struct {
next http.Handler
name string
trustPrefixes []netip.Prefix
}
// New creates a new CFHeaderWriter plugin.
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
if config == nil {
config = CreateConfig()
}
var trustPrefixes []netip.Prefix
// If IncludeDefault is true, then we add the default fallback addresses.
if config.IncludeDefault {
trustPrefixes = cfaddrs.CloudflareAddresses()
}
// If TrustedProxies is not empty, then we add the user defined addresses.
if len(config.TrustedProxies) > 0 {
trustPrefixes = append(trustPrefixes, cfaddrs.ParsePrefixes(config.TrustedProxies)...)
}
// If we have no addresses to trust, then we return an error.
return &CFHeaderWriter{
next: next,
name: name,
trustPrefixes: trustPrefixes,
}, nil
}
// ServeHTTP implements the http.Handler interface.
func (writer *CFHeaderWriter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Check if the request is coming from a trusted proxy.
checkResult := writer.checkSourceAddr(req.RemoteAddr)
// If the remote address is invalid, then we return an error.
if !checkResult.IsValid {
http.Error(rw, "Invalid remote address", http.StatusInternalServerError)
return
}
// Set headers if the request is coming from a trusted proxy.
if checkResult.IsTrusted {
// Set X-Trusted-Proxy header if we have a valid proxy address.
if checkResult.ProxyAddr.IsValid() {
req.Header.Set(XTrustedProxy, checkResult.ProxyAddr.String())
}
// Set X-Real-IP and X-Forwarded-For headers if the CF-Connecting-IP header is set.
if req.Header.Get(CFConnectingIP) != "" {
req.Header.Set(XRealIP, req.Header.Get(CFConnectingIP))
req.Header.Set(XForwardedFor, req.Header.Get(CFConnectingIP))
}
}
// Pass the request to the next middleware.
writer.next.ServeHTTP(rw, req)
}
// checkSourceAddr checks if the remote address is trusted.
func (writer *CFHeaderWriter) checkSourceAddr(remoteAddr string) *CheckResult {
// Split the remote address into the IP and port, and then take the IP.
strIP := strings.Split(remoteAddr, ":")[0]
// Parse the IP address.
addr, err := netip.ParseAddr(strIP)
if err != nil {
return &CheckResult{
IsValid: false,
IsTrusted: false,
}
}
// Check if the address is in the trusted proxy list.
if len(writer.trustPrefixes) > 0 {
for _, network := range writer.trustPrefixes {
if network.Contains(addr) {
return &CheckResult{
IsValid: true,
IsTrusted: true,
ProxyAddr: addr,
}
}
}
}
// If we get here, then the remote address is not trusted, or we trust no proxies.
return &CheckResult{
IsValid: true,
IsTrusted: false,
ProxyAddr: addr,
}
}