Skip to content

Commit 4f3b89f

Browse files
committed
TLS v1.3, empty values (default settings) of proxy-listener-cipher-suites and proxy-listener-curve-preferences config parameters will configure default golang TLS CipherSuites and CurvePreferences instead of preconfigured ones.
Naming of provided proxy-listener-cipher-suites names was changed e.g. from ECDHE-ECDSA-AES256-GCM-SHA384 to TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.
1 parent 3161c06 commit 4f3b89f

File tree

5 files changed

+115
-99
lines changed

5 files changed

+115
-99
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ VERSION ?= $(shell git describe --tags --always --dirty)
1010
GOPKGS = $(shell go list ./... | grep -v /vendor/)
1111
BUILD_FLAGS ?=
1212
LDFLAGS ?= -X github.com/grepplabs/kafka-proxy/config.Version=$(VERSION) -w -s
13-
TAG ?= "v0.2.9"
13+
TAG ?= "v0.3.0"
1414
GOARCH ?= amd64
1515
GOOS ?= linux
1616

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ As not every Kafka release adds new messages/versions which are relevant to the
4646

4747
Linux
4848

49-
curl -Ls https://github.com/grepplabs/kafka-proxy/releases/download/v0.2.9/kafka-proxy-v0.2.9-linux-amd64.tar.gz | tar xz
49+
curl -Ls https://github.com/grepplabs/kafka-proxy/releases/download/v0.3.0/kafka-proxy-v0.3.0-linux-amd64.tar.gz | tar xz
5050

5151
macOS
5252

53-
curl -Ls https://github.com/grepplabs/kafka-proxy/releases/download/v0.2.9/kafka-proxy-v0.2.9-darwin-amd64.tar.gz | tar xz
53+
curl -Ls https://github.com/grepplabs/kafka-proxy/releases/download/v0.3.0/kafka-proxy-v0.3.0-darwin-amd64.tar.gz | tar xz
5454

5555
2. Move the binary in to your PATH.
5656

@@ -68,7 +68,7 @@ Docker images are available on [Docker Hub](https://hub.docker.com/r/grepplabs/k
6868
6969
You can launch a kafka-proxy container for trying it out with
7070
71-
docker run --rm -p 30001-30003:30001-30003 grepplabs/kafka-proxy:v0.2.9 \
71+
docker run --rm -p 30001-30003:30001-30003 grepplabs/kafka-proxy:v0.3.0 \
7272
server \
7373
--bootstrap-server-mapping "localhost:19092,0.0.0.0:30001" \
7474
--bootstrap-server-mapping "localhost:29092,0.0.0.0:30002" \
@@ -87,7 +87,7 @@ Docker images with precompiled plugins located in `/opt/kafka-proxy/bin/` are ta
8787
8888
You can launch a kafka-proxy container with auth-ldap plugin for trying it out with
8989
90-
docker run --rm -p 30001-30003:30001-30003 grepplabs/kafka-proxy:v0.2.9-all \
90+
docker run --rm -p 30001-30003:30001-30003 grepplabs/kafka-proxy:v0.3.0-all \
9191
server \
9292
--bootstrap-server-mapping "localhost:19092,0.0.0.0:30001" \
9393
--bootstrap-server-mapping "localhost:29092,0.0.0.0:30002" \
@@ -224,6 +224,17 @@ You can launch a kafka-proxy container with auth-ldap plugin for trying it out w
224224
225225
export BOOTSTRAP_SERVER_MAPPING="192.168.99.100:32401,0.0.0.0:32402 192.168.99.100:32402,0.0.0.0:32403" && kafka-proxy server
226226
227+
228+
### Restrict proxy listener cipher suites
229+
230+
kafka-proxy server --bootstrap-server-mapping "localhost:19092,0.0.0.0:30001,localhost:30001" \
231+
--bootstrap-server-mapping "localhost:29092,0.0.0.0:30002,localhost:30002" \
232+
--bootstrap-server-mapping "localhost:39092,0.0.0.0:30003,localhost:30003" \
233+
--proxy-listener-cert-file "tls/ca-cert.pem" \
234+
--proxy-listener-key-file "tls/ca-key.pem" \
235+
--proxy-listener-tls-enable \
236+
--proxy-listener-cipher-suites TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256
237+
227238
### SASL authentication initiated by proxy example
228239
229240
SASL authentication is initiated by the proxy. SASL authentication is disabled on the clients and enabled on the Kafka brokers.

proxy/protocol/responses_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,9 +2452,11 @@ func testMetadataResponse(t *testing.T, apiVersion int16, payload string, expect
24522452
if err != nil {
24532453
t.Fatal(err)
24542454
}
2455-
for _, av := range dc.AttrValues() {
2456-
fmt.Printf("\"%s\",\n", av)
2457-
}
2455+
/*
2456+
for _, av := range dc.AttrValues() {
2457+
fmt.Printf("\"%s\",\n", av)
2458+
}
2459+
*/
24582460
a.Equal(expectedInput, dc.AttrValues())
24592461
resp, err := EncodeSchema(s, schema)
24602462
a.Nil(err)

proxy/tls.go

Lines changed: 30 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,48 @@ import (
66
"encoding/pem"
77
"io/ioutil"
88
"net"
9+
"reflect"
910
"strings"
1011
"time"
1112

1213
"github.com/grepplabs/kafka-proxy/config"
13-
"github.com/klauspost/cpuid"
1414
"github.com/pkg/errors"
1515
)
1616

17-
type clientCertSubjectField string
18-
19-
const (
20-
clientCertSubjectCommonName = "CN"
21-
clientCertSubjectCountry = "C"
22-
clientCertSubjectProvince = "S"
23-
clientCertSubjectLocality = "L"
24-
clientCertSubjectOrganization = "O"
25-
clientCertSubjectOrganizationalUnit = "OU"
26-
)
27-
2817
var (
29-
defaultCurvePreferences = []tls.CurveID{
30-
tls.CurveP256,
31-
tls.X25519,
32-
}
33-
3418
supportedCurvesMap = map[string]tls.CurveID{
3519
"X25519": tls.X25519,
3620
"P256": tls.CurveP256,
3721
"P384": tls.CurveP384,
3822
"P521": tls.CurveP521,
3923
}
40-
41-
defaultCiphers = []uint16{
42-
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
43-
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
44-
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
45-
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
46-
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
47-
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
48-
}
49-
50-
defaultCiphersNonAESNI = []uint16{
51-
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
52-
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
53-
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
54-
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
55-
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
56-
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
57-
}
58-
// https://github.com/mholt/caddy/blob/master/caddytls/config.go
5924
supportedCiphersMap = map[string]uint16{
60-
"ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
61-
"ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
62-
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
63-
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
64-
"ECDHE-ECDSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
65-
"ECDHE-RSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
66-
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
67-
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
68-
"ECDHE-RSA-AES128-CBC-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
69-
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
70-
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
71-
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
72-
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
73-
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
74-
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
25+
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
26+
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
27+
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
28+
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
29+
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
30+
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
31+
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
32+
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
33+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
34+
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
35+
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
36+
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
37+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
38+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
39+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
40+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
41+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
42+
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
43+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
44+
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
45+
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
46+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
47+
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
48+
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
49+
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
7550
}
76-
7751
zeroTime = time.Time{}
7852
)
7953

@@ -103,10 +77,6 @@ func newTLSListenerConfig(conf *config.Config) (*tls.Config, error) {
10377
if err != nil {
10478
return nil, err
10579
}
106-
// for security, ensure TLS_FALLBACK_SCSV is always included first
107-
if len(cipherSuites) == 0 || cipherSuites[0] != tls.TLS_FALLBACK_SCSV {
108-
cipherSuites = append([]uint16{tls.TLS_FALLBACK_SCSV}, cipherSuites...)
109-
}
11080
curvePreferences, err := getCurvePreferences(opts.ListenerCurvePreferences)
11181
if err != nil {
11282
return nil, err
@@ -142,56 +112,32 @@ func newTLSListenerConfig(conf *config.Config) (*tls.Config, error) {
142112
return cfg, nil
143113
}
144114

145-
func removeEmptyStrings(input []string) []string {
146-
output := []string{}
147-
for _, value := range input {
148-
if value == "" {
149-
continue
150-
}
151-
output = append(output, value)
152-
}
153-
return output
154-
}
155-
156115
func getCipherSuites(enabledCipherSuites []string) ([]uint16, error) {
157116
suites := make([]uint16, 0)
158117
for _, suite := range enabledCipherSuites {
159118
cipher, ok := supportedCiphersMap[strings.TrimSpace(suite)]
160119
if !ok {
161-
return nil, errors.Errorf("invalid cipher suite '%s' selected", suite)
120+
return nil, errors.Errorf("invalid cipher suite '%s' selected, supported ciphers %v", suite, reflect.ValueOf(supportedCiphersMap).MapKeys())
162121
}
163122
suites = append(suites, cipher)
164123
}
165124
if len(suites) == 0 {
166-
return getPreferredDefaultCiphers(), nil
125+
return nil, nil
167126
}
168127
return suites, nil
169128
}
170129

171-
// getPreferredDefaultCiphers returns an appropriate cipher suite to use, depending on
172-
// the hardware support available for AES-NI.
173-
//
174-
// See https://github.com/mholt/caddy/issues/1674
175-
func getPreferredDefaultCiphers() []uint16 {
176-
if cpuid.CPU.AesNi() {
177-
return defaultCiphers
178-
}
179-
180-
// Return a cipher suite that prefers ChaCha20
181-
return defaultCiphersNonAESNI
182-
}
183-
184130
func getCurvePreferences(enabledCurvePreferences []string) ([]tls.CurveID, error) {
185131
curvePreferences := make([]tls.CurveID, 0)
186132
for _, curveID := range enabledCurvePreferences {
187133
curvePreference, ok := supportedCurvesMap[strings.TrimSpace(curveID)]
188134
if !ok {
189-
return nil, errors.Errorf("invalid curveID '%s' selected", curveID)
135+
return nil, errors.Errorf("invalid curveID '%s' selected, supported curveIDs %v", curveID, reflect.ValueOf(supportedCurvesMap).MapKeys())
190136
}
191137
curvePreferences = append(curvePreferences, curvePreference)
192138
}
193139
if len(curvePreferences) == 0 {
194-
return defaultCurvePreferences, nil
140+
return nil, nil
195141
}
196142
return curvePreferences, nil
197143
}

proxy/tls_test.go

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ func TestDefaultCipherSuites(t *testing.T) {
2525
c := new(config.Config)
2626
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
2727
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
28+
c.Proxy.TLS.ListenerCipherSuites = []string{}
29+
c.Proxy.TLS.ListenerCurvePreferences = []string{}
2830

2931
serverConfig, err := newTLSListenerConfig(c)
3032
a.Nil(err)
31-
// TLS_FALLBACK_SCSV is added as first
32-
a.Equal(len(getPreferredDefaultCiphers())+1, len(serverConfig.CipherSuites))
33-
a.Equal(len(defaultCurvePreferences), len(serverConfig.CurvePreferences))
33+
a.Nil(serverConfig.CipherSuites)
34+
a.Nil(serverConfig.CurvePreferences)
3435
}
3536

36-
func TestEnabledCipherSuites(t *testing.T) {
37+
func TestEnabledCipherSuitesAndCurves(t *testing.T) {
3738
a := assert.New(t)
3839

3940
bundle := NewCertsBundle()
@@ -42,16 +43,72 @@ func TestEnabledCipherSuites(t *testing.T) {
4243
c := new(config.Config)
4344
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
4445
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
45-
c.Proxy.TLS.ListenerCipherSuites = []string{"ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES256-GCM-SHA384"}
46+
c.Proxy.TLS.ListenerCipherSuites = []string{"TLS_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}
4647
c.Proxy.TLS.ListenerCurvePreferences = []string{"P521"}
4748

4849
serverConfig, err := newTLSListenerConfig(c)
4950
a.Nil(err)
50-
// TLS_FALLBACK_SCSV is added as first
51-
a.Equal(3, len(serverConfig.CipherSuites))
51+
a.Equal(2, len(serverConfig.CipherSuites))
5252
a.Equal(1, len(serverConfig.CurvePreferences))
5353
}
5454

55+
func TestAllCipherSuitesAndCurves(t *testing.T) {
56+
a := assert.New(t)
57+
58+
bundle := NewCertsBundle()
59+
defer bundle.Close()
60+
61+
allSupportedCipherSuites := make([]string, 0)
62+
for k := range supportedCiphersMap {
63+
allSupportedCipherSuites = append(allSupportedCipherSuites, k)
64+
}
65+
allSupportedCurvesSuites := make([]string, 0)
66+
for k := range supportedCurvesMap {
67+
allSupportedCurvesSuites = append(allSupportedCurvesSuites, k)
68+
}
69+
70+
c := new(config.Config)
71+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
72+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
73+
c.Proxy.TLS.ListenerCipherSuites = allSupportedCipherSuites
74+
c.Proxy.TLS.ListenerCurvePreferences = allSupportedCurvesSuites
75+
76+
serverConfig, err := newTLSListenerConfig(c)
77+
a.Nil(err)
78+
a.Equal(len(allSupportedCipherSuites), len(serverConfig.CipherSuites))
79+
a.Equal(len(allSupportedCurvesSuites), len(serverConfig.CurvePreferences))
80+
}
81+
82+
func TestUnsupportedCipherSuite(t *testing.T) {
83+
a := assert.New(t)
84+
85+
bundle := NewCertsBundle()
86+
defer bundle.Close()
87+
88+
c := new(config.Config)
89+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
90+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
91+
c.Proxy.TLS.ListenerCipherSuites = []string{"TLS_unknown"}
92+
93+
_, err := newTLSListenerConfig(c)
94+
a.NotNil(err)
95+
}
96+
97+
func TestUnsupportedCurve(t *testing.T) {
98+
a := assert.New(t)
99+
100+
bundle := NewCertsBundle()
101+
defer bundle.Close()
102+
103+
c := new(config.Config)
104+
c.Proxy.TLS.ListenerCertFile = bundle.ServerCert.Name()
105+
c.Proxy.TLS.ListenerKeyFile = bundle.ServerKey.Name()
106+
c.Proxy.TLS.ListenerCurvePreferences = []string{"unknown"}
107+
108+
_, err := newTLSListenerConfig(c)
109+
a.NotNil(err)
110+
}
111+
55112
func TestTLSUnknownAuthorityNoCAChainCert(t *testing.T) {
56113
a := assert.New(t)
57114

0 commit comments

Comments
 (0)