Skip to content

Commit

Permalink
bugfix: build with -tags go_tarantool_ssl_disable
Browse files Browse the repository at this point in the history
The patch fixes build with the build tag `go_tarantool_ssl_disable`:

1. It moves tests with OpenSslDialer to a test file that executes only
with the tag.
2. It defines structure `sslOpts` in the common place to use it in
the code with/without the flag.

Finally, it adds tests to CI with the build tag.

Closes #357
  • Loading branch information
oleg-jukovec committed Dec 4, 2023
1 parent cabe16e commit 36b05f6
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 326 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ jobs:
make test
make testrace
- name: Run regression tests with disabled SSL
run: |
make test TAGS="go_tarantool_ssl_disable"
make testrace TAGS="go_tarantool_ssl_disable"
- name: Run fuzzing tests
if: ${{ matrix.fuzzing }}
run: make fuzzing TAGS="go_tarantool_decimal_fuzzing"
Expand Down Expand Up @@ -193,6 +198,12 @@ jobs:
env:
TEST_TNT_SSL: ${{matrix.ssl}}

- name: Run regression tests with disabled SSL
run: |
source tarantool-enterprise/env.sh
make test TAGS="go_tarantool_ssl_disable"
make testrace TAGS="go_tarantool_ssl_disable"
- name: Run fuzzing tests
if: ${{ matrix.fuzzing }}
run: make fuzzing TAGS="go_tarantool_decimal_fuzzing"
Expand Down Expand Up @@ -359,6 +370,12 @@ jobs:
make test
make testrace
- name: Run regression tests with disabled SSL
run: |
cd "${SRCDIR}"
make test TAGS="go_tarantool_ssl_disable"
make testrace TAGS="go_tarantool_ssl_disable"
- name: Run fuzzing tests
if: ${{ matrix.fuzzing }}
run: |
Expand Down
181 changes: 0 additions & 181 deletions dial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
"github.com/tarantool/go-openssl"

"github.com/tarantool/go-tarantool/v2"
"github.com/tarantool/go-tarantool/v2/test_helpers"
Expand Down Expand Up @@ -605,186 +604,6 @@ func TestNetDialer_Dial_requirements(t *testing.T) {
require.Contains(t, err.Error(), "invalid server protocol")
}

func createSslListener(t *testing.T, opts tarantool.SslTestOpts) net.Listener {
ctx, err := tarantool.SslCreateContext(opts)
require.NoError(t, err)
l, err := openssl.Listen("tcp", "127.0.0.1:0", ctx.(*openssl.Ctx))
require.NoError(t, err)
return l
}

func TestOpenSslDialer_Dial_basic(t *testing.T) {
l := createSslListener(t, tarantool.SslTestOpts{
KeyFile: "testdata/localhost.key",
CertFile: "testdata/localhost.crt",
})

defer l.Close()
addr := l.Addr().String()

dialer := tarantool.OpenSslDialer{
Address: addr,
User: testDialUser,
Password: testDialPass,
}

cases := []testDialOpts{
{
name: "all is ok",
expectedProtocolInfo: idResponseTyped.Clone(),
},
{
name: "id request unsupported",
// Dialer sets auth.
expectedProtocolInfo: tarantool.ProtocolInfo{Auth: tarantool.ChapSha1Auth},
isIdUnsupported: true,
},
{
name: "greeting response error",
wantErr: true,
expectedErr: "failed to read greeting",
isErrGreeting: true,
},
{
name: "id response error",
wantErr: true,
expectedErr: "failed to identify",
isErrId: true,
},
{
name: "auth response error",
wantErr: true,
expectedErr: "failed to authenticate",
isErrAuth: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
testDialer(t, l, dialer, tc)
})
}
}

func TestOpenSslDialer_Dial_requirements(t *testing.T) {
l := createSslListener(t, tarantool.SslTestOpts{
KeyFile: "testdata/localhost.key",
CertFile: "testdata/localhost.crt",
})

defer l.Close()
addr := l.Addr().String()

dialer := tarantool.OpenSslDialer{
Address: addr,
User: testDialUser,
Password: testDialPass,
RequiredProtocolInfo: tarantool.ProtocolInfo{
Features: []iproto.Feature{42},
},
}

testDialAccept(testDialOpts{}, l)
ctx, cancel := test_helpers.GetConnectContext()
defer cancel()
conn, err := dialer.Dial(ctx, tarantool.DialOpts{})
if err == nil {
conn.Close()
}
require.Error(t, err)
require.Contains(t, err.Error(), "invalid server protocol")
}

func TestOpenSslDialer_Dial_papSha256Auth(t *testing.T) {
l := createSslListener(t, tarantool.SslTestOpts{
KeyFile: "testdata/localhost.key",
CertFile: "testdata/localhost.crt",
})

defer l.Close()
addr := l.Addr().String()

dialer := tarantool.OpenSslDialer{
Address: addr,
User: testDialUser,
Password: testDialPass,
Auth: tarantool.PapSha256Auth,
}

protocol := idResponseTyped.Clone()
protocol.Auth = tarantool.PapSha256Auth

testDialer(t, l, dialer, testDialOpts{
expectedProtocolInfo: protocol,
isPapSha256Auth: true,
})
}

func TestOpenSslDialer_Dial_opts(t *testing.T) {
for _, test := range sslTests {
t.Run(test.name, func(t *testing.T) {
l := createSslListener(t, test.serverOpts)
defer l.Close()
addr := l.Addr().String()

dialer := tarantool.OpenSslDialer{
Address: addr,
User: testDialUser,
Password: testDialPass,
SslKeyFile: test.clientOpts.KeyFile,
SslCertFile: test.clientOpts.CertFile,
SslCaFile: test.clientOpts.CaFile,
SslCiphers: test.clientOpts.Ciphers,
SslPassword: test.clientOpts.Password,
SslPasswordFile: test.clientOpts.PasswordFile,
}
testDialer(t, l, dialer, testDialOpts{
wantErr: !test.ok,
expectedProtocolInfo: idResponseTyped.Clone(),
})
})
}
}

func TestOpenSslDialer_Dial_ctx_cancel(t *testing.T) {
serverOpts := tarantool.SslTestOpts{
KeyFile: "testdata/localhost.key",
CertFile: "testdata/localhost.crt",
CaFile: "testdata/ca.crt",
Ciphers: "ECDHE-RSA-AES256-GCM-SHA384",
}
clientOpts := tarantool.SslTestOpts{
KeyFile: "testdata/localhost.key",
CertFile: "testdata/localhost.crt",
CaFile: "testdata/ca.crt",
Ciphers: "ECDHE-RSA-AES256-GCM-SHA384",
}

l := createSslListener(t, serverOpts)
defer l.Close()
addr := l.Addr().String()
testDialAccept(testDialOpts{}, l)

dialer := tarantool.OpenSslDialer{
Address: addr,
User: testDialUser,
Password: testDialPass,
SslKeyFile: clientOpts.KeyFile,
SslCertFile: clientOpts.CertFile,
SslCaFile: clientOpts.CaFile,
SslCiphers: clientOpts.Ciphers,
SslPassword: clientOpts.Password,
SslPasswordFile: clientOpts.PasswordFile,
}

ctx, cancel := context.WithCancel(context.Background())
cancel()
conn, err := dialer.Dial(ctx, tarantool.DialOpts{})
if err == nil {
conn.Close()
}
require.Error(t, err)
}

func TestFdDialer_Dial(t *testing.T) {
l, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
Expand Down
143 changes: 0 additions & 143 deletions ssl.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
//go:build !go_tarantool_ssl_disable
// +build !go_tarantool_ssl_disable

package tarantool

import (
"bufio"
"context"
"errors"
"io/ioutil"
"net"
"os"
"strings"

"github.com/tarantool/go-openssl"
)

type sslOpts struct {
// KeyFile is a path to a private SSL key file.
KeyFile string
Expand Down Expand Up @@ -43,131 +28,3 @@ type sslOpts struct {
// file as a password.
PasswordFile string
}

func sslDialContext(ctx context.Context, network, address string,
opts sslOpts) (connection net.Conn, err error) {
var sslCtx interface{}
if sslCtx, err = sslCreateContext(opts); err != nil {
return
}

return openssl.DialContext(ctx, network, address, sslCtx.(*openssl.Ctx), 0)
}

// interface{} is a hack. It helps to avoid dependency of go-openssl in build
// of tests with the tag 'go_tarantool_ssl_disable'.
func sslCreateContext(opts sslOpts) (ctx interface{}, err error) {
var sslCtx *openssl.Ctx

// Require TLSv1.2, because other protocol versions don't seem to
// support the GOST cipher.
if sslCtx, err = openssl.NewCtxWithVersion(openssl.TLSv1_2); err != nil {
return
}
ctx = sslCtx
sslCtx.SetMaxProtoVersion(openssl.TLS1_2_VERSION)
sslCtx.SetMinProtoVersion(openssl.TLS1_2_VERSION)

if opts.CertFile != "" {
if err = sslLoadCert(sslCtx, opts.CertFile); err != nil {
return
}
}

if opts.KeyFile != "" {
if err = sslLoadKey(sslCtx, opts.KeyFile, opts.Password, opts.PasswordFile); err != nil {
return
}
}

if opts.CaFile != "" {
if err = sslCtx.LoadVerifyLocations(opts.CaFile, ""); err != nil {
return
}
verifyFlags := openssl.VerifyPeer | openssl.VerifyFailIfNoPeerCert
sslCtx.SetVerify(verifyFlags, nil)
}

if opts.Ciphers != "" {
sslCtx.SetCipherList(opts.Ciphers)
}

return
}

func sslLoadCert(ctx *openssl.Ctx, certFile string) (err error) {
var certBytes []byte
if certBytes, err = ioutil.ReadFile(certFile); err != nil {
return
}

certs := openssl.SplitPEM(certBytes)
if len(certs) == 0 {
err = errors.New("No PEM certificate found in " + certFile)
return
}
first, certs := certs[0], certs[1:]

var cert *openssl.Certificate
if cert, err = openssl.LoadCertificateFromPEM(first); err != nil {
return
}
if err = ctx.UseCertificate(cert); err != nil {
return
}

for _, pem := range certs {
if cert, err = openssl.LoadCertificateFromPEM(pem); err != nil {
break
}
if err = ctx.AddChainCertificate(cert); err != nil {
break
}
}
return
}

func sslLoadKey(ctx *openssl.Ctx, keyFile string, password string,
passwordFile string) error {
var keyBytes []byte
var err, firstDecryptErr error

if keyBytes, err = ioutil.ReadFile(keyFile); err != nil {
return err
}

// If the key is encrypted and password is not provided,
// openssl.LoadPrivateKeyFromPEM(keyBytes) asks to enter PEM pass phrase
// interactively. On the other hand,
// openssl.LoadPrivateKeyFromPEMWithPassword(keyBytes, password) works fine
// for non-encrypted key with any password, including empty string. If
// the key is encrypted, we fast fail with password error instead of
// requesting the pass phrase interactively.
passwords := []string{password}
if passwordFile != "" {
file, err := os.Open(passwordFile)
if err == nil {
defer file.Close()

scanner := bufio.NewScanner(file)
// Tarantool itself tries each password file line.
for scanner.Scan() {
password = strings.TrimSpace(scanner.Text())
passwords = append(passwords, password)
}
} else {
firstDecryptErr = err
}
}

for _, password := range passwords {
key, err := openssl.LoadPrivateKeyFromPEMWithPassword(keyBytes, password)
if err == nil {
return ctx.UsePrivateKey(key)
} else if firstDecryptErr == nil {
firstDecryptErr = err
}
}

return firstDecryptErr
}
4 changes: 2 additions & 2 deletions ssl_disable.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
)

func sslDialContext(ctx context.Context, network, address string,
opts SslOpts) (connection net.Conn, err error) {
opts sslOpts) (connection net.Conn, err error) {
return nil, errors.New("SSL support is disabled.")
}

func sslCreateContext(opts SslOpts) (ctx interface{}, err error) {
func sslCreateContext(opts sslOpts) (ctx interface{}, err error) {
return nil, errors.New("SSL support is disabled.")
}
Loading

0 comments on commit 36b05f6

Please sign in to comment.