Skip to content

Commit 760bc31

Browse files
committed
Test connect to Kafka through HTTP Proxy
1 parent 13e4f8b commit 760bc31

File tree

4 files changed

+249
-14
lines changed

4 files changed

+249
-14
lines changed

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ Connect through test SOCKS5 Proxy server
219219
--bootstrap-server-mapping "kafka-1.grepplabs.com:9092,127.0.0.1:32501" \
220220
--bootstrap-server-mapping "kafka-2.grepplabs.com:9092,127.0.0.1:32502"
221221
--forward-proxy socks5://localhost:1080
222-
223222
```
224223
225224
```
@@ -229,7 +228,6 @@ Connect through test SOCKS5 Proxy server
229228
--bootstrap-server-mapping "kafka-1.grepplabs.com:9092,127.0.0.1:32501" \
230229
--bootstrap-server-mapping "kafka-2.grepplabs.com:9092,127.0.0.1:32502" \
231230
--forward-proxy socks5://my-proxy-user:my-proxy-password@localhost:1080
232-
233231
```
234232
235233
### Connect to Kafka through HTTP Proxy example
@@ -243,7 +241,6 @@ Connect through test HTTP Proxy server using CONNECT method
243241
--bootstrap-server-mapping "kafka-1.grepplabs.com:9092,127.0.0.1:32501" \
244242
--bootstrap-server-mapping "kafka-2.grepplabs.com:9092,127.0.0.1:32502"
245243
--forward-proxy http://localhost:3128
246-
247244
```
248245
249246
```
@@ -253,7 +250,6 @@ Connect through test HTTP Proxy server using CONNECT method
253250
--bootstrap-server-mapping "kafka-1.grepplabs.com:9092,127.0.0.1:32501" \
254251
--bootstrap-server-mapping "kafka-2.grepplabs.com:9092,127.0.0.1:32502" \
255252
--forward-proxy http://my-proxy-user:my-proxy-password@localhost:3128
256-
257253
```
258254
259255
### Kubernetes sidecar container example

proxy/auth_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@ func TestAuthHandshake(t *testing.T) {
1717
testAuthHandshake(a, makePipe)
1818
}
1919

20-
func TestAuthHandshakeSocks5(t *testing.T) {
20+
func TestAuthHandshakeSocks5Proxy(t *testing.T) {
2121
a := assert.New(t)
22-
testAuthHandshake(a, makeSocks5Pipe)
22+
testAuthHandshake(a, makeSocks5ProxyPipe)
23+
}
24+
25+
func TestAuthHandshakeHttpProxy(t *testing.T) {
26+
a := assert.New(t)
27+
testAuthHandshake(a, makeHttpProxyPipe)
2328
}
2429

2530
func testAuthHandshake(a *assert.Assertions, mp func() (c1, c2 net.Conn, stop func(), err error)) {

proxy/tls_test.go

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func TestTLSSelfSigned(t *testing.T) {
120120
pingPong(t, c1, c2)
121121
}
122122

123-
func TestTLSThroughSocks5(t *testing.T) {
123+
func TestTLSThroughSocks5Proxy(t *testing.T) {
124124
a := assert.New(t)
125125

126126
bundle := NewCertsBundle()
@@ -131,15 +131,34 @@ func TestTLSThroughSocks5(t *testing.T) {
131131
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
132132
c.Kafka.TLS.CAChainCertFile = bundle.ServerCert.Name()
133133

134-
c1, c2, stop, err := makeTLSSocks5Pipe(c, nil, "", "")
134+
c1, c2, stop, err := makeTLSSocks5ProxyPipe(c, nil, "", "")
135135
if err != nil {
136136
a.FailNow(err.Error())
137137
}
138138
defer stop()
139139
pingPong(t, c1, c2)
140140
}
141141

142-
func TestTLSThroughSocks5WithCredentials(t *testing.T) {
142+
func TestTLSThroughHttpProxy(t *testing.T) {
143+
a := assert.New(t)
144+
145+
bundle := NewCertsBundle()
146+
defer bundle.Close()
147+
148+
c := new(config.Config)
149+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
150+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
151+
c.Kafka.TLS.CAChainCertFile = bundle.ServerCert.Name()
152+
153+
c1, c2, stop, err := makeTLSHttpProxyPipe(c, "", "", "", "")
154+
if err != nil {
155+
a.FailNow(err.Error())
156+
}
157+
defer stop()
158+
pingPong(t, c1, c2)
159+
}
160+
161+
func TestTLSThroughSocks5ProxyWithCredentials(t *testing.T) {
143162
a := assert.New(t)
144163

145164
bundle := NewCertsBundle()
@@ -156,15 +175,33 @@ func TestTLSThroughSocks5WithCredentials(t *testing.T) {
156175
password: "test-password",
157176
},
158177
}
159-
c1, c2, stop, err := makeTLSSocks5Pipe(c, authenticator, "test-user", "test-password")
178+
c1, c2, stop, err := makeTLSSocks5ProxyPipe(c, authenticator, "test-user", "test-password")
179+
if err != nil {
180+
a.FailNow(err.Error())
181+
}
182+
defer stop()
183+
pingPong(t, c1, c2)
184+
}
185+
186+
func TestTLSThroughHttpProxyWithCredentials(t *testing.T) {
187+
a := assert.New(t)
188+
189+
bundle := NewCertsBundle()
190+
defer bundle.Close()
191+
192+
c := new(config.Config)
193+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
194+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
195+
c.Kafka.TLS.CAChainCertFile = bundle.ServerCert.Name()
196+
c1, c2, stop, err := makeTLSHttpProxyPipe(c, "test-user", "test-password", "test-user", "test-password")
160197
if err != nil {
161198
a.FailNow(err.Error())
162199
}
163200
defer stop()
164201
pingPong(t, c1, c2)
165202
}
166203

167-
func TestTLSThroughSocks5WithBadCredentials(t *testing.T) {
204+
func TestTLSThroughSocks5ProxyWithBadCredentials(t *testing.T) {
168205
a := assert.New(t)
169206

170207
bundle := NewCertsBundle()
@@ -181,12 +218,28 @@ func TestTLSThroughSocks5WithBadCredentials(t *testing.T) {
181218
password: "test-password",
182219
},
183220
}
184-
_, _, _, err := makeTLSSocks5Pipe(c, authenticator, "test-user", "bad-password")
221+
_, _, _, err := makeTLSSocks5ProxyPipe(c, authenticator, "test-user", "bad-password")
185222
a.NotNil(err)
186223
a.True(strings.HasPrefix(err.Error(), "proxy: SOCKS5 proxy at"))
187224
a.True(strings.HasSuffix(err.Error(), "rejected username/password"))
188225
}
189226

227+
func TestTLSThroughHttpProxyWithBadCredentials(t *testing.T) {
228+
a := assert.New(t)
229+
230+
bundle := NewCertsBundle()
231+
defer bundle.Close()
232+
233+
c := new(config.Config)
234+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
235+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
236+
c.Kafka.TLS.CAChainCertFile = bundle.ServerCert.Name()
237+
238+
_, _, _, err := makeTLSHttpProxyPipe(c, "test-user", "test-password", "test-user", "bad-password")
239+
a.NotNil(err)
240+
a.Equal(err.Error(), "connect server using proxy error, statuscode [407]")
241+
}
242+
190243
func TestTLSVerifyClientCertDifferentCAs(t *testing.T) {
191244
a := assert.New(t)
192245

proxy/util_test.go

Lines changed: 183 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ import (
88
"crypto/x509/pkix"
99
"encoding/pem"
1010
"github.com/armon/go-socks5"
11+
"github.com/elazarl/goproxy"
12+
"github.com/elazarl/goproxy/ext/auth"
1113
"github.com/grepplabs/kafka-proxy/config"
1214
"github.com/pkg/errors"
1315
"io/ioutil"
1416
"math/big"
1517
"net"
18+
"net/http"
1619
"os"
1720
"time"
1821
)
@@ -101,7 +104,7 @@ func (s testCredentials) Valid(username, password string) bool {
101104
return s.username == username && s.password == password
102105
}
103106

104-
func makeTLSSocks5Pipe(conf *config.Config, authenticator socks5.Authenticator, username, password string) (c1, c2 net.Conn, stop func(), err error) {
107+
func makeTLSSocks5ProxyPipe(conf *config.Config, authenticator socks5.Authenticator, username, password string) (c1, c2 net.Conn, stop func(), err error) {
105108
socks5Conf := &socks5.Config{}
106109
if authenticator != nil {
107110
socks5Conf.AuthMethods = []socks5.Authenticator{authenticator}
@@ -209,6 +212,113 @@ func makeTLSSocks5Pipe(conf *config.Config, authenticator socks5.Authenticator,
209212
}
210213
}
211214

215+
func makeTLSHttpProxyPipe(conf *config.Config, proxyusername, proxypassword string, username, password string) (c1, c2 net.Conn, stop func(), err error) {
216+
server := goproxy.NewProxyHttpServer()
217+
218+
if proxyusername != "" && proxypassword != "" {
219+
server.OnRequest().HandleConnect(auth.BasicConnect("", func(user, passwd string) bool {
220+
return user == proxyusername && passwd == proxypassword
221+
}))
222+
}
223+
224+
if err != nil {
225+
return nil, nil, nil, err
226+
}
227+
clientConfig, err := newTLSClientConfig(conf)
228+
if err != nil {
229+
return nil, nil, nil, err
230+
}
231+
serverConfig, err := newTLSListenerConfig(conf)
232+
if err != nil {
233+
return nil, nil, nil, err
234+
}
235+
236+
proxy, err := net.Listen("tcp4", "127.0.0.1:0")
237+
if err != nil {
238+
return nil, nil, nil, err
239+
}
240+
httpProxy := &httpProxy{
241+
forwardDialer: directDialer{
242+
dialTimeout: 2 * time.Second,
243+
keepAlive: 60 * time.Second,
244+
},
245+
network: proxy.Addr().Network(),
246+
hostPort: proxy.Addr().String(),
247+
username: username,
248+
password: password,
249+
}
250+
251+
tlsDialer := tlsDialer{
252+
timeout: 3 * time.Second,
253+
rawDialer: httpProxy,
254+
config: clientConfig,
255+
}
256+
257+
target, err := tls.Listen("tcp", "127.0.0.1:0", serverConfig)
258+
if err != nil {
259+
proxy.Close()
260+
return nil, nil, nil, err
261+
}
262+
// Start a connection between two endpoints.
263+
var err0, err1, err2 error
264+
go func() {
265+
err0 = http.Serve(proxy, server)
266+
}()
267+
done := make(chan bool)
268+
go func() {
269+
c2, err2 = target.Accept()
270+
close(done)
271+
if err2 != nil {
272+
return
273+
}
274+
// will force handshake completion
275+
buf := make([]byte, 0)
276+
c2.Read(buf)
277+
278+
tlscon, ok := c2.(*tls.Conn)
279+
if ok {
280+
state := tlscon.ConnectionState()
281+
for _, v := range state.PeerCertificates {
282+
_ = v
283+
//fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
284+
}
285+
}
286+
}()
287+
stop = func() {
288+
if err1 == nil {
289+
c1.Close()
290+
}
291+
if err2 == nil {
292+
c2.Close()
293+
}
294+
target.Close()
295+
proxy.Close()
296+
}
297+
298+
c1, err1 = tlsDialer.Dial(target.Addr().Network(), target.Addr().String())
299+
if err1 != nil {
300+
target.Close()
301+
return nil, nil, nil, err1
302+
}
303+
select {
304+
case <-done:
305+
case <-time.After(4 * time.Second):
306+
target.Close()
307+
return nil, nil, nil, errors.New("Accept timeout ")
308+
}
309+
310+
switch {
311+
case err1 != nil:
312+
stop()
313+
return nil, nil, nil, err1
314+
case err2 != nil:
315+
stop()
316+
return nil, nil, nil, err2
317+
default:
318+
return c1, c2, stop, nil
319+
}
320+
}
321+
212322
func makePipe() (c1, c2 net.Conn, stop func(), err error) {
213323
dialer := directDialer{
214324
dialTimeout: 2 * time.Second,
@@ -257,7 +367,7 @@ func makePipe() (c1, c2 net.Conn, stop func(), err error) {
257367
}
258368
}
259369

260-
func makeSocks5Pipe() (c1, c2 net.Conn, stop func(), err error) {
370+
func makeSocks5ProxyPipe() (c1, c2 net.Conn, stop func(), err error) {
261371
server, err := socks5.New(&socks5.Config{})
262372
if err != nil {
263373
return nil, nil, nil, err
@@ -330,6 +440,77 @@ func makeSocks5Pipe() (c1, c2 net.Conn, stop func(), err error) {
330440
}
331441
}
332442

443+
func makeHttpProxyPipe() (c1, c2 net.Conn, stop func(), err error) {
444+
server := goproxy.NewProxyHttpServer()
445+
//server.Verbose = true
446+
447+
if err != nil {
448+
return nil, nil, nil, err
449+
}
450+
proxy, err := net.Listen("tcp4", "127.0.0.1:0")
451+
if err != nil {
452+
return nil, nil, nil, err
453+
}
454+
target, err := net.Listen("tcp4", "127.0.0.1:0")
455+
if err != nil {
456+
proxy.Close()
457+
return nil, nil, nil, err
458+
}
459+
httpProxy := httpProxy{
460+
forwardDialer: directDialer{
461+
dialTimeout: 2 * time.Second,
462+
keepAlive: 60 * time.Second,
463+
},
464+
network: proxy.Addr().Network(),
465+
hostPort: proxy.Addr().String(),
466+
}
467+
468+
var err0, err1, err2 error
469+
go func() {
470+
err0 = http.Serve(proxy, server)
471+
}()
472+
473+
done := make(chan bool)
474+
go func() {
475+
c2, err2 = target.Accept()
476+
close(done)
477+
}()
478+
479+
c1, err1 = httpProxy.Dial(target.Addr().Network(), target.Addr().String())
480+
481+
stop = func() {
482+
if err1 == nil {
483+
c1.Close()
484+
}
485+
if err2 == nil {
486+
c2.Close()
487+
}
488+
target.Close()
489+
proxy.Close()
490+
}
491+
492+
select {
493+
case <-done:
494+
case <-time.After(3 * time.Second):
495+
stop()
496+
return nil, nil, nil, errors.New("Accept timeout")
497+
}
498+
499+
switch {
500+
case err0 != nil:
501+
stop()
502+
return nil, nil, nil, err0
503+
case err1 != nil:
504+
stop()
505+
return nil, nil, nil, err1
506+
case err2 != nil:
507+
stop()
508+
return nil, nil, nil, err2
509+
default:
510+
return c1, c2, stop, nil
511+
}
512+
}
513+
333514
func generateCert(catls *tls.Certificate, certFile *os.File, keyFile *os.File) error {
334515
// Prepare certificate
335516
cert := &x509.Certificate{

0 commit comments

Comments
 (0)