Kaynağa Gözat

Merge pull request #262 from 9seconds/better-whitelist

More elegant management of ip allowlists
tags/v2.1.6^2
Sergey Arkhipov 4 yıl önce
ebeveyn
işleme
eba3673e27
No account linked to committer's email address

+ 51
- 19
internal/cli/run_proxy.go Dosyayı Görüntüle

@@ -12,11 +12,13 @@ import (
12 12
 	"github.com/9seconds/mtg/v2/internal/config"
13 13
 	"github.com/9seconds/mtg/v2/internal/utils"
14 14
 	"github.com/9seconds/mtg/v2/ipblocklist"
15
+	"github.com/9seconds/mtg/v2/ipblocklist/files"
15 16
 	"github.com/9seconds/mtg/v2/logger"
16 17
 	"github.com/9seconds/mtg/v2/mtglib"
17 18
 	"github.com/9seconds/mtg/v2/network"
18 19
 	"github.com/9seconds/mtg/v2/stats"
19 20
 	"github.com/rs/zerolog"
21
+	"github.com/yl2chen/cidranger"
20 22
 )
21 23
 
22 24
 func makeLogger(conf *config.Config) mtglib.Logger {
@@ -106,7 +108,7 @@ func makeIPBlocklist(conf config.ListConfig,
106 108
 		}
107 109
 	}
108 110
 
109
-	firehol, err := ipblocklist.NewFirehol(logger.Named("ipblockist"),
111
+	blocklist, err := ipblocklist.NewFirehol(logger.Named("ipblockist"),
110 112
 		ntw,
111 113
 		conf.DownloadConcurrency.Get(1),
112 114
 		remoteURLs,
@@ -116,9 +118,44 @@ func makeIPBlocklist(conf config.ListConfig,
116 118
 		return nil, fmt.Errorf("incorrect parameters for firehol: %w", err)
117 119
 	}
118 120
 
119
-	go firehol.Run(conf.UpdateEach.Get(ipblocklist.DefaultFireholUpdateEach))
121
+	go blocklist.Run(conf.UpdateEach.Get(ipblocklist.DefaultFireholUpdateEach))
120 122
 
121
-	return firehol, nil
123
+	return blocklist, nil
124
+}
125
+
126
+func makeIPAllowlist(conf config.ListConfig,
127
+	logger mtglib.Logger,
128
+	ntw mtglib.Network,
129
+	updateCallback ipblocklist.FireholUpdateCallback,
130
+) (allowlist mtglib.IPBlocklist, err error) {
131
+	if !conf.Enabled.Get(false) {
132
+		allowlist, err = ipblocklist.NewFireholFromFiles(
133
+			logger.Named("ipblocklist"),
134
+			1,
135
+			[]files.File{
136
+				files.NewMem([]*net.IPNet{
137
+					cidranger.AllIPv4,
138
+					cidranger.AllIPv6,
139
+				}),
140
+			},
141
+			updateCallback,
142
+		)
143
+
144
+		go allowlist.Run(conf.UpdateEach.Get(ipblocklist.DefaultFireholUpdateEach))
145
+	} else {
146
+		allowlist, err = makeIPBlocklist(
147
+			conf,
148
+			logger,
149
+			ntw,
150
+			updateCallback,
151
+		)
152
+	}
153
+
154
+	if err != nil {
155
+		return nil, fmt.Errorf("cannot build allowlist: %w", err)
156
+	}
157
+
158
+	return allowlist, nil
122 159
 }
123 160
 
124 161
 func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStream, error) {
@@ -186,21 +223,16 @@ func runProxy(conf *config.Config, version string) error { // nolint: funlen
186 223
 		return fmt.Errorf("cannot build ip blocklist: %w", err)
187 224
 	}
188 225
 
189
-	var whitelist mtglib.IPBlocklist
190
-
191
-	if conf.Defense.Allowlist.Enabled.Get(false) {
192
-		whlist, err := makeIPBlocklist(
193
-			conf.Defense.Allowlist,
194
-			logger.Named("allowlist"),
195
-			ntw,
196
-			func(ctx context.Context, size int) {
197
-				eventStream.Send(ctx, mtglib.NewEventIPListSize(size, false))
198
-			})
199
-		if err != nil {
200
-			return fmt.Errorf("cannot build ip allowlist: %w", err)
201
-		}
202
-
203
-		whitelist = whlist
226
+	allowlist, err := makeIPAllowlist(
227
+		conf.Defense.Allowlist,
228
+		logger.Named("allowlist"),
229
+		ntw,
230
+		func(ctx context.Context, size int) {
231
+			eventStream.Send(ctx, mtglib.NewEventIPListSize(size, false))
232
+		},
233
+	)
234
+	if err != nil {
235
+		return fmt.Errorf("cannot build ip allowlist: %w", err)
204 236
 	}
205 237
 
206 238
 	opts := mtglib.ProxyOpts{
@@ -208,7 +240,7 @@ func runProxy(conf *config.Config, version string) error { // nolint: funlen
208 240
 		Network:         ntw,
209 241
 		AntiReplayCache: makeAntiReplayCache(conf),
210 242
 		IPBlocklist:     blocklist,
211
-		IPWhitelist:     whitelist,
243
+		IPAllowlist:     allowlist,
212 244
 		EventStream:     eventStream,
213 245
 
214 246
 		Secret:             conf.Secret,

+ 37
- 0
ipblocklist/files/mem.go Dosyayı Görüntüle

@@ -0,0 +1,37 @@
1
+package files
2
+
3
+import (
4
+	"context"
5
+	"io"
6
+	"net"
7
+	"strings"
8
+)
9
+
10
+type memFile struct {
11
+	data string
12
+}
13
+
14
+func (m memFile) Open(ctx context.Context) (io.ReadCloser, error) {
15
+	return io.NopCloser(strings.NewReader(m.data)), nil
16
+}
17
+
18
+func (m memFile) String() string {
19
+	return "mem"
20
+}
21
+
22
+func NewMem(networks []*net.IPNet) File {
23
+	builder := strings.Builder{}
24
+
25
+	if len(networks) > 0 {
26
+		builder.WriteString(networks[0].String())
27
+	}
28
+
29
+	for i := 1; i < len(networks); i++ {
30
+		builder.WriteString("\n")
31
+		builder.WriteString(networks[i].String())
32
+	}
33
+
34
+	return memFile{
35
+		data: builder.String(),
36
+	}
37
+}

+ 42
- 0
ipblocklist/files/mem_test.go Dosyayı Görüntüle

@@ -0,0 +1,42 @@
1
+package files_test
2
+
3
+import (
4
+	"context"
5
+	"io"
6
+	"net"
7
+	"strings"
8
+	"testing"
9
+
10
+	"github.com/9seconds/mtg/v2/ipblocklist/files"
11
+	"github.com/stretchr/testify/suite"
12
+)
13
+
14
+type MemTestSuite struct {
15
+	suite.Suite
16
+}
17
+
18
+func (suite *MemTestSuite) TestOk() {
19
+	_, network1, _ := net.ParseCIDR("192.168.0.1/24")
20
+	_, network2, _ := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/36")
21
+
22
+	file := files.NewMem([]*net.IPNet{
23
+		network1,
24
+		network2,
25
+	})
26
+
27
+	reader, err := file.Open(context.Background())
28
+	suite.NoError(err)
29
+
30
+	data, err := io.ReadAll(reader)
31
+	suite.NoError(err)
32
+
33
+	strData := strings.TrimSpace(string(data))
34
+
35
+	suite.Contains(strData, "192.168.0.0/24")
36
+	suite.Contains(strData, "2001:db8:8000::/36")
37
+}
38
+
39
+func TestMem(t *testing.T) {
40
+	t.Parallel()
41
+	suite.Run(t, &MemTestSuite{})
42
+}

+ 4
- 0
mtglib/init.go Dosyayı Görüntüle

@@ -48,6 +48,10 @@ var (
48 48
 	// create a proxy but ip blocklist instance is not defined.
49 49
 	ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
50 50
 
51
+	// ErrIPAllowlistIsNotDefined is returned if you are trying to
52
+	// create a proxy but ip allowlist instance is not defined.
53
+	ErrIPAllowlistIsNotDefined = errors.New("ip allowlist is not defined")
54
+
51 55
 	// ErrEventStreamIsNotDefined is returned if you are trying to create a
52 56
 	// proxy but event stream instance is not defined.
53 57
 	ErrEventStreamIsNotDefined = errors.New("event stream is not defined")

+ 6
- 9
mtglib/proxy.go Dosyayı Görüntüle

@@ -34,7 +34,7 @@ type Proxy struct {
34 34
 	network         Network
35 35
 	antiReplayCache AntiReplayCache
36 36
 	blocklist       IPBlocklist
37
-	whitelist       IPBlocklist
37
+	allowlist       IPBlocklist
38 38
 	eventStream     EventStream
39 39
 	logger          Logger
40 40
 }
@@ -91,7 +91,7 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
91 91
 }
92 92
 
93 93
 // Serve starts a proxy on a given listener.
94
-func (p *Proxy) Serve(listener net.Listener) error { // nolint: cyclop
94
+func (p *Proxy) Serve(listener net.Listener) error {
95 95
 	p.streamWaitGroup.Add(1)
96 96
 	defer p.streamWaitGroup.Done()
97 97
 
@@ -109,9 +109,9 @@ func (p *Proxy) Serve(listener net.Listener) error { // nolint: cyclop
109 109
 		ipAddr := conn.RemoteAddr().(*net.TCPAddr).IP // nolint: forcetypeassert
110 110
 		logger := p.logger.BindStr("ip", ipAddr.String())
111 111
 
112
-		if p.whitelist != nil && !p.whitelist.Contains(ipAddr) {
112
+		if !p.allowlist.Contains(ipAddr) {
113 113
 			conn.Close()
114
-			logger.Info("ip was rejected by whitelist")
114
+			logger.Info("ip was rejected by allowlist")
115 115
 			p.eventStream.Send(p.ctx, NewEventIPBlocklisted(ipAddr))
116 116
 
117 117
 			continue
@@ -145,10 +145,7 @@ func (p *Proxy) Shutdown() {
145 145
 	p.streamWaitGroup.Wait()
146 146
 	p.workerPool.Release()
147 147
 
148
-	if p.whitelist != nil {
149
-		p.whitelist.Shutdown()
150
-	}
151
-
148
+	p.allowlist.Shutdown()
152 149
 	p.blocklist.Shutdown()
153 150
 }
154 151
 
@@ -308,7 +305,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
308 305
 		network:                  opts.Network,
309 306
 		antiReplayCache:          opts.AntiReplayCache,
310 307
 		blocklist:                opts.IPBlocklist,
311
-		whitelist:                opts.IPWhitelist,
308
+		allowlist:                opts.IPAllowlist,
312 309
 		eventStream:              opts.EventStream,
313 310
 		logger:                   opts.getLogger("proxy"),
314 311
 		domainFrontingPort:       opts.getDomainFrontingPort(),

+ 4
- 2
mtglib/proxy_opts.go Dosyayı Görüntüle

@@ -28,10 +28,10 @@ type ProxyOpts struct {
28 28
 	// This is a mandatory setting.
29 29
 	IPBlocklist IPBlocklist
30 30
 
31
-	// IPWhitelist defines a whitelist of IPs to allow to use proxy.
31
+	// IPAllowlist defines a whitelist of IPs to allow to use proxy.
32 32
 	//
33 33
 	// This is an optional setting, ignored by default (no restrictions).
34
-	IPWhitelist IPBlocklist
34
+	IPAllowlist IPBlocklist
35 35
 
36 36
 	// EventStream defines an instance of event stream.
37 37
 	//
@@ -125,6 +125,8 @@ func (p ProxyOpts) valid() error {
125 125
 		return ErrAntiReplayCacheIsNotDefined
126 126
 	case p.IPBlocklist == nil:
127 127
 		return ErrIPBlocklistIsNotDefined
128
+	case p.IPAllowlist == nil:
129
+		return ErrIPAllowlistIsNotDefined
128 130
 	case p.EventStream == nil:
129 131
 		return ErrEventStreamIsNotDefined
130 132
 	case p.Logger == nil:

+ 25
- 0
mtglib/proxy_test.go Dosyayı Görüntüle

@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/9seconds/mtg/v2/antireplay"
16 16
 	"github.com/9seconds/mtg/v2/events"
17 17
 	"github.com/9seconds/mtg/v2/ipblocklist"
18
+	"github.com/9seconds/mtg/v2/ipblocklist/files"
18 19
 	"github.com/9seconds/mtg/v2/logger"
19 20
 	"github.com/9seconds/mtg/v2/mtglib"
20 21
 	"github.com/9seconds/mtg/v2/network"
@@ -22,6 +23,7 @@ import (
22 23
 	"github.com/gotd/td/telegram/dcs"
23 24
 	"github.com/gotd/td/tg"
24 25
 	"github.com/stretchr/testify/suite"
26
+	"github.com/yl2chen/cidranger"
25 27
 )
26 28
 
27 29
 type ProxyTestSuite struct {
@@ -49,11 +51,26 @@ func (suite *ProxyTestSuite) SetupSuite() {
49 51
 	ntw, err := network.NewNetwork(dialer, "mtgtest", "1.1.1.1", 0)
50 52
 	suite.NoError(err)
51 53
 
54
+	allowlist, _ := ipblocklist.NewFireholFromFiles(
55
+		logger.NewNoopLogger(),
56
+		1,
57
+		[]files.File{
58
+			files.NewMem([]*net.IPNet{
59
+				cidranger.AllIPv4,
60
+				cidranger.AllIPv6,
61
+			}),
62
+		},
63
+		nil,
64
+	)
65
+
66
+	allowlist.Run(time.Second)
67
+
52 68
 	suite.opts = &mtglib.ProxyOpts{
53 69
 		Secret:          mtglib.GenerateSecret("httpbin.org"),
54 70
 		Network:         ntw,
55 71
 		AntiReplayCache: antireplay.NewNoop(),
56 72
 		IPBlocklist:     ipblocklist.NewNoop(),
73
+		IPAllowlist:     allowlist,
57 74
 		EventStream:     events.NewNoopStream(),
58 75
 		Logger:          logger.NewNoopLogger(),
59 76
 		UseTestDCs:      true,
@@ -114,6 +131,14 @@ func (suite *ProxyTestSuite) TestCannotInitNoIPBlocklist() {
114 131
 	suite.Error(err)
115 132
 }
116 133
 
134
+func (suite *ProxyTestSuite) TestCannotInitNoIPAllowlist() {
135
+	opts := *suite.opts
136
+	opts.IPAllowlist = nil
137
+
138
+	_, err := mtglib.NewProxy(opts)
139
+	suite.Error(err)
140
+}
141
+
117 142
 func (suite *ProxyTestSuite) TestCannotInitNoEventStream() {
118 143
 	opts := *suite.opts
119 144
 	opts.EventStream = nil

Loading…
İptal
Kaydet