Parcourir la source

Add blocklist to config

tags/v2.0.0-rc1
9seconds il y a 5 ans
Parent
révision
1f862027af
5 fichiers modifiés avec 262 ajouts et 1 suppressions
  1. 13
    0
      config/config.go
  2. 1
    1
      config/config_test.go
  3. 68
    0
      config/type_blocklist_uri.go
  4. 156
    0
      config/type_blocklist_uri_test.go
  5. 24
    0
      example.config.toml

+ 13
- 0
config/config.go Voir le fichier

@@ -27,6 +27,12 @@ type Config struct {
27 27
 			MaxSize   TypeBytes     `json:"max-size"`
28 28
 			ErrorRate TypeErrorRate `json:"error-rate"`
29 29
 		} `json:"anti-replay"`
30
+		Blocklist struct {
31
+			Enabled             bool               `json:"enabled"`
32
+			DownloadConcurrency uint               `json:"download-concurrency"`
33
+			URLs                []TypeBlocklistURI `json:"urls"`
34
+			UpdateEach          TypeDuration       `json:"update-each"`
35
+		} `json:"blocklist"`
30 36
 	} `json:"defense"`
31 37
 	Network struct {
32 38
 		PublicIP struct {
@@ -60,6 +66,7 @@ func (c *Config) Validate() error {
60 66
 	if !c.Secret.Valid() {
61 67
 		return fmt.Errorf("invalid secret %s", c.Secret.String())
62 68
 	}
69
+
63 70
 	if len(c.BindTo.HostValue(nil)) == 0 || c.BindTo.PortValue(0) == 0 {
64 71
 		return fmt.Errorf("incorrect bind-to parameter %s", c.BindTo.String())
65 72
 	}
@@ -98,6 +105,12 @@ type configRaw struct {
98 105
 			MaxSize   string  `toml:"max-size" json:"max-size,omitempty"`
99 106
 			ErrorRate float64 `toml:"error-rate" json:"error-rate,omitempty"`
100 107
 		} `toml:"anti-replay" json:"anti-replay,omitempty"`
108
+		Blocklist struct {
109
+			Enabled             bool     `toml:"enabled" json:"enabled,omitempty"`
110
+			DownloadConcurrency uint     `toml:"download-concurrency" json:"download-concurrency,omitempty"`
111
+			URLs                []string `toml:"urls" json:"urls,omitempty"`
112
+			UpdateEach          string   `toml:"update-each" json:"update-each,omitempty"`
113
+		} `toml:"blocklist" json:"blocklist,omitempty"`
101 114
 	} `toml:"defense" json:"defense,omitempty"`
102 115
 	Network struct {
103 116
 		PublicIP struct {

+ 1
- 1
config/config_test.go Voir le fichier

@@ -39,7 +39,7 @@ func (suite *ConfigTestSuite) TestParseMinimalConfig() {
39 39
 	conf, err := config.Parse(suite.ReadConfig("minimal.toml"))
40 40
 	suite.NoError(err)
41 41
 	suite.Equal("7oe1GqLy6TBc38CV3jx7q09nb29nbGUuY29t", conf.Secret.Base64())
42
-    suite.Equal("0.0.0.0:3128", conf.BindTo.String())
42
+	suite.Equal("0.0.0.0:3128", conf.BindTo.String())
43 43
 }
44 44
 
45 45
 func TestConfig(t *testing.T) {

+ 68
- 0
config/type_blocklist_uri.go Voir le fichier

@@ -0,0 +1,68 @@
1
+package config
2
+
3
+import (
4
+	"fmt"
5
+	"net/url"
6
+	"os"
7
+	"path/filepath"
8
+)
9
+
10
+type TypeBlocklistURI struct {
11
+	value string
12
+}
13
+
14
+func (c *TypeBlocklistURI) UnmarshalText(data []byte) error {
15
+	if len(data) == 0 {
16
+		return nil
17
+	}
18
+
19
+	text := string(data)
20
+	if filepath.IsAbs(text) {
21
+		if _, err := os.Stat(text); os.IsNotExist(err) {
22
+			return fmt.Errorf("filepath %s does not exist", text)
23
+		}
24
+
25
+		c.value = text
26
+
27
+		return nil
28
+	}
29
+
30
+	parsedURL, err := url.Parse(text)
31
+	if err != nil {
32
+		return fmt.Errorf("incorrect url: %w", err)
33
+	}
34
+
35
+	switch parsedURL.Scheme {
36
+	case "http", "https": // nolint: goconst
37
+	default:
38
+		return fmt.Errorf("unknown schema %s", parsedURL.Scheme)
39
+	}
40
+
41
+	if parsedURL.Host == "" {
42
+		return fmt.Errorf("incorrect url %s", text)
43
+	}
44
+
45
+	c.value = parsedURL.String()
46
+
47
+	return nil
48
+}
49
+
50
+func (c TypeBlocklistURI) MarshalText() ([]byte, error) {
51
+	return []byte(c.value), nil
52
+}
53
+
54
+func (c TypeBlocklistURI) String() string {
55
+	return c.value
56
+}
57
+
58
+func (c TypeBlocklistURI) IsRemote() bool {
59
+	return !filepath.IsAbs(c.value)
60
+}
61
+
62
+func (c TypeBlocklistURI) Value(defaultValue string) string {
63
+	if c.value == "" {
64
+		return defaultValue
65
+	}
66
+
67
+	return c.value
68
+}

+ 156
- 0
config/type_blocklist_uri_test.go Voir le fichier

@@ -0,0 +1,156 @@
1
+package config_test
2
+
3
+import (
4
+	"crypto/rand"
5
+	"encoding/base64"
6
+	"encoding/json"
7
+	"os"
8
+	"path/filepath"
9
+	"strconv"
10
+	"testing"
11
+
12
+	"github.com/9seconds/mtg/v2/config"
13
+	"github.com/stretchr/testify/assert"
14
+	"github.com/stretchr/testify/suite"
15
+)
16
+
17
+type typeBlocklistURITestStruct struct {
18
+	Value config.TypeBlocklistURI `json:"value"`
19
+}
20
+
21
+type TypeBlocklistURITestSuite struct {
22
+	suite.Suite
23
+}
24
+
25
+func (suite *TypeBlocklistURITestSuite) TestUnmarshalFail() {
26
+	rnd := make([]byte, 48)
27
+
28
+	rand.Read(rnd) // nolint: errcheck
29
+
30
+	unknownPath := base64.StdEncoding.EncodeToString(rnd)
31
+
32
+	testData := []string{
33
+		"1",
34
+		unknownPath,
35
+		"/" + unknownPath,
36
+		"http:/",
37
+		"gopher://lalalal",
38
+	}
39
+
40
+	for _, v := range testData {
41
+		data, err := json.Marshal(map[string]string{
42
+			"value": v,
43
+		})
44
+		suite.NoError(err)
45
+
46
+		suite.T().Run(v, func(t *testing.T) {
47
+			assert.Error(t, json.Unmarshal(data, &typeBlocklistURITestStruct{}))
48
+		})
49
+	}
50
+}
51
+
52
+func (suite *TypeBlocklistURITestSuite) TestUnmarshalOk() {
53
+	dir, _ := os.Getwd()
54
+	dir, _ = filepath.Abs(dir)
55
+
56
+	testData := []string{
57
+		"http://lalala",
58
+		filepath.Join(dir, "config.go"),
59
+		"https://lalala",
60
+	}
61
+
62
+	for _, v := range testData {
63
+		value := v
64
+
65
+		data, err := json.Marshal(map[string]string{
66
+			"value": v,
67
+		})
68
+		suite.NoError(err)
69
+
70
+		suite.T().Run(v, func(t *testing.T) {
71
+			testStruct := &typeBlocklistURITestStruct{}
72
+
73
+			assert.NoError(t, json.Unmarshal(data, testStruct))
74
+			assert.EqualValues(t, value, testStruct.Value.Value(""))
75
+		})
76
+	}
77
+}
78
+
79
+func (suite *TypeBlocklistURITestSuite) TestMarshalOk() {
80
+	dir, _ := os.Getwd()
81
+	dir, _ = filepath.Abs(dir)
82
+
83
+	testData := []string{
84
+		"http://lalalal",
85
+		filepath.Join(dir, "config.go"),
86
+	}
87
+
88
+	for _, v := range testData {
89
+		name := v
90
+
91
+		data, err := json.Marshal(map[string]string{
92
+			"value": name,
93
+		})
94
+		suite.NoError(err)
95
+
96
+		suite.T().Run(name, func(t *testing.T) {
97
+			testStruct := &typeBlocklistURITestStruct{}
98
+
99
+			assert.NoError(t, json.Unmarshal(data, testStruct))
100
+			assert.Equal(t, name, testStruct.Value.String())
101
+
102
+			marshalled, err := testStruct.Value.MarshalText()
103
+			assert.NoError(t, err)
104
+			assert.Equal(t, name, string(marshalled))
105
+		})
106
+	}
107
+}
108
+
109
+func (suite *TypeBlocklistURITestSuite) TestValue() {
110
+	testStruct := &typeBlocklistURITestStruct{}
111
+
112
+	suite.Equal("http://lalala", testStruct.Value.Value("http://lalala"))
113
+
114
+	data, err := json.Marshal(map[string]string{
115
+		"value": "http://blablabla",
116
+	})
117
+	suite.NoError(err)
118
+	suite.NoError(json.Unmarshal(data, testStruct))
119
+
120
+	suite.Equal("http://blablabla", testStruct.Value.Value(""))
121
+}
122
+
123
+func (suite *TypeBlocklistURITestSuite) TestIsRemote() {
124
+	dir, _ := os.Getwd()
125
+	dir, _ = filepath.Abs(dir)
126
+
127
+	testData := map[bool]string{
128
+		true:  "http://lalalal",
129
+		false: filepath.Join(dir, "config.go"),
130
+	}
131
+
132
+	for k, v := range testData {
133
+		ok := k
134
+
135
+		data, err := json.Marshal(map[string]string{
136
+			"value": v,
137
+		})
138
+		suite.NoError(err)
139
+
140
+		suite.T().Run(strconv.FormatBool(ok), func(t *testing.T) {
141
+			testStruct := &typeBlocklistURITestStruct{}
142
+			assert.NoError(t, json.Unmarshal(data, testStruct))
143
+
144
+			if ok {
145
+				assert.True(t, testStruct.Value.IsRemote())
146
+			} else {
147
+				assert.False(t, testStruct.Value.IsRemote())
148
+			}
149
+		})
150
+	}
151
+}
152
+
153
+func TestTypeBlocklistURI(t *testing.T) {
154
+	t.Parallel()
155
+	suite.Run(t, &TypeBlocklistURITestSuite{})
156
+}

+ 24
- 0
example.config.toml Voir le fichier

@@ -149,6 +149,30 @@ max-size = "16mb"
149 149
 # to maintain a desired error ratio.
150 150
 error-rate = 0.0001
151 151
 
152
+# You can protect proxies by using different blocklists. If client has
153
+# ip from the given range, we do not try to do a proper handshake. We
154
+# actually route it to fronting domain. So, this client will never ever
155
+# have a chance to use mtg to access Telegram.
156
+#
157
+# Please remember that blocklists are initialized in async way. So,
158
+# when you start a proxy, blocklists are empty, they are populated and
159
+# processed in backgrounds. An error in any URL is ignored.
160
+[defense.blocklist]
161
+# You can enable/disable this feature.
162
+enabled = true
163
+# This is a limiter for concurrency. In order to protect website
164
+# from overloading, we download files in this number of threads.
165
+download-concurrency = 2
166
+# A list of URLs in FireHOL format (https://iplists.firehol.org/)
167
+# You can provider links here (starts with https:// or http://) or
168
+# path to a local file, but in this case it should be absolute.
169
+urls = [
170
+    # "https://iplists.firehol.org/files/firehol_level1.netset",
171
+    # "/local.file"
172
+]
173
+# How often do we need to update a blocklist set.
174
+update-each = "1d"
175
+
152 176
 # statsd statistics integration.
153 177
 [stats.statsd]
154 178
 # enabled/disabled

Chargement…
Annuler
Enregistrer