File: whitelist.go

package info (click to toggle)
golang-github-cloudflare-cfssl 1.2.0%2Bgit20160825.89.7fb22c8-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 4,916 kB
  • ctags: 2,827
  • sloc: sh: 146; sql: 62; python: 11; makefile: 8
file content (211 lines) | stat: -rw-r--r-- 5,103 bytes parent folder | download | duplicates (3)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Package whitelist implements IP whitelisting for various types
// of connections. Two types of access control lists (ACLs) are
// supported: host-based and network-based.
package whitelist

import (
	"errors"
	"log"
	"net"
	"sort"
	"strings"
	"sync"
)

// An ACL stores a list of permitted IP addresses, and handles
// concurrency as needed.
type ACL interface {
	// Permitted takes an IP address, and returns true if the
	// IP address is whitelisted (e.g. permitted access).
	Permitted(net.IP) bool
}

// A HostACL stores a list of permitted hosts.
type HostACL interface {
	ACL

	// Add takes an IP address and adds it to the whitelist so
	// that it is now permitted.
	Add(net.IP)

	// Remove takes an IP address and drops it from the whitelist
	// so that it is no longer permitted.
	Remove(net.IP)
}

// validIP takes an IP address (which is implemented as a byte slice)
// and ensures that it is a possible address. Right now, this means
// just doing length checks.
func validIP(ip net.IP) bool {
	if len(ip) == 4 {
		return true
	}

	if len(ip) == 16 {
		return true
	}

	return false
}

// Basic implements a basic map-backed whitelister that uses an
// RWMutex for conccurency. IPv4 addresses are treated differently
// than an IPv6 address; namely, the IPv4 localhost will not match
// the IPv6 localhost.
type Basic struct {
	lock      *sync.Mutex
	whitelist map[string]bool
}

// Permitted returns true if the IP has been whitelisted.
func (wl *Basic) Permitted(ip net.IP) bool {
	if !validIP(ip) {
		return false
	}

	wl.lock.Lock()
	permitted := wl.whitelist[ip.String()]
	wl.lock.Unlock()
	return permitted
}

// Add whitelists an IP.
func (wl *Basic) Add(ip net.IP) {
	if !validIP(ip) {
		return
	}

	wl.lock.Lock()
	defer wl.lock.Unlock()
	wl.whitelist[ip.String()] = true
}

// Remove clears the IP from the whitelist.
func (wl *Basic) Remove(ip net.IP) {
	if !validIP(ip) {
		return
	}

	wl.lock.Lock()
	defer wl.lock.Unlock()
	delete(wl.whitelist, ip.String())
}

// NewBasic returns a new initialised basic whitelist.
func NewBasic() *Basic {
	return &Basic{
		lock:      new(sync.Mutex),
		whitelist: map[string]bool{},
	}
}

// MarshalJSON serialises a host whitelist to a comma-separated list of
// hosts, implementing the json.Marshaler interface.
func (wl *Basic) MarshalJSON() ([]byte, error) {
	wl.lock.Lock()
	defer wl.lock.Unlock()
	var ss = make([]string, 0, len(wl.whitelist))
	for ip := range wl.whitelist {
		ss = append(ss, ip)
	}

	out := []byte(`"` + strings.Join(ss, ",") + `"`)
	return out, nil
}

// UnmarshalJSON implements the json.Unmarshaler interface for host
// whitelists, taking a comma-separated string of hosts.
func (wl *Basic) UnmarshalJSON(in []byte) error {
	if in[0] != '"' || in[len(in)-1] != '"' {
		return errors.New("whitelist: invalid whitelist")
	}

	if wl.lock == nil {
		wl.lock = new(sync.Mutex)
	}

	wl.lock.Lock()
	defer wl.lock.Unlock()

	netString := strings.TrimSpace(string(in[1 : len(in)-1]))
	nets := strings.Split(netString, ",")

	wl.whitelist = map[string]bool{}
	for i := range nets {
		addr := strings.TrimSpace(nets[i])
		if addr == "" {
			continue
		}

		ip := net.ParseIP(addr)
		if ip == nil {
			wl.whitelist = nil
			return errors.New("whitelist: invalid IP address " + addr)
		}
		wl.whitelist[addr] = true
	}

	return nil
}

// DumpBasic returns a whitelist as a byte slice where each IP is on
// its own line.
func DumpBasic(wl *Basic) []byte {
	wl.lock.Lock()
	defer wl.lock.Unlock()

	var addrs = make([]string, 0, len(wl.whitelist))
	for ip := range wl.whitelist {
		addrs = append(addrs, ip)
	}

	sort.Strings(addrs)

	addrList := strings.Join(addrs, "\n")
	return []byte(addrList)
}

// LoadBasic loads a whitelist from a byteslice.
func LoadBasic(in []byte) (*Basic, error) {
	wl := NewBasic()
	addrs := strings.Split(string(in), "\n")

	for _, addr := range addrs {
		ip := net.ParseIP(addr)
		if ip == nil {
			return nil, errors.New("whitelist: invalid address")
		}
		wl.Add(ip)
	}
	return wl, nil
}

// HostStub allows host whitelisting to be added into a system's flow
// without doing anything yet. All operations result in warning log
// messages being printed to stderr. There is no mechanism for
// squelching these messages short of modifying the log package's
// default logger.
type HostStub struct{}

// Permitted always returns true, but prints a warning message alerting
// that whitelisting is stubbed.
func (wl HostStub) Permitted(ip net.IP) bool {
	log.Printf("WARNING: whitelist check for %s but whitelisting is stubbed", ip)
	return true
}

// Add prints a warning message about whitelisting being stubbed.
func (wl HostStub) Add(ip net.IP) {
	log.Printf("WARNING: IP %s added to whitelist but whitelisting is stubbed", ip)
}

// Remove prints a warning message about whitelisting being stubbed.
func (wl HostStub) Remove(ip net.IP) {
	log.Printf("WARNING: IP %s removed from whitelist but whitelisting is stubbed", ip)
}

// NewHostStub returns a new stubbed host whitelister.
func NewHostStub() HostStub {
	log.Println("WARNING: whitelisting is being stubbed")
	return HostStub{}
}