Bläddra i källkod

Add support for domain fronting proxy protocol

tags/v2.1.11^2^2
9seconds 2 månader sedan
förälder
incheckning
cde313b359

+ 4
- 0
example.config.toml Visa fil

@@ -67,6 +67,10 @@ domain-fronting-port = 443
67 67
 # default value is not set (DNS resolution is used).
68 68
 # domain-fronting-ip = "142.250.185.112"
69 69
 
70
+# This makes a communication between both fronting website and mtg to use
71
+# proxy protocol.
72
+domain-fronting-proxy-protocol = false
73
+
70 74
 # FakeTLS can compare timestamps to prevent probes. Each message has
71 75
 # encrypted timestamp. So, mtg can compare this timestamp and decide if
72 76
 # we need to proceed with connection or not.

+ 5
- 4
internal/cli/run_proxy.go Visa fil

@@ -250,10 +250,11 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
250 250
 		IPAllowlist:     allowlist,
251 251
 		EventStream:     eventStream,
252 252
 
253
-		Secret:             conf.Secret,
254
-		DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort),
255
-		DomainFrontingIP:   conf.DomainFrontingIP.String(),
256
-		PreferIP:           conf.PreferIP.Get(mtglib.DefaultPreferIP),
253
+		Secret:                      conf.Secret,
254
+		DomainFrontingPort:          conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort),
255
+		DomainFrontingIP:            conf.DomainFrontingIP.String(),
256
+		DomainFrontingProxyProtocol: conf.DomainFrontingProxyProtocol.Get(false),
257
+		PreferIP:                    conf.PreferIP.Get(mtglib.DefaultPreferIP),
257 258
 
258 259
 		AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
259 260
 		TolerateTimeSkewness:     conf.TolerateTimeSkewness.Value,

+ 12
- 11
internal/config/config.go Visa fil

@@ -21,17 +21,18 @@ type ListConfig struct {
21 21
 }
22 22
 
23 23
 type Config struct {
24
-	Debug                    TypeBool        `json:"debug"`
25
-	AllowFallbackOnUnknownDC TypeBool        `json:"allowFallbackOnUnknownDc"`
26
-	Secret                   mtglib.Secret   `json:"secret"`
27
-	BindTo                   TypeHostPort    `json:"bindTo"`
28
-	ProxyProtocolListener    TypeBool        `json:"proxyProtocolListener"`
29
-	PreferIP                 TypePreferIP    `json:"preferIp"`
30
-	DomainFrontingPort       TypePort        `json:"domainFrontingPort"`
31
-	DomainFrontingIP         TypeIP          `json:"domainFrontingIp"`
32
-	TolerateTimeSkewness     TypeDuration    `json:"tolerateTimeSkewness"`
33
-	Concurrency              TypeConcurrency `json:"concurrency"`
34
-	Defense                  struct {
24
+	Debug                       TypeBool        `json:"debug"`
25
+	AllowFallbackOnUnknownDC    TypeBool        `json:"allowFallbackOnUnknownDc"`
26
+	Secret                      mtglib.Secret   `json:"secret"`
27
+	BindTo                      TypeHostPort    `json:"bindTo"`
28
+	ProxyProtocolListener       TypeBool        `json:"proxyProtocolListener"`
29
+	PreferIP                    TypePreferIP    `json:"preferIp"`
30
+	DomainFrontingPort          TypePort        `json:"domainFrontingPort"`
31
+	DomainFrontingIP            TypeIP          `json:"domainFrontingIp"`
32
+	DomainFrontingProxyProtocol TypeBool        `json:"domainFrontingProxyProtocol"`
33
+	TolerateTimeSkewness        TypeDuration    `json:"tolerateTimeSkewness"`
34
+	Concurrency                 TypeConcurrency `json:"concurrency"`
35
+	Defense                     struct {
35 36
 		AntiReplay struct {
36 37
 			Optional
37 38
 

+ 12
- 11
internal/config/parse.go Visa fil

@@ -9,17 +9,18 @@ import (
9 9
 )
10 10
 
11 11
 type tomlConfig struct {
12
-	Debug                    bool   `toml:"debug" json:"debug,omitempty"`
13
-	AllowFallbackOnUnknownDC bool   `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"`
14
-	Secret                   string `toml:"secret" json:"secret"`
15
-	BindTo                   string `toml:"bind-to" json:"bindTo"`
16
-	ProxyProtocolListener    bool   `toml:"proxy-protocol-listener" json:"proxyProtocolListener"`
17
-	PreferIP                 string `toml:"prefer-ip" json:"preferIp,omitempty"`
18
-	DomainFrontingPort       uint   `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
19
-	DomainFrontingIP         string `toml:"domain-fronting-ip" json:"domainFrontingIp,omitempty"`
20
-	TolerateTimeSkewness     string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
21
-	Concurrency              uint   `toml:"concurrency" json:"concurrency,omitempty"`
22
-	Defense                  struct {
12
+	Debug                       bool   `toml:"debug" json:"debug,omitempty"`
13
+	AllowFallbackOnUnknownDC    bool   `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"`
14
+	Secret                      string `toml:"secret" json:"secret"`
15
+	BindTo                      string `toml:"bind-to" json:"bindTo"`
16
+	ProxyProtocolListener       bool   `toml:"proxy-protocol-listener" json:"proxyProtocolListener"`
17
+	PreferIP                    string `toml:"prefer-ip" json:"preferIp,omitempty"`
18
+	DomainFrontingPort          uint   `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
19
+	DomainFrontingIP            string `toml:"domain-fronting-ip" json:"domainFrontingIp,omitempty"`
20
+	DomainFrontingProxyProtocol bool   `toml:"domain-fronting-proxy-protocol" json:"domainFrontingProxyProtocol,omitempty"`
21
+	TolerateTimeSkewness        string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
22
+	Concurrency                 uint   `toml:"concurrency" json:"concurrency,omitempty"`
23
+	Defense                     struct {
23 24
 		AntiReplay struct {
24 25
 			Enabled   bool    `toml:"enabled" json:"enabled,omitempty"`
25 26
 			MaxSize   string  `toml:"max-size" json:"maxSize,omitempty"`

+ 36
- 0
mtglib/conns.go Visa fil

@@ -3,9 +3,12 @@ package mtglib
3 3
 import (
4 4
 	"bytes"
5 5
 	"context"
6
+	"fmt"
6 7
 	"io"
8
+	"net"
7 9
 
8 10
 	"github.com/9seconds/mtg/v2/essentials"
11
+	"github.com/pires/go-proxyproto"
9 12
 )
10 13
 
11 14
 type connTraffic struct {
@@ -59,3 +62,36 @@ func newConnRewind(conn essentials.Conn) *connRewind {
59 62
 
60 63
 	return rv
61 64
 }
65
+
66
+type connProxyProtocol struct {
67
+	essentials.Conn
68
+
69
+	sourceAddr     net.Addr
70
+	headersWritten bool
71
+}
72
+
73
+func (c *connProxyProtocol) Write(p []byte) (int, error) {
74
+	if !c.headersWritten {
75
+		headers := proxyproto.HeaderProxyFromAddrs(2, c.sourceAddr, c.RemoteAddr())
76
+
77
+		toSend, err := headers.Format()
78
+		if err != nil {
79
+			panic(err)
80
+		}
81
+
82
+		if _, err := c.Conn.Write(toSend); err != nil {
83
+			return 0, fmt.Errorf("cannot send proxy protocol header: %w", err)
84
+		}
85
+
86
+		c.headersWritten = true
87
+	}
88
+
89
+	return c.Conn.Write(p)
90
+}
91
+
92
+func newConnProxyProtocol(source, target essentials.Conn) *connProxyProtocol {
93
+	return &connProxyProtocol{
94
+		Conn:       target,
95
+		sourceAddr: source.RemoteAddr(),
96
+	}
97
+}

+ 96
- 0
mtglib/conns_internal_test.go Visa fil

@@ -1,14 +1,17 @@
1 1
 package mtglib
2 2
 
3 3
 import (
4
+	"bufio"
4 5
 	"bytes"
5 6
 	"context"
6 7
 	"errors"
7 8
 	"io"
9
+	"net"
8 10
 	"testing"
9 11
 	"time"
10 12
 
11 13
 	"github.com/9seconds/mtg/v2/internal/testlib"
14
+	"github.com/pires/go-proxyproto"
12 15
 	"github.com/stretchr/testify/mock"
13 16
 	"github.com/stretchr/testify/suite"
14 17
 )
@@ -200,6 +203,94 @@ func (suite *ConnRewindTestSuite) TestRead() {
200 203
 	suite.Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, data)
201 204
 }
202 205
 
206
+type ConnProxyProtocolTestSuite struct {
207
+	suite.Suite
208
+
209
+	sourceConnMock *testlib.EssentialsConnMock
210
+	targetConnMock *testlib.EssentialsConnMock
211
+	conn           *connProxyProtocol
212
+}
213
+
214
+func (suite *ConnProxyProtocolTestSuite) SetupTest() {
215
+	suite.sourceConnMock = &testlib.EssentialsConnMock{}
216
+	suite.targetConnMock = &testlib.EssentialsConnMock{}
217
+
218
+	localAddr := &net.TCPAddr{
219
+		IP: net.ParseIP("127.0.0.1").To4(),
220
+	}
221
+	remoteAddr := &net.TCPAddr{
222
+		IP: net.ParseIP("127.0.0.2").To4(),
223
+	}
224
+
225
+	suite.sourceConnMock.
226
+		On("RemoteAddr").
227
+		Return(localAddr)
228
+	suite.targetConnMock.
229
+		On("RemoteAddr").
230
+		Maybe().
231
+		Return(remoteAddr)
232
+
233
+	suite.conn = newConnProxyProtocol(suite.sourceConnMock, suite.targetConnMock)
234
+}
235
+
236
+func (suite *ConnProxyProtocolTestSuite) TestRead() {
237
+	value := []byte{1, 2, 3, 4, 5}
238
+	toRead := make([]byte, len(value))
239
+
240
+	suite.targetConnMock.
241
+		On("Read", mock.AnythingOfType("[]uint8")).
242
+		Once().
243
+		Return(len(toRead), nil).
244
+		Run(func(args mock.Arguments) {
245
+			arr := args.Get(0).([]byte)
246
+			copy(arr, value)
247
+		})
248
+
249
+	n, err := suite.conn.Read(toRead)
250
+	suite.Equal(len(value), n)
251
+	suite.NoError(err)
252
+	suite.Equal(value, toRead)
253
+}
254
+
255
+func (suite *ConnProxyProtocolTestSuite) TestWrite() {
256
+	value := []byte{1, 2, 3, 4, 5}
257
+	buf := &bytes.Buffer{}
258
+	bufReader := bufio.NewReader(buf)
259
+
260
+	suite.targetConnMock.
261
+		On("Write", mock.AnythingOfType("[]uint8")).
262
+		Return(28, nil).
263
+		Run(func(args mock.Arguments) {
264
+			arr := args.Get(0).([]byte)
265
+			buf.Write(arr)
266
+		})
267
+
268
+	_, err := suite.conn.Write(value)
269
+	suite.NoError(err)
270
+
271
+	header, err := proxyproto.Read(bufReader)
272
+	suite.NoError(err)
273
+
274
+	sourceAddr, destAddr, ok := header.TCPAddrs()
275
+	suite.True(ok)
276
+	suite.Equal(suite.sourceConnMock.RemoteAddr(), sourceAddr)
277
+	suite.Equal(suite.targetConnMock.RemoteAddr(), destAddr)
278
+
279
+	read, _ := io.ReadAll(bufReader)
280
+	suite.Equal(value, read)
281
+
282
+	_, err = suite.conn.Write(value)
283
+	suite.NoError(err)
284
+
285
+	read, _ = io.ReadAll(bufReader)
286
+	suite.Equal(value, read)
287
+}
288
+
289
+func (suite *ConnProxyProtocolTestSuite) TearDownTest() {
290
+	suite.sourceConnMock.AssertExpectations(suite.T())
291
+	suite.targetConnMock.AssertExpectations(suite.T())
292
+}
293
+
203 294
 func TestConnTraffic(t *testing.T) {
204 295
 	t.Parallel()
205 296
 	suite.Run(t, &ConnTrafficTestSuite{})
@@ -209,3 +300,8 @@ func TestConnRewind(t *testing.T) {
209 300
 	t.Parallel()
210 301
 	suite.Run(t, &ConnRewindTestSuite{})
211 302
 }
303
+
304
+func TestConnProxyProtocol(t *testing.T) {
305
+	t.Parallel()
306
+	suite.Run(t, &ConnProxyProtocolTestSuite{})
307
+}

+ 25
- 19
mtglib/proxy.go Visa fil

@@ -24,12 +24,13 @@ type Proxy struct {
24 24
 	ctxCancel       context.CancelFunc
25 25
 	streamWaitGroup sync.WaitGroup
26 26
 
27
-	allowFallbackOnUnknownDC bool
28
-	tolerateTimeSkewness     time.Duration
29
-	domainFrontingPort       int
30
-	domainFrontingIP         string
31
-	workerPool               *ants.PoolWithFunc
32
-	telegram                 *dc.Telegram
27
+	allowFallbackOnUnknownDC    bool
28
+	tolerateTimeSkewness        time.Duration
29
+	domainFrontingPort          int
30
+	domainFrontingIP            string
31
+	domainFrontingProxyProtocol bool
32
+	workerPool                  *ants.PoolWithFunc
33
+	telegram                    *dc.Telegram
33 34
 	configUpdater            *dc.PublicConfigUpdater
34 35
 	clientObfuscatror        obfuscation.Obfuscator
35 36
 
@@ -285,6 +286,10 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) {
285 286
 		return
286 287
 	}
287 288
 
289
+	if p.domainFrontingProxyProtocol {
290
+		frontConn = newConnProxyProtocol(ctx.clientConn, frontConn)
291
+	}
292
+
288 293
 	frontConn = connTraffic{
289 294
 		Conn:     frontConn,
290 295
 		ctx:      ctx,
@@ -316,20 +321,20 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
316 321
 	updatersLogger := logger.Named("telegram-updaters")
317 322
 
318 323
 	proxy := &Proxy{
319
-		ctx:                      ctx,
320
-		ctxCancel:                cancel,
321
-		secret:                   opts.Secret,
322
-		network:                  opts.Network,
323
-		antiReplayCache:          opts.AntiReplayCache,
324
-		blocklist:                opts.IPBlocklist,
325
-		allowlist:                opts.IPAllowlist,
326
-		eventStream:              opts.EventStream,
324
+		ctx:                         ctx,
325
+		ctxCancel:                   cancel,
326
+		secret:                      opts.Secret,
327
+		network:                     opts.Network,
328
+		antiReplayCache:             opts.AntiReplayCache,
329
+		blocklist:                   opts.IPBlocklist,
330
+		allowlist:                   opts.IPAllowlist,
331
+		eventStream:                 opts.EventStream,
327 332
 		logger:                   logger,
328
-		domainFrontingPort:       opts.getDomainFrontingPort(),
329
-		domainFrontingIP:         opts.DomainFrontingIP,
330
-		tolerateTimeSkewness:     opts.getTolerateTimeSkewness(),
331
-		allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
332
-		telegram:                 tg,
333
+		domainFrontingPort:          opts.getDomainFrontingPort(),
334
+		domainFrontingIP:            opts.DomainFrontingIP,
335
+		tolerateTimeSkewness:        opts.getTolerateTimeSkewness(),
336
+		allowFallbackOnUnknownDC:    opts.AllowFallbackOnUnknownDC,
337
+		telegram:                    tg,
333 338
 		configUpdater: dc.NewPublicConfigUpdater(
334 339
 			tg,
335 340
 			updatersLogger.Named("public-config"),
@@ -338,6 +343,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
338 343
 		clientObfuscatror: obfuscation.Obfuscator{
339 344
 			Secret: opts.Secret.Key[:],
340 345
 		},
346
+		domainFrontingProxyProtocol: opts.DomainFrontingProxyProtocol,
341 347
 	}
342 348
 
343 349
 	proxy.configUpdater.Run(ctx, dc.PublicConfigUpdateURLv4, "tcp4")

+ 8
- 0
mtglib/proxy_opts.go Visa fil

@@ -102,6 +102,14 @@ type ProxyOpts struct {
102 102
 	// This is an optional setting.
103 103
 	DomainFrontingIP string
104 104
 
105
+	// DomainFrontingProxyProtocol is used if communication between upstream
106
+	// endpoint and mtg supports proxy protocol. This is useful in case
107
+	// if mtg is also placed behind load balancer, and this will make
108
+	// fronting webserver to know about real IP addresses
109
+	//
110
+	// This is an optional setting.
111
+	DomainFrontingProxyProtocol bool
112
+
105 113
 	// AllowFallbackOnUnknownDC defines how proxy behaves if unknown DC was
106 114
 	// requested. If this setting is set to false, then such connection will be
107 115
 	// rejected. Otherwise, proxy will chose any DC.

Laddar…
Avbryt
Spara