-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
api: add
OpenSSLDialer
implementation
To disable SSL by default we want to transfer OpenSSLDialer and any other SSL logic to the new go-tlsdialer repository. go-tlsdialer serves as an interlayer between go-tarantool and go-openssl. All SSL logic from go-tarantool is moved to the go-tlsdialer. go-tlsdialer still uses tarantool connection, but also types and methods from go-openssl. This way we are removing the direct go-openssl dependency from go-tarantool, without creating a tarantool dependency in go-openssl. Moved all SSL code from go-tarantool, some test helpers. Part of tarantool/go-tarantool#301
- Loading branch information
1 parent
01f24e2
commit e2caece
Showing
22 changed files
with
2,013 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package tlsdialer | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
"net" | ||
|
||
"github.com/tarantool/go-tarantool/v2" | ||
) | ||
|
||
type ttConn struct { | ||
net net.Conn | ||
reader io.Reader | ||
writer writeFlusher | ||
} | ||
|
||
// writeFlusher is the interface that groups the basic Write and Flush methods. | ||
type writeFlusher interface { | ||
io.Writer | ||
Flush() error | ||
} | ||
|
||
// Addr makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Addr() net.Addr { | ||
return c.net.RemoteAddr() | ||
} | ||
|
||
// Read makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Read(p []byte) (int, error) { | ||
return c.reader.Read(p) | ||
} | ||
|
||
// Write makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Write(p []byte) (int, error) { | ||
var ( | ||
l int | ||
err error | ||
) | ||
|
||
if l, err = c.writer.Write(p); err != nil { | ||
return l, err | ||
} else if l != len(p) { | ||
return l, errors.New("wrong length written") | ||
} | ||
return l, nil | ||
} | ||
|
||
// Flush makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Flush() error { | ||
return c.writer.Flush() | ||
} | ||
|
||
// Close makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Close() error { | ||
return c.net.Close() | ||
} | ||
|
||
// Greeting makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) Greeting() tarantool.Greeting { | ||
return tarantool.Greeting{} | ||
} | ||
|
||
// ProtocolInfo makes ttConn satisfy the Conn interface. | ||
func (c *ttConn) ProtocolInfo() tarantool.ProtocolInfo { | ||
return tarantool.ProtocolInfo{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package tlsdialer | ||
|
||
import ( | ||
"net" | ||
"time" | ||
) | ||
|
||
type deadlineIO struct { | ||
to time.Duration | ||
c net.Conn | ||
} | ||
|
||
func (d *deadlineIO) Write(b []byte) (n int, err error) { | ||
if d.to > 0 { | ||
if err := d.c.SetWriteDeadline(time.Now().Add(d.to)); err != nil { | ||
return 0, err | ||
} | ||
} | ||
n, err = d.c.Write(b) | ||
return | ||
} | ||
|
||
func (d *deadlineIO) Read(b []byte) (n int, err error) { | ||
if d.to > 0 { | ||
if err := d.c.SetReadDeadline(time.Now().Add(d.to)); err != nil { | ||
return 0, err | ||
} | ||
} | ||
n, err = d.c.Read(b) | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package tlsdialer | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"errors" | ||
"net" | ||
"os" | ||
"strings" | ||
|
||
"github.com/tarantool/go-openssl" | ||
) | ||
|
||
func sslDialContext(ctx context.Context, network, address string, | ||
sslOpts opts) (connection net.Conn, err error) { | ||
var sslCtx *openssl.Ctx | ||
if sslCtx, err = sslCreateContext(sslOpts); err != nil { | ||
return | ||
} | ||
|
||
return openssl.DialContext(ctx, network, address, sslCtx, 0) | ||
} | ||
|
||
func sslCreateContext(sslOpts opts) (sslCtx *openssl.Ctx, err error) { | ||
// 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 | ||
} | ||
sslCtx.SetMaxProtoVersion(openssl.TLS1_2_VERSION) | ||
sslCtx.SetMinProtoVersion(openssl.TLS1_2_VERSION) | ||
|
||
if sslOpts.CertFile != "" { | ||
if err = sslLoadCert(sslCtx, sslOpts.CertFile); err != nil { | ||
return | ||
} | ||
} | ||
|
||
if sslOpts.KeyFile != "" { | ||
if err = sslLoadKey(sslCtx, sslOpts.KeyFile, sslOpts.Password, | ||
sslOpts.PasswordFile); err != nil { | ||
return | ||
} | ||
} | ||
|
||
if sslOpts.CaFile != "" { | ||
if err = sslCtx.LoadVerifyLocations(sslOpts.CaFile, ""); err != nil { | ||
return | ||
} | ||
verifyFlags := openssl.VerifyPeer | openssl.VerifyFailIfNoPeerCert | ||
sslCtx.SetVerify(verifyFlags, nil) | ||
} | ||
|
||
if sslOpts.Ciphers != "" { | ||
if err = sslCtx.SetCipherList(sslOpts.Ciphers); err != nil { | ||
return | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
func sslLoadCert(ctx *openssl.Ctx, certFile string) (err error) { | ||
var certBytes []byte | ||
if certBytes, err = os.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 = os.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 | ||
} |
Oops, something went wrong.