Ver código fonte

Remove timeattack module

tags/v2.0.0-rc1
9seconds 5 anos atrás
pai
commit
e2073f0585

+ 8
- 13
example.config.toml Ver arquivo

@@ -48,6 +48,14 @@ prefer-ip = "prefer-ipv6"
48 48
 # access.
49 49
 domain-fronting-port = 443
50 50
 
51
+# FakeTLS can compare timestamps to prevent probes. Each message has
52
+# encrypted timestamp. So, mtg can compare this timestamp and decide if
53
+# we need to proceed with connection or not.
54
+#
55
+# Sometimes time can be skewed so we accept all messages within a
56
+# time range of this parameter.
57
+tolerate-time-skewness = "5s"
58
+
51 59
 # network defines different network-related settings
52 60
 [network]
53 61
 # please be aware that mtg needs to do some external requests. For
@@ -111,19 +119,6 @@ tcp = "5s"
111 119
 http = "10s"
112 120
 idle = "1m"
113 121
 
114
-# FakeTLS can compare timestamps to prevent probes. Each message has
115
-# encrypted timestamp. So, mtg can compare this timestamp and decide if
116
-# we need to proceed with connection or not.
117
-#
118
-# Please ensure that you have some ntp active on this host. Otherwise,
119
-# you can endup with badly performing proxy.
120
-[defense.time]
121
-# You can enable/disable that. A good idea is always enable.
122
-enabled = true
123
-# Time can be skewed by many reasons. So, this is a time interval
124
-# when message is cosidered as a good one.
125
-allow-skewness = "5s"
126
-
127 122
 # Some countries do active probing on Telegram connections. This technique
128 123
 # allows to protect from such effort.
129 124
 #

+ 5
- 18
internal/cli/proxy.go Ver arquivo

@@ -12,7 +12,6 @@ import (
12 12
 	"github.com/9seconds/mtg/v2/logger"
13 13
 	"github.com/9seconds/mtg/v2/mtglib"
14 14
 	"github.com/9seconds/mtg/v2/stats"
15
-	"github.com/9seconds/mtg/v2/timeattack"
16 15
 	"github.com/rs/zerolog"
17 16
 )
18 17
 
@@ -41,12 +40,11 @@ func (c *Proxy) Execute() error {
41 40
 
42 41
 	ctx := utils.RootContext()
43 42
 	opts := mtglib.ProxyOpts{
44
-		Logger:             logger.NewZeroLogger(zerolog.New(os.Stdout).With().Timestamp().Logger()),
45
-		Network:            c.Network,
46
-		AntiReplayCache:    antireplay.NewNoop(),
47
-		IPBlocklist:        ipblocklist.NewNoop(),
48
-		TimeAttackDetector: timeattack.NewNoop(),
49
-		EventStream:        events.NewNoopStream(),
43
+		Logger:          logger.NewZeroLogger(zerolog.New(os.Stdout).With().Timestamp().Logger()),
44
+		Network:         c.Network,
45
+		AntiReplayCache: antireplay.NewNoop(),
46
+		IPBlocklist:     ipblocklist.NewNoop(),
47
+		EventStream:     events.NewNoopStream(),
50 48
 
51 49
 		Secret:             c.Config.Secret,
52 50
 		BufferSize:         c.Config.TCPBuffer.Value(mtglib.DefaultBufferSize),
@@ -58,7 +56,6 @@ func (c *Proxy) Execute() error {
58 56
 	opts.Logger.BindStr("configuration", c.Config.String()).Debug("configuration")
59 57
 
60 58
 	c.setupAntiReplayCache(&opts)
61
-	c.setupTimeAttackDetector(&opts)
62 59
 
63 60
 	if err := c.setupIPBlocklist(&opts); err != nil {
64 61
 		return fmt.Errorf("cannot setup ipblocklist: %w", err)
@@ -98,16 +95,6 @@ func (c *Proxy) setupAntiReplayCache(opts *mtglib.ProxyOpts) {
98 95
 	)
99 96
 }
100 97
 
101
-func (c *Proxy) setupTimeAttackDetector(opts *mtglib.ProxyOpts) {
102
-	if !c.Config.Defense.Time.Enabled {
103
-		return
104
-	}
105
-
106
-	opts.TimeAttackDetector = timeattack.NewDetector(
107
-		c.Config.Defense.Time.AllowSkewness.Value(timeattack.DefaultDuration),
108
-	)
109
-}
110
-
111 98
 func (c *Proxy) setupIPBlocklist(opts *mtglib.ProxyOpts) error {
112 99
 	if !c.Config.Defense.Blocklist.Enabled {
113 100
 		return nil

+ 18
- 24
internal/config/config.go Ver arquivo

@@ -10,18 +10,15 @@ import (
10 10
 )
11 11
 
12 12
 type Config struct {
13
-	Debug              bool          `json:"debug"`
14
-	Secret             mtglib.Secret `json:"secret"`
15
-	BindTo             TypeHostPort  `json:"bind-to"`
16
-	TCPBuffer          TypeBytes     `json:"tcp-buffer"`
17
-	PreferIP           TypePreferIP  `json:"prefer-ip"`
18
-	DomainFrontingPort TypePort      `json:"domain-fronting-port"`
19
-	Concurrency        uint          `json:"concurrency"`
20
-	Defense            struct {
21
-		Time struct {
22
-			Enabled       bool         `json:"enabled"`
23
-			AllowSkewness TypeDuration `json:"allow-skewness"`
24
-		} `json:"time"`
13
+	Debug                bool          `json:"debug"`
14
+	Secret               mtglib.Secret `json:"secret"`
15
+	BindTo               TypeHostPort  `json:"bind-to"`
16
+	TCPBuffer            TypeBytes     `json:"tcp-buffer"`
17
+	PreferIP             TypePreferIP  `json:"prefer-ip"`
18
+	DomainFrontingPort   TypePort      `json:"domain-fronting-port"`
19
+	TolerateTimeSkewness TypeDuration  `json:"tolerate-time-skewness"`
20
+	Concurrency          uint          `json:"concurrency"`
21
+	Defense              struct {
25 22
 		AntiReplay struct {
26 23
 			Enabled   bool          `json:"enabled"`
27 24
 			MaxSize   TypeBytes     `json:"max-size"`
@@ -85,18 +82,15 @@ func (c *Config) String() string {
85 82
 }
86 83
 
87 84
 type configRaw struct {
88
-	Debug              bool   `toml:"debug" json:"debug,omitempty"`
89
-	Secret             string `toml:"secret" json:"secret"`
90
-	BindTo             string `toml:"bind-to" json:"bind-to"`
91
-	TCPBuffer          string `toml:"tcp-buffer" json:"tcp-buffer,omitempty"`
92
-	PreferIP           string `toml:"prefer-ip" json:"prefer-ip,omitempty"`
93
-	DomainFrontingPort uint   `toml:"domain-fronting-port" json:"domain-fronting-port,omitempty"`
94
-	Concurrency        uint   `toml:"concurrency" json:"concurrency,omitempty"`
95
-	Defense            struct {
96
-		Time struct {
97
-			Enabled       bool   `toml:"enabled" json:"enabled,omitempty"`
98
-			AllowSkewness string `toml:"allow-skewness" json:"allow-skewness,omitempty"`
99
-		} `toml:"time" json:"time,omitempty"`
85
+	Debug                bool   `toml:"debug" json:"debug,omitempty"`
86
+	Secret               string `toml:"secret" json:"secret"`
87
+	BindTo               string `toml:"bind-to" json:"bind-to"`
88
+	TCPBuffer            string `toml:"tcp-buffer" json:"tcp-buffer,omitempty"`
89
+	PreferIP             string `toml:"prefer-ip" json:"prefer-ip,omitempty"`
90
+	DomainFrontingPort   uint   `toml:"domain-fronting-port" json:"domain-fronting-port,omitempty"`
91
+	TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerate-time-skewness,omitempty"`
92
+	Concurrency          uint   `toml:"concurrency" json:"concurrency,omitempty"`
93
+	Defense              struct {
100 94
 		AntiReplay struct {
101 95
 			Enabled   bool    `toml:"enabled" json:"enabled,omitempty"`
102 96
 			MaxSize   string  `toml:"max-size" json:"max-size,omitempty"`

+ 4
- 16
mtglib/init.go Ver arquivo

@@ -42,10 +42,6 @@ var (
42 42
 	// create a proxy but anti replay cache value is undefined.
43 43
 	ErrAntiReplayCacheIsNotDefined = errors.New("anti-replay cache is not defined")
44 44
 
45
-	// ErrTimeAttackDetectorIsNotDefined is returned if you are trying to
46
-	// create a proxy but time attack detector is not defined.
47
-	ErrTimeAttackDetectorIsNotDefined = errors.New("time attack detector is not defined")
48
-
49 45
 	// ErrIPBlocklistIsNotDefined is returned if you are trying to
50 46
 	// create a proxy but ip blocklist instance is not defined.
51 47
 	ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
@@ -75,6 +71,10 @@ const (
75 71
 	// in case of idling.
76 72
 	DefaultIdleTimeout = time.Minute
77 73
 
74
+	// DefaultTolerateTimeSkewness is a default timeout for time
75
+	// skewness on a faketls timeout verification.
76
+	DefaultTolerateTimeSkewness = 3 * time.Second
77
+
78 78
 	// DefaultPreferIP is a default value for Telegram IP connectivity
79 79
 	// preference.
80 80
 	DefaultPreferIP = "prefer-ipv6"
@@ -206,18 +206,6 @@ type EventStream interface {
206 206
 	Send(context.Context, Event)
207 207
 }
208 208
 
209
-// TimeAttackDetector is an abstraction that checks a time, taken from
210
-// the faketls client hello message. This timestamp is encoded into
211
-// client-generated random bytes and can be extracted after some client
212
-// hello verification.
213
-//
214
-// This is mostly to prevent replay attacks.
215
-type TimeAttackDetector interface {
216
-	// Valid returns an error if timestamp is invalid or should not be
217
-	// accepted.
218
-	Valid(time.Time) error
219
-}
220
-
221 209
 // Logger defines an interface of the logger used by mtglib.
222 210
 //
223 211
 // Each logger has a name. It is possible to stack names to organize

+ 20
- 0
mtglib/internal/faketls/client_hello.go Ver arquivo

@@ -19,6 +19,26 @@ type ClientHello struct {
19 19
 	CipherSuite uint16
20 20
 }
21 21
 
22
+func (c ClientHello) Valid(hostname string, tolerateTimeSkewness time.Duration) error {
23
+	if c.Host != "" && c.Host != hostname {
24
+		return fmt.Errorf("incorrect hostname %s", hostname)
25
+	}
26
+
27
+	now := time.Now()
28
+
29
+	timeDiff := now.Sub(c.Time)
30
+	if timeDiff < 0 {
31
+		timeDiff = -timeDiff
32
+	}
33
+
34
+	if timeDiff > tolerateTimeSkewness {
35
+		return fmt.Errorf("incorrect timestamp. got=%d, now=%d, diff=%s",
36
+			c.Time.Unix(), now.Unix(), timeDiff.String())
37
+	}
38
+
39
+	return nil
40
+}
41
+
22 42
 func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
23 43
 	hello := ClientHello{}
24 44
 

+ 31
- 35
mtglib/proxy.go Ver arquivo

@@ -23,19 +23,19 @@ type Proxy struct {
23 23
 	ctxCancel       context.CancelFunc
24 24
 	streamWaitGroup sync.WaitGroup
25 25
 
26
-	idleTimeout        time.Duration
27
-	bufferSize         int
28
-	domainFrontingPort int
29
-	workerPool         *ants.PoolWithFunc
30
-	telegram           *telegram.Telegram
31
-
32
-	secret             Secret
33
-	network            Network
34
-	antiReplayCache    AntiReplayCache
35
-	timeAttackDetector TimeAttackDetector
36
-	ipBlocklist        IPBlocklist
37
-	eventStream        EventStream
38
-	logger             Logger
26
+	idleTimeout          time.Duration
27
+	tolerateTimeSkewness time.Duration
28
+	bufferSize           int
29
+	domainFrontingPort   int
30
+	workerPool           *ants.PoolWithFunc
31
+	telegram             *telegram.Telegram
32
+
33
+	secret          Secret
34
+	network         Network
35
+	antiReplayCache AntiReplayCache
36
+	ipBlocklist     IPBlocklist
37
+	eventStream     EventStream
38
+	logger          Logger
39 39
 }
40 40
 
41 41
 // DomainFrontingAddress returns a host:port pair for a fronting domain.
@@ -159,15 +159,11 @@ func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
159 159
 		return false
160 160
 	}
161 161
 
162
-	if hello.Host != "" && hello.Host != p.secret.Host {
163
-		p.logger.BindStr("hostname", hello.Host).Info("incorrect domain was found in SNI")
164
-		p.doDomainFronting(ctx, rewind)
165
-
166
-		return false
167
-	}
168
-
169
-	if err := p.timeAttackDetector.Valid(hello.Time); err != nil {
170
-		p.logger.InfoError("invalid faketls time", err)
162
+	if err := hello.Valid(p.secret.Host, p.tolerateTimeSkewness); err != nil {
163
+		p.logger.
164
+			BindStr("hostname", hello.Host).
165
+			BindStr("hello-time", hello.Time.String()).
166
+			InfoError("invalid faketls client hello", err)
171 167
 		p.doDomainFronting(ctx, rewind)
172 168
 
173 169
 		return false
@@ -281,19 +277,19 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
281 277
 
282 278
 	ctx, cancel := context.WithCancel(context.Background())
283 279
 	proxy := &Proxy{
284
-		ctx:                ctx,
285
-		ctxCancel:          cancel,
286
-		secret:             opts.Secret,
287
-		network:            opts.Network,
288
-		antiReplayCache:    opts.AntiReplayCache,
289
-		timeAttackDetector: opts.TimeAttackDetector,
290
-		ipBlocklist:        opts.IPBlocklist,
291
-		eventStream:        opts.EventStream,
292
-		logger:             opts.getLogger("proxy"),
293
-		domainFrontingPort: opts.getDomainFrontingPort(),
294
-		idleTimeout:        opts.getIdleTimeout(),
295
-		bufferSize:         opts.getBufferSize(),
296
-		telegram:           tg,
280
+		ctx:                  ctx,
281
+		ctxCancel:            cancel,
282
+		secret:               opts.Secret,
283
+		network:              opts.Network,
284
+		antiReplayCache:      opts.AntiReplayCache,
285
+		ipBlocklist:          opts.IPBlocklist,
286
+		eventStream:          opts.EventStream,
287
+		logger:               opts.getLogger("proxy"),
288
+		domainFrontingPort:   opts.getDomainFrontingPort(),
289
+		tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
290
+		idleTimeout:          opts.getIdleTimeout(),
291
+		bufferSize:           opts.getBufferSize(),
292
+		telegram:             tg,
297 293
 	}
298 294
 
299 295
 	pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),

+ 17
- 7
mtglib/proxy_opts.go Ver arquivo

@@ -23,11 +23,6 @@ type ProxyOpts struct {
23 23
 	// This is a mandatory setting.
24 24
 	AntiReplayCache AntiReplayCache
25 25
 
26
-	// TimeAttackDetector defines an instance of timeattack detector.
27
-	//
28
-	// This is a mandatory setting.
29
-	TimeAttackDetector TimeAttackDetector
30
-
31 26
 	// IPBlocklist defines an instance of IP blocklist.
32 27
 	//
33 28
 	// This is a mandatory setting.
@@ -80,6 +75,15 @@ type ProxyOpts struct {
80 75
 	// This is an optional setting.
81 76
 	IdleTimeout time.Duration
82 77
 
78
+	// TolerateTimeSkewness is a time boundary that defines a time
79
+	// range where faketls timestamp is acceptable.
80
+	//
81
+	// This means that if if you got a timestamp X, now is Y, then
82
+	// if |X-Y| < TolerateTimeSkewness, then you accept a packet.
83
+	//
84
+	// This is an optional setting.
85
+	TolerateTimeSkewness time.Duration
86
+
83 87
 	// PreferIP defines an IP connectivity preference. Valid values are:
84 88
 	// 'prefer-ipv4', 'prefer-ipv6', 'only-ipv4', 'only-ipv6'.
85 89
 	//
@@ -97,8 +101,6 @@ func (p ProxyOpts) valid() error {
97 101
 		return ErrIPBlocklistIsNotDefined
98 102
 	case p.EventStream == nil:
99 103
 		return ErrEventStreamIsNotDefined
100
-	case p.TimeAttackDetector == nil:
101
-		return ErrTimeAttackDetectorIsNotDefined
102 104
 	case p.Logger == nil:
103 105
 		return ErrLoggerIsNotDefined
104 106
 	case !p.Secret.Valid():
@@ -140,6 +142,14 @@ func (p ProxyOpts) getIdleTimeout() time.Duration {
140 142
 	return p.IdleTimeout
141 143
 }
142 144
 
145
+func (p ProxyOpts) getTolerateTimeSkewness() time.Duration {
146
+	if p.TolerateTimeSkewness == 0 {
147
+		return DefaultTolerateTimeSkewness
148
+	}
149
+
150
+	return p.TolerateTimeSkewness
151
+}
152
+
143 153
 func (p ProxyOpts) getPreferIP() string {
144 154
 	if p.PreferIP == "" {
145 155
 		return DefaultPreferIP

+ 6
- 16
mtglib/proxy_test.go Ver arquivo

@@ -16,7 +16,6 @@ import (
16 16
 	"github.com/9seconds/mtg/v2/logger"
17 17
 	"github.com/9seconds/mtg/v2/mtglib"
18 18
 	"github.com/9seconds/mtg/v2/network"
19
-	"github.com/9seconds/mtg/v2/timeattack"
20 19
 	"github.com/stretchr/testify/suite"
21 20
 )
22 21
 
@@ -46,13 +45,12 @@ func (suite *ProxyTestSuite) SetupSuite() {
46 45
 	suite.NoError(err)
47 46
 
48 47
 	suite.opts = &mtglib.ProxyOpts{
49
-		Secret:             mtglib.GenerateSecret("httpbin.org"),
50
-		Network:            ntw,
51
-		AntiReplayCache:    antireplay.NewNoop(),
52
-		TimeAttackDetector: timeattack.NewNoop(),
53
-		IPBlocklist:        ipblocklist.NewNoop(),
54
-		EventStream:        events.NewNoopStream(),
55
-		Logger:             logger.NewNoopLogger(),
48
+		Secret:          mtglib.GenerateSecret("httpbin.org"),
49
+		Network:         ntw,
50
+		AntiReplayCache: antireplay.NewNoop(),
51
+		IPBlocklist:     ipblocklist.NewNoop(),
52
+		EventStream:     events.NewNoopStream(),
53
+		Logger:          logger.NewNoopLogger(),
56 54
 	}
57 55
 
58 56
 	proxy, err := mtglib.NewProxy(*suite.opts)
@@ -118,14 +116,6 @@ func (suite *ProxyTestSuite) TestCannotInitNoEventStream() {
118 116
 	suite.Error(err)
119 117
 }
120 118
 
121
-func (suite *ProxyTestSuite) TestCannotInitNoTimeAttackDetector() {
122
-	opts := *suite.opts
123
-	opts.TimeAttackDetector = nil
124
-
125
-	_, err := mtglib.NewProxy(opts)
126
-	suite.Error(err)
127
-}
128
-
129 119
 func (suite *ProxyTestSuite) TestCannotInitNoLogger() {
130 120
 	opts := *suite.opts
131 121
 	opts.Logger = nil

+ 0
- 39
timeattack/detector.go Ver arquivo

@@ -1,39 +0,0 @@
1
-package timeattack
2
-
3
-import (
4
-	"fmt"
5
-	"time"
6
-
7
-	"github.com/9seconds/mtg/v2/mtglib"
8
-)
9
-
10
-type detector struct {
11
-	time.Duration
12
-}
13
-
14
-func (d detector) Valid(then time.Time) error {
15
-	now := time.Now()
16
-
17
-	diff := now.Sub(then)
18
-	if diff < 0 {
19
-		diff = -diff
20
-	}
21
-
22
-	if diff > d.Duration {
23
-		return fmt.Errorf("time is invalid. now=%d, then=%d, diff=%v",
24
-			now.Unix(),
25
-			then.Unix(),
26
-			diff)
27
-	}
28
-
29
-	return nil
30
-}
31
-
32
-// NewDetector returns a new TimeAttackDetector which validates that
33
-// timestamp belongs to intervar [X-duration, X+duration], so a small
34
-// timeshift is acceptable.
35
-func NewDetector(duration time.Duration) mtglib.TimeAttackDetector {
36
-	return detector{
37
-		Duration: duration,
38
-	}
39
-}

+ 0
- 28
timeattack/detector_test.go Ver arquivo

@@ -1,28 +0,0 @@
1
-package timeattack_test
2
-
3
-import (
4
-	"testing"
5
-	"time"
6
-
7
-	"github.com/9seconds/mtg/v2/timeattack"
8
-	"github.com/stretchr/testify/suite"
9
-)
10
-
11
-type DetectorTestSuite struct {
12
-	suite.Suite
13
-}
14
-
15
-func (suite *DetectorTestSuite) TestOp() {
16
-	d := timeattack.NewDetector(time.Second)
17
-
18
-	suite.NoError(d.Valid(time.Now()))
19
-	suite.NoError(d.Valid(time.Now().Add(100 * time.Millisecond)))
20
-	suite.NoError(d.Valid(time.Now().Add(-100 * time.Millisecond)))
21
-	suite.Error(d.Valid(time.Now().Add(time.Hour)))
22
-	suite.Error(d.Valid(time.Now().Add(-time.Hour)))
23
-}
24
-
25
-func TestDetector(t *testing.T) {
26
-	t.Parallel()
27
-	suite.Run(t, &DetectorTestSuite{})
28
-}

+ 0
- 10
timeattack/init.go Ver arquivo

@@ -1,10 +0,0 @@
1
-// TimeAttack has implementation of mtglib.TimeAttackDetector.
2
-package timeattack
3
-
4
-import "time"
5
-
6
-// DefaultDuration is a default duration when timestamps are acceptable.
7
-//
8
-// It means that all timestamps which are X-DefaultDuration <= X <=
9
-// X+DefaultDuration are fine.
10
-const DefaultDuration = 5 * time.Second

+ 0
- 16
timeattack/noop.go Ver arquivo

@@ -1,16 +0,0 @@
1
-package timeattack
2
-
3
-import (
4
-	"time"
5
-
6
-	"github.com/9seconds/mtg/v2/mtglib"
7
-)
8
-
9
-type noop struct{}
10
-
11
-func (n noop) Valid(_ time.Time) error { return nil }
12
-
13
-// NewNoop returns TimeAttackDetector which accepts all timestamps.
14
-func NewNoop() mtglib.TimeAttackDetector {
15
-	return noop{}
16
-}

+ 0
- 26
timeattack/noop_test.go Ver arquivo

@@ -1,26 +0,0 @@
1
-package timeattack_test
2
-
3
-import (
4
-	"testing"
5
-	"time"
6
-
7
-	"github.com/9seconds/mtg/v2/timeattack"
8
-	"github.com/stretchr/testify/suite"
9
-)
10
-
11
-type NoopTestSuite struct {
12
-	suite.Suite
13
-}
14
-
15
-func (suite *NoopTestSuite) TestOp() {
16
-	d := timeattack.NewNoop()
17
-
18
-	suite.NoError(d.Valid(time.Now()))
19
-	suite.NoError(d.Valid(time.Now().Add(time.Hour)))
20
-	suite.NoError(d.Valid(time.Now().Add(-time.Hour)))
21
-}
22
-
23
-func TestNoop(t *testing.T) {
24
-	t.Parallel()
25
-	suite.Run(t, &NoopTestSuite{})
26
-}

Carregando…
Cancelar
Salvar