Repository navigation
Expand file tree
/
Copy pathonlyfrom.go
More file actions
66 lines (61 loc) · 1.8 KB
/
Copy pathonlyfrom.go
File metadata and controls
66 lines (61 loc) · 1.8 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
package rest
import (
"fmt"
"net"
"net/http"
"strings"
"github.com/go-pkgz/rest/realip"
)
// OnlyFrom middleware allows access for limited list of source IPs.
// Rules can be complete IPs (like 192.168.1.12), textual prefixes (129.168.), or CIDRs (192.168.0.0/16).
// Complete IPs use semantic address equality, CIDRs use network containment, and all other rules use prefix matching.
func OnlyFrom(onlyIps ...string) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if len(onlyIps) == 0 {
// no restrictions if no ips defined
h.ServeHTTP(w, r)
return
}
matched, ip, err := matchSourceIP(r, onlyIps)
if err != nil {
_ = EncodeJSON(w, http.StatusInternalServerError, JSON{"error": fmt.Sprintf("can't get realip: %s", err)})
return
}
if matched {
// matched ip - allow
h.ServeHTTP(w, r)
return
}
_ = EncodeJSON(w, http.StatusForbidden, JSON{"error": fmt.Sprintf("ip %q rejected", ip)})
}
return http.HandlerFunc(fn)
}
}
// matchSourceIP returns true if request's ip matches any of ips
func matchSourceIP(r *http.Request, ips []string) (result bool, match string, err error) {
ip, err := realip.Get(r)
if err != nil {
return false, "", fmt.Errorf("can't get realip: %w", err) // we can't get ip, so no match
}
parsedIP := net.ParseIP(ip)
// check for cidr, complete ip, or ip prefix
for _, exclIP := range ips {
if _, cidrnet, err := net.ParseCIDR(exclIP); err == nil {
if cidrnet.Contains(parsedIP) {
return true, ip, nil
}
continue
}
if allowedIP := net.ParseIP(exclIP); allowedIP != nil {
if allowedIP.Equal(parsedIP) {
return true, ip, nil
}
continue
}
if strings.HasPrefix(ip, exclIP) {
return true, ip, nil
}
}
return false, ip, nil
}