Skip to content

Commit ba5515a

Browse files
authored
Allow tls.Config to be passed as a session option (#53)
* Allow tls.Config to be passed as a session option
1 parent 3326b2f commit ba5515a

File tree

4 files changed

+120
-5
lines changed

4 files changed

+120
-5
lines changed

.github/workflows/build.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ jobs:
114114
COHERENCE_TLS_CLIENT_KEY_OPTION=`pwd`/test/utils/certs/star-lord.key \
115115
COHERENCE_VERSION=22.06.5 PROFILES=,secure,-jakarta,javax,scope make clean certs generate-proto build-test-images test-e2e-standalone-scope
116116
117+
- name: E2E Local Tests SSL (tlsConfig)
118+
shell: bash
119+
run: |
120+
SECURE=tlsConfig COHERENCE_IGNORE_INVALID_CERTS_OPTION=true \
121+
COHERENCE_TLS_CERTS_PATH_OPTION=`pwd`/test/utils/certs/guardians-ca.crt \
122+
COHERENCE_TLS_CLIENT_CERT_OPTION=`pwd`/test/utils/certs/star-lord.crt \
123+
COHERENCE_TLS_CLIENT_KEY_OPTION=`pwd`/test/utils/certs/star-lord.key \
124+
COHERENCE_VERSION=22.06.5 PROFILES=,secure,-jakarta,javax,scope make clean certs generate-proto build-test-images test-e2e-standalone-scope
125+
117126
- uses: actions/upload-artifact@v3
118127
if: failure()
119128
with:

coherence/session.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
defaultRequestTimeout = "30000" // millis
3636
defaultDisconnectTimeout = "30000" // millis
3737
defaultReadyTimeout = "0" // millis
38+
insecureWarning = "WARNING: you have turned off SSL certificate validation. This is insecure and not recommended."
3839
)
3940

4041
// Session provides APIs to create NamedCaches. The [NewSession] method creates a
@@ -69,6 +70,7 @@ type SessionOptions struct {
6970
RequestTimeout time.Duration
7071
DisconnectTimeout time.Duration
7172
ReadyTimeout time.Duration
73+
TlSConfig *tls.Config
7274
}
7375

7476
// NewSession creates a new Session with the specified sessionOptions.
@@ -96,7 +98,14 @@ type SessionOptions struct {
9698
//
9799
// To Configure SSL, you must first enable SSL on the gRPC Proxy, see [gRPC Proxy Server] for details.
98100
//
99-
// You can use the following to set the required TLS options when creating a session:
101+
// There are a number of ways to set the TLS options when creating a session.
102+
// You can use [WithTLSConfig] to specify a custom tls.Config or specify the client certificate, key and trust
103+
// certificate using additional session options or using environment variables. See below for more details.
104+
//
105+
// myTlSConfig = &tls.Config{....}
106+
// session, err := coherence.NewSession(ctx, coherence.WithTLSConfig(myTLSConfig))
107+
//
108+
// You can also use the following to set the required TLS options when creating a session:
100109
//
101110
// session, err := coherence.NewSession(ctx, coherence.WithTLSClientCert("/path/to/client/certificate"),
102111
// coherence.WithTLSClientKey("/path/path/to/client/key"),
@@ -278,6 +287,14 @@ func WithReadyTimeout(timeout time.Duration) func(sessionOptions *SessionOptions
278287
}
279288
}
280289

290+
// WithTLSConfig returns a function to set the tls.Config directly. This is typically used
291+
// when you require fine-grained control over these options.
292+
func WithTLSConfig(tlsConfig *tls.Config) func(sessionOptions *SessionOptions) {
293+
return func(s *SessionOptions) {
294+
s.TlSConfig = tlsConfig
295+
}
296+
}
297+
281298
// ID returns the identifier of a session.
282299
func (s *Session) ID() string {
283300
return s.sessionID.String()
@@ -596,6 +613,14 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
596613
return grpc.WithTransportCredentials(insecure.NewCredentials()), nil
597614
}
598615

616+
// check if a tls.Config has been set and use this, otherwise continue to check for env and other options
617+
if s.TlSConfig != nil {
618+
if s.TlSConfig.InsecureSkipVerify {
619+
log.Println(insecureWarning)
620+
}
621+
return grpc.WithTransportCredentials(credentials.NewTLS(s.TlSConfig)), nil
622+
}
623+
599624
var (
600625
err error
601626
cp *x509.CertPool
@@ -612,7 +637,7 @@ func (s *SessionOptions) createTLSOption() (grpc.DialOption, error) {
612637

613638
ignoreInvalidCerts := ignoreInvalidCertsEnv == "true"
614639
if ignoreInvalidCerts {
615-
log.Println("WARNING: you have turned off SSL certificate validation. This is insecure and not recommended.")
640+
log.Println(insecureWarning)
616641
}
617642
s.IgnoreInvalidCerts = ignoreInvalidCerts
618643

@@ -695,8 +720,12 @@ func (s *SessionOptions) String() string {
695720
s.Address, s.PlainText, s.Scope, s.Format, s.RequestTimeout, s.DisconnectTimeout, s.ReadyTimeout))
696721

697722
if !s.PlainText {
698-
sb.WriteString(fmt.Sprintf(" clientCertPath=%v, clientKeyPath=%v, caCertPath=%v, igoreInvalidCerts=%v",
699-
s.ClientCertPath, s.ClientKeyPath, s.CaCertPath, s.IgnoreInvalidCerts))
723+
if s.TlSConfig == nil {
724+
sb.WriteString(fmt.Sprintf(" clientCertPath=%v, clientKeyPath=%v, caCertPath=%v, igoreInvalidCerts=%v",
725+
s.ClientCertPath, s.ClientKeyPath, s.CaCertPath, s.IgnoreInvalidCerts))
726+
} else {
727+
sb.WriteString("tls.Config specified")
728+
}
700729
}
701730

702731
return sb.String()

scripts/run-compat-ce.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ SECURE=options COHERENCE_IGNORE_INVALID_CERTS_OPTION=true \
3333
COHERENCE_TLS_CLIENT_KEY_OPTION=`pwd`/test/utils/certs/star-lord.key \
3434
COHERENCE_VERSION=22.06.5 PROFILES=,secure make clean certs generate-proto build-test-images test-e2e-standalone
3535

36+
echo "Coherence CE 22.06.5 with SSL using tlsConfig"
37+
# suite_test.go takes the below env vars and then creates a tls.Config and passes to the WithTLSConfig
38+
SECURE=tlsConfig COHERENCE_IGNORE_INVALID_CERTS_OPTION=true \
39+
COHERENCE_TLS_CERTS_PATH_OPTION=`pwd`/test/utils/certs/guardians-ca.crt \
40+
COHERENCE_TLS_CLIENT_CERT_OPTION=`pwd`/test/utils/certs/star-lord.crt \
41+
COHERENCE_TLS_CLIENT_KEY_OPTION=`pwd`/test/utils/certs/star-lord.key \
42+
COHERENCE_VERSION=22.06.5 PROFILES=,secure make clean certs generate-proto build-test-images test-e2e-standalone
43+
3644
echo "Coherence CE 23.03.1"
3745
COHERENCE_BASE_IMAGE=gcr.io/distroless/java17 PROFILES=,jakarta,-javax COHERENCE_VERSION=23.03.1 make clean generate-proto build-test-images test-e2e-standalone
3846

test/utils/utils.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bytes"
1111
"context"
1212
"crypto/tls"
13+
"crypto/x509"
1314
"errors"
1415
"fmt"
1516
"github.com/onsi/gomega"
@@ -50,7 +51,7 @@ var (
5051
)
5152

5253
const (
53-
// the following env options are used to set the SSL mode via coherence.With* options rather
54+
// the following env options are used to set the SSL mode via coherence.With* options or tlsCOnfig rather
5455
// than environment variables
5556
envTLSCertPath = "COHERENCE_TLS_CERTS_PATH_OPTION"
5657
envTLSClientCert = "COHERENCE_TLS_CLIENT_CERT_OPTION"
@@ -361,12 +362,80 @@ func GetSession(options ...func(session *coherence.SessionOptions)) (*coherence.
361362
sessionOptions = append(sessionOptions, coherence.WithIgnoreInvalidCerts())
362363
}
363364
log.Println(sessionOptions)
365+
} else if testContext.SecureMode == "tlsConfig" {
366+
// read the environment variables as above and create the TLS options
367+
config, err := createTLSOptions(os.Getenv(envTLSClientCert), os.Getenv(envTLSClientKey), os.Getenv(envTLSCertPath),
368+
os.Getenv(envIgnoreInvalidCerts) == "true")
369+
if err != nil {
370+
return nil, err
371+
}
372+
sessionOptions = append(sessionOptions, coherence.WithTLSConfig(config))
364373
}
365374
}
366375

367376
return coherence.NewSession(ctx, sessionOptions...)
368377
}
369378

379+
// createTLSOptions creates tls.Config for testing.
380+
func createTLSOptions(clientCertPath, clientKeyPath, certsPath string, ignoreInvalidCerts bool) (*tls.Config, error) {
381+
var (
382+
cp *x509.CertPool
383+
certData []byte
384+
certificates = make([]tls.Certificate, 0)
385+
err error
386+
)
387+
log.Println("creating tls.Config")
388+
if certsPath != "" {
389+
cp = x509.NewCertPool()
390+
391+
if err = validateFilePath(certsPath); err != nil {
392+
return nil, err
393+
}
394+
395+
certData, err = os.ReadFile(certsPath)
396+
if err != nil {
397+
return nil, err
398+
}
399+
400+
if !cp.AppendCertsFromPEM(certData) {
401+
return nil, errors.New("credentials: failed to append certificates")
402+
}
403+
}
404+
405+
if clientCertPath != "" && clientKeyPath != "" {
406+
log.Println("loading client certificate and key, cert=", clientCertPath, "key=", clientKeyPath)
407+
if err = validateFilePath(clientCertPath); err != nil {
408+
return nil, err
409+
}
410+
if err = validateFilePath(clientKeyPath); err != nil {
411+
return nil, err
412+
}
413+
var clientCert tls.Certificate
414+
clientCert, err = tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
415+
if err != nil {
416+
return nil, err
417+
}
418+
certificates = []tls.Certificate{clientCert}
419+
}
420+
421+
config := &tls.Config{
422+
InsecureSkipVerify: ignoreInvalidCerts, //nolint
423+
RootCAs: cp,
424+
Certificates: certificates,
425+
}
426+
427+
return config, nil
428+
}
429+
430+
// validateFilePath checks to see if a file path is valid.
431+
func validateFilePath(file string) error {
432+
if _, err := os.Stat(file); err == nil {
433+
return nil
434+
}
435+
436+
return fmt.Errorf("%s is not a valid file", file)
437+
}
438+
370439
func GetNamedMapWithScope[K comparable, V any](g *gomega.WithT, session *coherence.Session, cacheName, _ string) coherence.NamedMap[K, V] {
371440
namedCache, err := coherence.GetNamedMap[K, V](session, cacheName)
372441
g.Expect(err).ShouldNot(gomega.HaveOccurred())

0 commit comments

Comments
 (0)