Skip to content

Commit e915bb8

Browse files
WIP
1 parent b30e2b6 commit e915bb8

12 files changed

+367
-67
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,15 @@
44
work_dir*
55
.rocks
66
bench*
7+
8+
testdata/*.crt
9+
!testdata/ca.crt
10+
!testdata/invalidhost.crt
11+
!testdata/localhost.crt
12+
testdata/*.csr
13+
testdata/*.ext
14+
testdata/*.key
15+
!testdata/localhost.key
16+
!testdata/localhost.enc.key
17+
testdata/*.pem
18+
testdata/*.srl

connection.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ type SslOpts struct {
345345
//
346346
// * https://www.openssl.org/docs/man1.1.1/man1/ciphers.html
347347
Ciphers string
348+
// Password is a password for decrypting private SSL key file.
349+
Password string
350+
// PasswordFile is a path to the list of passwords for decrypting private SSL
351+
// key file. Connection tries every line from the file as a password.
352+
PasswordFile string
348353
}
349354

350355
// Clone returns a copy of the Opts object.

ssl.go

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
package tarantool
55

66
import (
7+
"bufio"
78
"errors"
9+
"fmt"
810
"io/ioutil"
911
"net"
12+
"os"
13+
"strings"
1014
"time"
1115

1216
"github.com/tarantool/go-openssl"
@@ -43,7 +47,7 @@ func sslCreateContext(opts SslOpts) (ctx interface{}, err error) {
4347
}
4448

4549
if opts.KeyFile != "" {
46-
if err = sslLoadKey(sslCtx, opts.KeyFile); err != nil {
50+
if err = sslLoadKey(sslCtx, opts.KeyFile, opts.Password, opts.PasswordFile); err != nil {
4751
return
4852
}
4953
}
@@ -95,16 +99,61 @@ func sslLoadCert(ctx *openssl.Ctx, certFile string) (err error) {
9599
return
96100
}
97101

98-
func sslLoadKey(ctx *openssl.Ctx, keyFile string) (err error) {
102+
func sslLoadKey(ctx *openssl.Ctx, keyFile string, password string,
103+
passwordFile string) error {
99104
var keyBytes []byte
105+
var err error
106+
100107
if keyBytes, err = ioutil.ReadFile(keyFile); err != nil {
101-
return
108+
return err
102109
}
103110

104111
var key openssl.PrivateKey
105-
if key, err = openssl.LoadPrivateKeyFromPEM(keyBytes); err != nil {
106-
return
112+
var errs []error
113+
114+
// If key is encrypted and password is not provided,
115+
// openssl.LoadPrivateKeyFromPEM(keyBytes) asks to enter PEM pass phrase
116+
// interactively. On the other hand,
117+
// openssl.LoadPrivateKeyFromPEMWithPassword(keyBytes, '') works fine for
118+
// non-encrypted key.
119+
if key, err = openssl.LoadPrivateKeyFromPEMWithPassword(keyBytes, password); err == nil {
120+
return ctx.UsePrivateKey(key)
121+
} else {
122+
errs = append(errs, err)
123+
}
124+
125+
if passwordFile != "" {
126+
var file *os.File
127+
file, err = os.Open(passwordFile)
128+
if err == nil {
129+
defer file.Close()
130+
131+
scanner := bufio.NewScanner(file)
132+
for scanner.Scan() {
133+
password = strings.TrimSpace(scanner.Text())
134+
135+
if key, err = openssl.LoadPrivateKeyFromPEMWithPassword(keyBytes, password); err == nil {
136+
return ctx.UsePrivateKey(key)
137+
} else {
138+
errs = append(errs, err)
139+
}
140+
}
141+
} else {
142+
errs = append(errs, err)
143+
}
144+
}
145+
146+
if len(errs) > 1 {
147+
// Convenient multiple error wrapping was introduced only in Go 1.20
148+
// https://pkg.go.dev/errors#example-Join
149+
// https://github.com/golang/go/issues/53435
150+
rerr := errors.New("Got multiple errors on SSL decryption:")
151+
for _, err = range errs {
152+
rerr = fmt.Errorf("%s, %w", rerr, err)
153+
}
154+
155+
return rerr
107156
}
108157

109-
return ctx.UsePrivateKey(key)
158+
return errs[0]
110159
}

ssl_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,26 @@ func serverTntStop(inst test_helpers.TarantoolInstance) {
139139
test_helpers.StopTarantoolWithCleanup(inst)
140140
}
141141

142+
func skipEncrypted(t testing.TB, serverOpts SslOpts) {
143+
if serverOpts.Password == "" && serverOpts.PasswordFile == "" {
144+
return
145+
}
146+
147+
isLess, err := test_helpers.IsTarantoolVersionLess(2, 11, 0)
148+
if err != nil {
149+
t.Fatalf("Could not check the Tarantool version")
150+
}
151+
152+
if isLess {
153+
t.Skipf("Skipping test for Tarantool without SSL encryption support")
154+
}
155+
}
156+
142157
func assertConnectionSslFail(t testing.TB, serverOpts, clientOpts SslOpts) {
143158
t.Helper()
144159

160+
skipEncrypted(t, serverOpts)
161+
145162
l, c, _, _, err := createClientServerSsl(t, serverOpts, clientOpts)
146163
l.Close()
147164
if err == nil {
@@ -153,6 +170,8 @@ func assertConnectionSslFail(t testing.TB, serverOpts, clientOpts SslOpts) {
153170
func assertConnectionSslOk(t testing.TB, serverOpts, clientOpts SslOpts) {
154171
t.Helper()
155172

173+
skipEncrypted(t, serverOpts)
174+
156175
l, c, msgs, errs := createClientServerSslOk(t, serverOpts, clientOpts)
157176
const message = "any test string"
158177
c.Write([]byte(message))
@@ -431,6 +450,142 @@ var tests = []test{
431450
Ciphers: "TLS_AES_128_GCM_SHA256",
432451
},
433452
},
453+
{
454+
"pass_no_key_encrypt",
455+
true,
456+
SslOpts{
457+
KeyFile: "testdata/localhost.key",
458+
CertFile: "testdata/localhost.crt",
459+
CaFile: "testdata/ca.crt",
460+
},
461+
SslOpts{
462+
KeyFile: "testdata/localhost.enc.key",
463+
CertFile: "testdata/localhost.crt",
464+
Password: "mysslpassword",
465+
},
466+
},
467+
{
468+
"pass_file_no_key_encrypt",
469+
true,
470+
SslOpts{
471+
KeyFile: "testdata/localhost.key",
472+
CertFile: "testdata/localhost.crt",
473+
CaFile: "testdata/ca.crt",
474+
},
475+
SslOpts{
476+
KeyFile: "testdata/localhost.enc.key",
477+
CertFile: "testdata/localhost.crt",
478+
PasswordFile: "testdata/passwords",
479+
},
480+
},
481+
{
482+
"pass_key_encrypt",
483+
true,
484+
SslOpts{
485+
KeyFile: "testdata/localhost.enc.key",
486+
CertFile: "testdata/localhost.crt",
487+
CaFile: "testdata/ca.crt",
488+
Password: "mysslpassword",
489+
},
490+
SslOpts{
491+
KeyFile: "testdata/localhost.enc.key",
492+
CertFile: "testdata/localhost.crt",
493+
Password: "mysslpassword",
494+
},
495+
},
496+
{
497+
"pass_file_key_encrypt",
498+
true,
499+
SslOpts{
500+
KeyFile: "testdata/localhost.enc.key",
501+
CertFile: "testdata/localhost.crt",
502+
CaFile: "testdata/ca.crt",
503+
PasswordFile: "testdata/passwords",
504+
},
505+
SslOpts{
506+
KeyFile: "testdata/localhost.enc.key",
507+
CertFile: "testdata/localhost.crt",
508+
PasswordFile: "testdata/passwords",
509+
},
510+
},
511+
{
512+
"pass_and_pass_file_key_encrypt",
513+
true,
514+
SslOpts{
515+
KeyFile: "testdata/localhost.enc.key",
516+
CertFile: "testdata/localhost.crt",
517+
CaFile: "testdata/ca.crt",
518+
PasswordFile: "testdata/passwords",
519+
},
520+
SslOpts{
521+
KeyFile: "testdata/localhost.enc.key",
522+
CertFile: "testdata/localhost.crt",
523+
Password: "mysslpassword",
524+
PasswordFile: "testdata/passwords",
525+
},
526+
},
527+
{
528+
"inv_pass_and_pass_file_key_encrypt",
529+
true,
530+
SslOpts{
531+
KeyFile: "testdata/localhost.enc.key",
532+
CertFile: "testdata/localhost.crt",
533+
CaFile: "testdata/ca.crt",
534+
PasswordFile: "testdata/passwords",
535+
},
536+
SslOpts{
537+
KeyFile: "testdata/localhost.enc.key",
538+
CertFile: "testdata/localhost.crt",
539+
Password: "invalidpassword",
540+
PasswordFile: "testdata/passwords",
541+
},
542+
},
543+
{
544+
"pass_and_inv_pass_file_key_encrypt",
545+
true,
546+
SslOpts{
547+
KeyFile: "testdata/localhost.enc.key",
548+
CertFile: "testdata/localhost.crt",
549+
CaFile: "testdata/ca.crt",
550+
PasswordFile: "testdata/passwords",
551+
},
552+
SslOpts{
553+
KeyFile: "testdata/localhost.enc.key",
554+
CertFile: "testdata/localhost.crt",
555+
Password: "mysslpassword",
556+
PasswordFile: "testdata/invalidpasswords",
557+
},
558+
},
559+
{
560+
"pass_and_inv_pass_file_key_encrypt",
561+
false,
562+
SslOpts{
563+
KeyFile: "testdata/localhost.enc.key",
564+
CertFile: "testdata/localhost.crt",
565+
CaFile: "testdata/ca.crt",
566+
PasswordFile: "testdata/passwords",
567+
},
568+
SslOpts{
569+
KeyFile: "testdata/localhost.enc.key",
570+
CertFile: "testdata/localhost.crt",
571+
Password: "invalidpassword",
572+
PasswordFile: "testdata/invalidpasswords",
573+
},
574+
},
575+
{
576+
"no_pass_key_encrypt",
577+
false,
578+
SslOpts{
579+
KeyFile: "testdata/localhost.enc.key",
580+
CertFile: "testdata/localhost.crt",
581+
CaFile: "testdata/ca.crt",
582+
PasswordFile: "testdata/passwords",
583+
},
584+
SslOpts{
585+
KeyFile: "testdata/localhost.enc.key",
586+
CertFile: "testdata/localhost.crt",
587+
},
588+
},
434589
}
435590

436591
func isTestTntSsl() bool {

testdata/ca.crt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIDLzCCAhegAwIBAgIUMMZTmNkhr4qOfSwInVk2dAJvoBEwDQYJKoZIhvcNAQEL
2+
MIIDLzCCAhegAwIBAgIUaw6WTgYBXFRqlGbKRYczFApoNU4wDQYJKoZIhvcNAQEL
33
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
4-
MjA1MjYwNjE3NDBaFw00NDEwMjkwNjE3NDBaMCcxCzAJBgNVBAYTAlVTMRgwFgYD
4+
MzA3MjYwODM3MTZaFw00NTEyMjkwODM3MTZaMCcxCzAJBgNVBAYTAlVTMRgwFgYD
55
VQQDDA9FeGFtcGxlLVJvb3QtQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
6-
AoIBAQCRq/eaA3I6CB8t770H2XDdzcp1yuC/+TZOxV5o0LuRkogTvL2kYULBrfx1
7-
rVZu8zQJTx1fmSRj1cN8j+IrmXN5goZ3mYFTnnIOgkyi+hJysVlo5s0Kp0qtLLGM
8-
OuaVbxw2oAy75if5X3pFpiDaMvFBtJKsh8+SkncBIC5bbKC5AoLdFANLmPiH0CGr
9-
Mv3rL3ycnbciI6J4uKHcWnYGGiMjBomaZ7jd/cOjcjmGfpI5d0nq13G11omkyEyR
10-
wNX0eJRL02W+93Xu7tD+FEFMxFvak+70GvX+XWomwYw/Pjlio8KbTAlJxhfK2Lh6
11-
H798k17VfxIrOk0KjzZS7+a20hZ/AgMBAAGjUzBRMB0GA1UdDgQWBBT2f5o8r75C
12-
PWST36akpkKRRTbhvjAfBgNVHSMEGDAWgBT2f5o8r75CPWST36akpkKRRTbhvjAP
13-
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA9pb75p6mnqp2MQHSr
14-
5SKRf2UV4wQIUtXgF6V9vNfvVzJii+Lzrqir1YMk5QgavCzD96KlJcqJCcH559RY
15-
5743AxI3tdWfA3wajBctoy35oYnT4M30qbkryYLTUlv7PmeNWvrksTURchyyDt5/
16-
3T73yj5ZalmzKN6+xLfUDdnudspfWlUMutKU50MU1iuQESf4Fwd53vOg9jMcWJ2E
17-
vAgfVI0XAvYdU3ybJrUvBq5zokYR2RzGv14uHxwVPnLBjrBEHRnbrXvLZJhuIS2b
18-
xZ3CqwWi+9bvNqHz09HvhkU2b6fCGweKaAUGSo8OfQ5FRkjTUomMI/ZLs/qtJ6JR
19-
zzVt
6+
AoIBAQCunm5E+dyoYw+ECp0vOabsA4L7C+dUQLhfqdOEwFSpSanjBTuUEAPB+fEr
7+
wqaZXI2EnUSxYEYO03TkZmWoJgRJq+00laWPA4AuKHpg4SS/LUoveQiQdsie+kUj
8+
YMFu3rtP2CvTMpC4HMRK2CviOnU9iA4hPvRx4o5tESxLW31jNnBDeC/tsEVVR/6i
9+
lwB9Oh1RbZI/c429N67qq5C2rpU5+o+YszDou36WTxw6XeXdkw7QF4W2BNLysPLJ
10+
AY+aPrUVxKDOgDNk77h41HDqu+SuDg6mg528yfRqyAd4ooEE8MLcT0xztn1U8HvZ
11+
SKwWTnS8TSzCmQptRGPlb5oES/NlAgMBAAGjUzBRMB0GA1UdDgQWBBQ/S0H0dFUy
12+
OuEQ/kgDzGarWm2vlDAfBgNVHSMEGDAWgBQ/S0H0dFUyOuEQ/kgDzGarWm2vlDAP
13+
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCoP+kH96+b6GWByPTo
14+
LRK/QJmpPRWiZ5naV6CXJQNYg+nVG9wdiGXxEx4BBZs6yGdeHdiWCWbuRPMyH0Wp
15+
w2ajMmK7pC8+MGKzMSC/EISPFwKYumwe/6zNnde7eZ19n7EFwrOihgEf+hNfjOCj
16+
CqDIfMb2ztEHY7mEABMXDviKI80om2P1oIkHj5MD7z8ZetJRf1qCH7ke2cdTJ+Zr
17+
XNGiJ7sz3xRQO/QRCkbBbr/d4zeX3A5/+MXHLtzbPiWs+/XaDbGJTQIO5hFfwwAC
18+
v1/VrNmsy+3YYLLTZzmfpa60Sk3NsZbIQdvwLJJj8pOmH/zae7UZlrxlGWjQKvH5
19+
evsP
2020
-----END CERTIFICATE-----

testdata/generate.sh

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ set -xeuo pipefail
88
# $ openssl version
99
# OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
1010

11-
cat <<EOF > domains.ext
11+
cat <<EOF > domains_localhost.ext
1212
authorityKeyIdentifier=keyid,issuer
1313
basicConstraints=CA:FALSE
1414
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
@@ -18,8 +18,32 @@ DNS.1 = localhost
1818
IP.1 = 127.0.0.1
1919
EOF
2020

21+
cat <<EOF > domains_invalidhost.ext
22+
authorityKeyIdentifier=keyid,issuer
23+
basicConstraints=CA:FALSE
24+
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
25+
subjectAltName = @alt_names
26+
[alt_names]
27+
DNS.1 = invalidhostname
28+
EOF
29+
2130
openssl req -x509 -nodes -new -sha256 -days 8192 -newkey rsa:2048 -keyout ca.key -out ca.pem -subj "/C=US/CN=Example-Root-CA"
2231
openssl x509 -outform pem -in ca.pem -out ca.crt
2332

2433
openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost"
25-
openssl x509 -req -sha256 -days 8192 -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -extfile domains.ext -out localhost.crt
34+
openssl x509 -req -sha256 -days 8192 -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -extfile domains_localhost.ext -out localhost.crt
35+
openssl x509 -req -sha256 -days 8192 -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -extfile domains_invalidhost.ext -out invalidhost.crt
36+
37+
password=mysslpassword
38+
39+
# Tarantool tries every line from the password file.
40+
cat <<EOF > passwords
41+
unusedpassword
42+
$password
43+
EOF
44+
45+
cat <<EOF > invalidpasswords
46+
unusedpassword1
47+
EOF
48+
49+
openssl rsa -aes256 -passout "pass:${password}" -in localhost.key -out localhost.enc.key

testdata/invalidhost.crt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDkjCCAnqgAwIBAgIUQLHZRFM5sRy6sRCuHaevk+YGFpEwDQYJKoZIhvcNAQEL
3+
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
4+
MzA3MjYwODM3MTZaFw00NTEyMjkwODM3MTZaMGcxCzAJBgNVBAYTAlVTMRIwEAYD
5+
VQQIDAlZb3VyU3RhdGUxETAPBgNVBAcMCFlvdXJDaXR5MR0wGwYDVQQKDBRFeGFt
6+
cGxlLUNlcnRpZmljYXRlczESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG
7+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqG67dtzioYKHDkF4FN2ObVpSNHkX5JgN8cR6
8+
iOyoAlppZO3upwRePQX0Ra9SMoL4GNuHZrr4daABYW15H9utlT2E8kzZTIcL+7VQ
9+
rhGGrqzQRIXXOb6vGyKVnAC1FJ2mrnB7BfOCDcdMG1FeDcbhD3gyVzmr1wmXSvLh
10+
Zq5XmHc2uinfGgt5Q1HauEso1aQqJ0VjkK0Xh+4O2tP9E1tnzJoetUQt2f2NmigY
11+
hFURUsK+H2w9whWD9ynLkzD7xtNaPZ3o2WI4i/Eu9YHgAgnnSOkuKB0VjmirFWCz
12+
YnrYWdGEdhYC62Wx58vX5vOOeDsZNJyKagh+0/JOLZCKPKbh6QIDAQABo3YwdDAf
13+
BgNVHSMEGDAWgBQ/S0H0dFUyOuEQ/kgDzGarWm2vlDAJBgNVHRMEAjAAMAsGA1Ud
14+
DwQEAwIE8DAaBgNVHREEEzARgg9pbnZhbGlkaG9zdG5hbWUwHQYDVR0OBBYEFEMm
15+
fZC+zfSDniXFevQwlLQ8o1O1MA0GCSqGSIb3DQEBCwUAA4IBAQB/gE4CZdIma4sr
16+
+HHyosi13KuLewUydfwWlPQoBD4s+lbOelhylKj7u+AL/QOiPvYIcreFv7rwXXzj
17+
FTyT79G5pTNhlyMai+ehWRNxPIjksnZnDfa8dDciC3+w8wzWaJOXp/FEH0qfOs87
18+
F7smnKvOTVFcbshWq9u/bKTlA2dpuVKJub+ISfvbqLSxoaz5/NeBUFBIe8SllpWd
19+
EW+GKbROa2spChpECEEbYOhWgTZ0dVdrnWd0xq34gSnzA6H+gTQ6wSCSMHl7YKJs
20+
yCnPJz++Fl5hmwYGFA6HP60r0GZ4vAHUdfirehwJt/y0PRylFh0cXxDIY84UqRJa
21+
80SDNfZQ
22+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)