Skip to content

Commit 3e0d065

Browse files
committed
crosscluster: do not panic on poorly formatted inline certs
Previously, when parsing inline SSL certificates in an external connection, if the certificate was not formatted correctly we would run into a panic from a nil pointer. This fixes the parser to properly surface an error instead of crashing. Fixes: #145949 Release note: CockroachDB now raises an error when encountering improper inline SSL credentials instead of panicking.
1 parent 7b71d93 commit 3e0d065

File tree

5 files changed

+123
-11
lines changed

5 files changed

+123
-11
lines changed

pkg/crosscluster/streamclient/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,10 @@ func WithLogical() Option {
306306
}
307307
}
308308

309-
func processOptions(opts []Option) *options {
310-
ret := &options{}
309+
func processOptions(opts []Option) options {
310+
ret := options{}
311311
for _, o := range opts {
312-
o(ret)
312+
o(&ret)
313313
}
314314
return ret
315315
}

pkg/crosscluster/streamclient/client_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,112 @@ func TestStreamClientAppName(t *testing.T) {
326326
expectAppName(t, "$ internal repstream")
327327
expectAppName(t, "$ internal repstream job id=1337", WithStreamID(1337))
328328
}
329+
330+
func TestInlineSSLCertParsing(t *testing.T) {
331+
defer leaktest.AfterTest(t)()
332+
defer log.Scope(t).Close(t)
333+
334+
// These are valid but totally unused certs and keys.
335+
// Generated with the following command:
336+
// openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -nodes -days 365
337+
const validCert = `-----BEGIN CERTIFICATE-----
338+
MIIC4jCCAkugAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBjTELMAkGA1UEBhMCdXMx
339+
DzANBgNVBAgMBkFzZ2FyZDEUMBIGA1UECgwLQ29ja3JvYWNoREIxGDAWBgNVBAMM
340+
D2NvY2tyb2FjaGRiLmNvbTE9MDsGCSqGSIb3DQEJARYubGlrZUlzYWlkdG90YWxs
341+
eWludmFsaWRjZXJ0c0Bub3RhcmVhbGVtYWlsLmNvbTAeFw0yNTA2MTIyMTM2MzJa
342+
Fw0yNjA2MTIyMTM2MzJaMIGNMQswCQYDVQQGEwJ1czEPMA0GA1UECAwGQXNnYXJk
343+
MRQwEgYDVQQKDAtDb2Nrcm9hY2hEQjEYMBYGA1UEAwwPY29ja3JvYWNoZGIuY29t
344+
MT0wOwYJKoZIhvcNAQkBFi5saWtlSXNhaWR0b3RhbGx5aW52YWxpZGNlcnRzQG5v
345+
dGFyZWFsZW1haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY6E7v
346+
T5R0U9hhfvXFqkgQt25I+kWNvP/E7tbJAq1012SB97YMkLMDPA3vQXe1oZIHd0A7
347+
GyYUd3uY8yCGfW+sc+dij7CXHpxwdyv3tQ8HQDA6qgC9AkkVuiu7xuP6lT9fgi+p
348+
l0UOWOeUi+uedmybk24fRRE2VO6LJ1ULLMduWQIDAQABo1AwTjAdBgNVHQ4EFgQU
349+
gkrW97/Ie1krxfJMKRYcdjfdc5YwHwYDVR0jBBgwFoAUgkrW97/Ie1krxfJMKRYc
350+
djfdc5YwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQA6LspJ1yDlls+I
351+
gtCkbJWE/v5zmiVloj5xeMpLgJN4csApXDXPVO5Po4mc5+WIe25yJLRrcErpTyfn
352+
aG/DuKXZTNJ6WHb4uOBRJ41t+i7CT0OJ2mxnHO2MwN/3JKVLaZlMJpgT6DyCha2h
353+
qEAr+iWb85uXBq1QOxUIOc+eolWWLg==
354+
-----END CERTIFICATE-----`
355+
const validKey = `-----BEGIN PRIVATE KEY-----
356+
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANjoTu9PlHRT2GF+
357+
9cWqSBC3bkj6RY28/8Tu1skCrXTXZIH3tgyQswM8De9Bd7Whkgd3QDsbJhR3e5jz
358+
IIZ9b6xz52KPsJcenHB3K/e1DwdAMDqqAL0CSRW6K7vG4/qVP1+CL6mXRQ5Y55SL
359+
6552bJuTbh9FETZU7osnVQssx25ZAgMBAAECgYATyDQSvUZDybXNRn/xtBL4e1Iy
360+
k6iuQZNuCX5LPNRG+LHw7H+M69F3tQ1sSaM6TG7+AVE5UsOJUFBUZbAMs/nwLFbF
361+
MONoI3ZRu5jL38Mgk6FPDC7+lmbpsKEyMIg6bipIcHao8IeYEwUgeF9lfV1IeX85
362+
UNieOJpzGYghxNGJUQJBAPzzrIIeHyNHok2NcTrbUYl9D3t65MTGJGs1YIGtmzvD
363+
tS4Zoc7dykO6aeGTCgn2n6KwXJBrCgSX49AxIKyVn70CQQDbhXEcJuBdvKlq0/VB
364+
wDksBLuAKBkGwJKAIxRlnhFLfVpJ2GEppj6H/1EQNBgCorTZwQYTWdh7/0qcxfmf
365+
RtTNAkBXceWxFbit+ZWiOcNrFWaaoSE5DsMHQ3hTl6BFND716jI4PaQyX3oM7+Sq
366+
lqphx2BoXY+iXV6ZN+kJj/I7t34BAkEAzX/7JhaCvV2K37Wyh63SF5IKkOt4miiW
367+
PJwaURKLMDcV2cFVG+9D5H4vvdJ2k6kLUjnvXRgjn9iaWW6/wspFFQJAV2dUQR+f
368+
ywVy1aYZnLNXZkiO5eWGBgPSXdjq5qxdd2vuDdJGKUpZ9dtReu84OQQ53KrtpXzY
369+
SXy25ZnLdt1xMg==
370+
-----END PRIVATE KEY-----`
371+
const invalidCert = "This is not a cert"
372+
const invalidKey = "This is not a key"
373+
374+
t.Run("all valid certs", func(t *testing.T) {
375+
query := url.Values{}
376+
query.Add(SslInlineURLParam, "true")
377+
query.Add(sslCertURLParam, validCert)
378+
query.Add(sslKeyURLParam, validKey)
379+
query.Add(sslRootCertURLParam, validCert)
380+
381+
remote := url.URL{
382+
Scheme: "postgresql",
383+
Host: "localhost:26257",
384+
RawQuery: query.Encode(),
385+
}
386+
_, err := setupPGXConfig(remote, options{})
387+
require.NoError(t, err)
388+
})
389+
390+
t.Run("invalid cert", func(t *testing.T) {
391+
query := url.Values{}
392+
query.Add(SslInlineURLParam, "true")
393+
query.Add(sslCertURLParam, invalidCert)
394+
query.Add(sslKeyURLParam, validKey)
395+
query.Add(sslRootCertURLParam, validCert)
396+
397+
remote := url.URL{
398+
Scheme: "postgresql",
399+
Host: "localhost:26257",
400+
RawQuery: query.Encode(),
401+
}
402+
_, err := setupPGXConfig(remote, options{})
403+
require.Error(t, err)
404+
})
405+
406+
t.Run("invalid key", func(t *testing.T) {
407+
query := url.Values{}
408+
query.Add(SslInlineURLParam, "true")
409+
query.Add(sslCertURLParam, validCert)
410+
query.Add(sslKeyURLParam, invalidKey)
411+
query.Add(sslRootCertURLParam, validCert)
412+
413+
remote := url.URL{
414+
Scheme: "postgresql",
415+
Host: "localhost:26257",
416+
RawQuery: query.Encode(),
417+
}
418+
_, err := setupPGXConfig(remote, options{})
419+
require.Error(t, err)
420+
})
421+
422+
t.Run("invalid root cert", func(t *testing.T) {
423+
query := url.Values{}
424+
query.Add(SslInlineURLParam, "true")
425+
query.Add(sslCertURLParam, validCert)
426+
query.Add(sslKeyURLParam, validKey)
427+
query.Add(sslRootCertURLParam, invalidCert)
428+
429+
remote := url.URL{
430+
Scheme: "postgresql",
431+
Host: "localhost:26257",
432+
RawQuery: query.Encode(),
433+
}
434+
_, err := setupPGXConfig(remote, options{})
435+
require.Error(t, err)
436+
})
437+
}

pkg/crosscluster/streamclient/partitioned_stream_client.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ type partitionedStreamClient struct {
4444
func NewPartitionedStreamClient(
4545
ctx context.Context, remote ClusterUri, opts ...Option,
4646
) (*partitionedStreamClient, error) {
47-
options := processOptions(opts)
48-
conn, config, err := newPGConnForClient(ctx, remote.URL(), options)
47+
streamOpts := processOptions(opts)
48+
conn, config, err := newPGConnForClient(ctx, remote.URL(), streamOpts)
4949
if err != nil {
5050
return nil, err
5151
}
5252
client := partitionedStreamClient{
5353
clusterUri: remote,
5454
pgxConfig: config,
55-
compressed: options.compressed,
56-
logical: options.logical,
55+
compressed: streamOpts.compressed,
56+
logical: streamOpts.logical,
5757
}
5858
client.mu.activeSubscriptions = make(map[*partitionedStreamSubscription]struct{})
5959
client.mu.srcConn = conn

pkg/crosscluster/streamclient/pgconn.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const (
3131
)
3232

3333
func newPGConnForClient(
34-
ctx context.Context, remote url.URL, options *options,
34+
ctx context.Context, remote url.URL, options options,
3535
) (*pgx.Conn, *pgx.ConnConfig, error) {
3636
config, err := setupPGXConfig(remote, options)
3737
if err != nil {
@@ -44,7 +44,7 @@ func newPGConnForClient(
4444
return conn, config, nil
4545
}
4646

47-
func setupPGXConfig(remote url.URL, options *options) (*pgx.ConnConfig, error) {
47+
func setupPGXConfig(remote url.URL, options options) (*pgx.ConnConfig, error) {
4848
noInlineCertURI, tlsInfo, err := uriWithInlineTLSCertsRemoved(remote)
4949
if err != nil {
5050
return nil, err
@@ -107,6 +107,9 @@ func uriWithInlineTLSCertsRemoved(remote url.URL) (url.URL, *tlsCerts, error) {
107107
// are deprecated in the stdlib. For now, I've skipped
108108
// it.
109109
block, _ := pem.Decode([]byte(key))
110+
if block == nil {
111+
return url.URL{}, nil, errors.New("unable to decode sslkey PEM data")
112+
}
110113
pemKey := pem.EncodeToMemory(block)
111114
keyPair, err := tls.X509KeyPair([]byte(cert), pemKey)
112115
if err != nil {

pkg/crosscluster/streamclient/span_config_stream_client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func NewSpanConfigStreamClient(
4343
) (SpanConfigClient, error) {
4444
remote := remoteUri.URL()
4545

46-
options := processOptions(opts)
47-
conn, config, err := newPGConnForClient(ctx, remote, options)
46+
streamOpts := processOptions(opts)
47+
conn, config, err := newPGConnForClient(ctx, remote, streamOpts)
4848
if err != nil {
4949
return nil, err
5050
}

0 commit comments

Comments
 (0)