Просмотр исходного кода

Implement domain fronting

tags/v2.0.0-rc1
9seconds 5 лет назад
Родитель
Сommit
bddf180575
7 измененных файлов: 127 добавлений и 55 удалений
  1. 5
    5
      cli/proxy.go
  2. 16
    16
      config/config.go
  3. 1
    1
      example.config.toml
  4. 34
    0
      mtglib/conns.go
  5. 5
    8
      mtglib/init.go
  6. 61
    20
      mtglib/proxy.go
  7. 5
    5
      mtglib/proxy_opts.go

+ 5
- 5
cli/proxy.go Просмотреть файл

46
 		TimeAttackDetector: timeattack.NewNoop(),
46
 		TimeAttackDetector: timeattack.NewNoop(),
47
 		EventStream:        events.NewNoopStream(),
47
 		EventStream:        events.NewNoopStream(),
48
 
48
 
49
-		Secret:      c.Config.Secret,
50
-		BufferSize:  c.Config.TCPBuffer.Value(mtglib.DefaultBufferSize),
51
-		CloakPort:   c.Config.CloakPort.Value(mtglib.DefaultCloakPort),
52
-		IdleTimeout: c.Config.Network.Timeout.Idle.Value(mtglib.DefaultIdleTimeout),
53
-		PreferIP:    c.Config.PreferIP.Value(mtglib.DefaultPreferIP),
49
+		Secret:             c.Config.Secret,
50
+		BufferSize:         c.Config.TCPBuffer.Value(mtglib.DefaultBufferSize),
51
+		DomainFrontingPort: c.Config.DomainFrontingPort.Value(mtglib.DefaultDomainFrontingPort),
52
+		IdleTimeout:        c.Config.Network.Timeout.Idle.Value(mtglib.DefaultIdleTimeout),
53
+		PreferIP:           c.Config.PreferIP.Value(mtglib.DefaultPreferIP),
54
 	}
54
 	}
55
 
55
 
56
 	defer func() {
56
 	defer func() {

+ 16
- 16
config/config.go Просмотреть файл

10
 )
10
 )
11
 
11
 
12
 type Config struct {
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
-	CloakPort   TypePort      `json:"cloak-port"`
19
-	Concurrency uint          `json:"concurrency"`
20
-	Defense     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 {
21
 		Time struct {
22
 			Enabled       bool         `json:"enabled"`
22
 			Enabled       bool         `json:"enabled"`
23
 			AllowSkewness TypeDuration `json:"allow-skewness"`
23
 			AllowSkewness TypeDuration `json:"allow-skewness"`
85
 }
85
 }
86
 
86
 
87
 type configRaw struct {
87
 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
-	CloakPort   uint   `toml:"cloak-port" json:"cloak-port,omitempty"`
94
-	Concurrency uint   `toml:"concurrency" json:"concurrency,omitempty"`
95
-	Defense     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 {
96
 		Time struct {
97
 			Enabled       bool   `toml:"enabled" json:"enabled,omitempty"`
97
 			Enabled       bool   `toml:"enabled" json:"enabled,omitempty"`
98
 			AllowSkewness string `toml:"allow-skewness" json:"allow-skewness,omitempty"`
98
 			AllowSkewness string `toml:"allow-skewness" json:"allow-skewness,omitempty"`

+ 1
- 1
example.config.toml Просмотреть файл

46
 
46
 
47
 # FakeTLS uses domain fronting protection. So it needs to know a port to
47
 # FakeTLS uses domain fronting protection. So it needs to know a port to
48
 # access.
48
 # access.
49
-cloak-port = 443
49
+domain-fronting-port = 443
50
 
50
 
51
 # network defines different network-related settings
51
 # network defines different network-related settings
52
 [network]
52
 [network]

+ 34
- 0
mtglib/conns.go Просмотреть файл

1
 package mtglib
1
 package mtglib
2
 
2
 
3
 import (
3
 import (
4
+	"bytes"
4
 	"context"
5
 	"context"
6
+	"io"
5
 	"net"
7
 	"net"
8
+	"sync"
6
 	"time"
9
 	"time"
7
 )
10
 )
8
 
11
 
43
 
46
 
44
 	return n, err // nolint: wrapcheck
47
 	return n, err // nolint: wrapcheck
45
 }
48
 }
49
+
50
+type connRewind struct {
51
+	net.Conn
52
+
53
+	active io.Reader
54
+	buf    bytes.Buffer
55
+	mutex  sync.RWMutex
56
+}
57
+
58
+func (c *connRewind) Read(p []byte) (int, error) {
59
+	c.mutex.RLock()
60
+	defer c.mutex.RUnlock()
61
+
62
+	return c.active.Read(p)
63
+}
64
+
65
+func (c *connRewind) Rewind() {
66
+	c.mutex.Lock()
67
+	defer c.mutex.Unlock()
68
+
69
+	c.active = io.MultiReader(&c.buf, c.Conn)
70
+}
71
+
72
+func newConnRewind(conn net.Conn) *connRewind {
73
+	rv := &connRewind{
74
+		Conn: conn,
75
+	}
76
+	rv.active = io.TeeReader(conn, &rv.buf)
77
+
78
+	return rv
79
+}

+ 5
- 8
mtglib/init.go Просмотреть файл

17
 	ErrIPBlocklistIsNotDefined        = errors.New("ip blocklist is not defined")
17
 	ErrIPBlocklistIsNotDefined        = errors.New("ip blocklist is not defined")
18
 	ErrEventStreamIsNotDefined        = errors.New("event stream is not defined")
18
 	ErrEventStreamIsNotDefined        = errors.New("event stream is not defined")
19
 	ErrLoggerIsNotDefined             = errors.New("logger is not defined")
19
 	ErrLoggerIsNotDefined             = errors.New("logger is not defined")
20
-
21
-	errCannotSendWelcomePacket = errors.New("cannot send welcome packet")
22
-	errReplayAttackDetected    = errors.New("replay attack detected")
23
 )
20
 )
24
 
21
 
25
 const (
22
 const (
26
-	DefaultConcurrency = 4096
27
-	DefaultBufferSize  = 16 * 1024 // 16 kib
28
-	DefaultCloakPort   = 443
29
-	DefaultIdleTimeout = time.Minute
30
-	DefaultPreferIP    = "prefer-ipv6"
23
+	DefaultConcurrency        = 4096
24
+	DefaultBufferSize         = 16 * 1024 // 16 kib
25
+	DefaultDomainFrontingPort = 443
26
+	DefaultIdleTimeout        = time.Minute
27
+	DefaultPreferIP           = "prefer-ipv6"
31
 )
28
 )
32
 
29
 
33
 type Network interface {
30
 type Network interface {

+ 61
- 20
mtglib/proxy.go Просмотреть файл

5
 	"errors"
5
 	"errors"
6
 	"fmt"
6
 	"fmt"
7
 	"net"
7
 	"net"
8
+	"strconv"
8
 	"sync"
9
 	"sync"
9
 	"time"
10
 	"time"
10
 
11
 
21
 	ctxCancel       context.CancelFunc
22
 	ctxCancel       context.CancelFunc
22
 	streamWaitGroup sync.WaitGroup
23
 	streamWaitGroup sync.WaitGroup
23
 
24
 
24
-	idleTimeout time.Duration
25
-	bufferSize  int
26
-	workerPool  *ants.PoolWithFunc
27
-	telegram    *telegram.Telegram
25
+	idleTimeout        time.Duration
26
+	bufferSize         int
27
+	domainFrontAddress string
28
+	workerPool         *ants.PoolWithFunc
29
+	telegram           *telegram.Telegram
28
 
30
 
29
 	secret             Secret
31
 	secret             Secret
30
 	network            Network
32
 	network            Network
59
 		ctx.logger.Info("Stream has been finished")
61
 		ctx.logger.Info("Stream has been finished")
60
 	}()
62
 	}()
61
 
63
 
62
-	if err := p.doFakeTLSHandshake(ctx); err != nil {
63
-		p.logger.InfoError("faketls handshake is failed", err)
64
-
64
+	if !p.doFakeTLSHandshake(ctx) {
65
 		return
65
 		return
66
 	}
66
 	}
67
 
67
 
77
 		return
77
 		return
78
 	}
78
 	}
79
 
79
 
80
-	rel := relay.AcquireRelay(ctx, p.logger.Named("relay"), p.bufferSize, p.idleTimeout)
80
+	rel := relay.AcquireRelay(ctx,
81
+		p.logger.Named("relay"), p.bufferSize, p.idleTimeout)
81
 	defer relay.ReleaseRelay(rel)
82
 	defer relay.ReleaseRelay(rel)
82
 
83
 
83
 	if err := rel.Process(ctx.clientConn, ctx.telegramConn); err != nil {
84
 	if err := rel.Process(ctx.clientConn, ctx.telegramConn); err != nil {
122
 	p.workerPool.Release()
123
 	p.workerPool.Release()
123
 }
124
 }
124
 
125
 
125
-func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) error {
126
+func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
126
 	rec := record.AcquireRecord()
127
 	rec := record.AcquireRecord()
127
 	defer record.ReleaseRecord(rec)
128
 	defer record.ReleaseRecord(rec)
128
 
129
 
129
-	if err := rec.Read(ctx.clientConn); err != nil {
130
-		return fmt.Errorf("cannot read client hello: %w", err)
130
+	rewind := newConnRewind(ctx.clientConn)
131
+
132
+	if err := rec.Read(rewind); err != nil {
133
+		p.logger.InfoError("cannot read client hello", err)
134
+		p.doDomainFronting(ctx, rewind)
135
+
136
+		return false
131
 	}
137
 	}
132
 
138
 
133
 	hello, err := faketls.ParseClientHello(p.secret.Key[:], rec.Payload.Bytes())
139
 	hello, err := faketls.ParseClientHello(p.secret.Key[:], rec.Payload.Bytes())
134
 	if err != nil {
140
 	if err != nil {
135
-		return fmt.Errorf("cannot parse client hello: %w", err)
141
+		p.logger.InfoError("cannot parse client hello", err)
142
+		p.doDomainFronting(ctx, rewind)
143
+
144
+		return false
136
 	}
145
 	}
137
 
146
 
138
 	if err := p.timeAttackDetector.Valid(hello.Time); err != nil {
147
 	if err := p.timeAttackDetector.Valid(hello.Time); err != nil {
139
-		return fmt.Errorf("invalid time: %w", err)
148
+		p.logger.InfoError("invalid faketls time", err)
149
+		p.doDomainFronting(ctx, rewind)
150
+
151
+		return false
140
 	}
152
 	}
141
 
153
 
142
 	if p.antiReplayCache.SeenBefore(hello.SessionID) {
154
 	if p.antiReplayCache.SeenBefore(hello.SessionID) {
143
-		return errReplayAttackDetected
155
+		p.logger.Warning("replay attack has been detected!")
156
+		p.doDomainFronting(ctx, rewind)
157
+
158
+		return false
144
 	}
159
 	}
145
 
160
 
146
-	if err := faketls.SendWelcomePacket(ctx.clientConn, p.secret.Key[:], hello); err != nil {
161
+	if err := faketls.SendWelcomePacket(rewind, p.secret.Key[:], hello); err != nil {
147
 		p.logger.InfoError("cannot send welcome packet", err)
162
 		p.logger.InfoError("cannot send welcome packet", err)
148
 
163
 
149
-		return errCannotSendWelcomePacket
164
+		return false
150
 	}
165
 	}
151
 
166
 
152
 	ctx.clientConn = &faketls.Conn{
167
 	ctx.clientConn = &faketls.Conn{
153
 		Conn: ctx.clientConn,
168
 		Conn: ctx.clientConn,
154
 	}
169
 	}
155
 
170
 
156
-	return nil
171
+	return true
157
 }
172
 }
158
 
173
 
159
 func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
174
 func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
207
 	return nil
222
 	return nil
208
 }
223
 }
209
 
224
 
225
+func (p *Proxy) doDomainFronting(ctx context.Context, conn *connRewind) {
226
+	conn.Rewind()
227
+
228
+	frontConn, err := p.network.DialContext(ctx, "tcp", p.domainFrontAddress)
229
+	if err != nil {
230
+		p.logger.WarningError("cannot dial to the fronting domain", err)
231
+
232
+		return
233
+	}
234
+
235
+	rel := relay.AcquireRelay(ctx,
236
+		p.logger.Named("domain-fronting"), p.bufferSize, p.idleTimeout)
237
+	defer relay.ReleaseRelay(rel)
238
+
239
+	if err := rel.Process(conn, frontConn); err != nil {
240
+		p.logger.DebugError("domain fronting relay has been finished", err)
241
+	}
242
+}
243
+
210
 func NewProxy(opts ProxyOpts) (*Proxy, error) { // nolint: cyclop, funlen
244
 func NewProxy(opts ProxyOpts) (*Proxy, error) { // nolint: cyclop, funlen
211
 	switch {
245
 	switch {
212
 	case opts.Network == nil:
246
 	case opts.Network == nil:
245
 		bufferSize = DefaultBufferSize
279
 		bufferSize = DefaultBufferSize
246
 	}
280
 	}
247
 
281
 
282
+	domainFrontingPort := int(opts.DomainFrontingPort)
283
+	if domainFrontingPort == 0 {
284
+		domainFrontingPort = DefaultDomainFrontingPort
285
+	}
286
+
248
 	ctx, cancel := context.WithCancel(context.Background())
287
 	ctx, cancel := context.WithCancel(context.Background())
249
 	proxy := &Proxy{
288
 	proxy := &Proxy{
250
 		ctx:                ctx,
289
 		ctx:                ctx,
256
 		ipBlocklist:        opts.IPBlocklist,
295
 		ipBlocklist:        opts.IPBlocklist,
257
 		eventStream:        opts.EventStream,
296
 		eventStream:        opts.EventStream,
258
 		logger:             opts.Logger.Named("proxy"),
297
 		logger:             opts.Logger.Named("proxy"),
259
-		idleTimeout:        idleTimeout,
260
-		bufferSize:         int(bufferSize),
261
-		telegram:           tg,
298
+		domainFrontAddress: net.JoinHostPort(opts.Secret.Host,
299
+			strconv.Itoa(domainFrontingPort)),
300
+		idleTimeout: idleTimeout,
301
+		bufferSize:  int(bufferSize),
302
+		telegram:    tg,
262
 	}
303
 	}
263
 
304
 
264
 	pool, err := ants.NewPoolWithFunc(int(concurrency), func(arg interface{}) {
305
 	pool, err := ants.NewPoolWithFunc(int(concurrency), func(arg interface{}) {

+ 5
- 5
mtglib/proxy_opts.go Просмотреть файл

11
 	EventStream        EventStream
11
 	EventStream        EventStream
12
 	Logger             Logger
12
 	Logger             Logger
13
 
13
 
14
-	BufferSize  uint
15
-	Concurrency uint
16
-	CloakPort   uint
17
-	IdleTimeout time.Duration
18
-	PreferIP    string
14
+	BufferSize         uint
15
+	Concurrency        uint
16
+	DomainFrontingPort uint
17
+	IdleTimeout        time.Duration
18
+	PreferIP           string
19
 }
19
 }

Загрузка…
Отмена
Сохранить