-
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.
go-tlsdialer: initial repository setup
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
Showing
23 changed files
with
1,929 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,62 @@ | ||
package tlsdialer | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
"net" | ||
|
||
"github.com/tarantool/go-tarantool/v2" | ||
) | ||
|
||
type tntConn 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 tntConn satisfy the Conn interface. | ||
func (c *tntConn) Addr() net.Addr { | ||
return c.net.RemoteAddr() | ||
} | ||
|
||
// Read makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) Read(p []byte) (int, error) { | ||
return c.reader.Read(p) | ||
} | ||
|
||
// Write makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) Write(p []byte) (int, 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") | ||
} else { | ||
return l, nil | ||
} | ||
} | ||
|
||
// Flush makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) Flush() error { | ||
return c.writer.Flush() | ||
} | ||
|
||
// Close makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) Close() error { | ||
return c.net.Close() | ||
} | ||
|
||
// Greeting makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) Greeting() tarantool.Greeting { | ||
return tarantool.Greeting{} | ||
} | ||
|
||
// ProtocolInfo makes tntConn satisfy the Conn interface. | ||
func (c *tntConn) 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,143 @@ | ||
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 interface{} | ||
if sslCtx, err = sslCreateContext(sslOpts); 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(sslOpts opts) (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 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.