From 36324bdf8a9a27f08f20c43be8299e12cf9a1273 Mon Sep 17 00:00:00 2001 From: Jesse Schmidt Date: Thu, 13 Jun 2024 13:21:18 -0700 Subject: [PATCH] start adding tls --- cmd/createIndex.go | 4 +- cmd/dropIndex.go | 6 ++- cmd/listIndex.go | 6 ++- cmd/utils.go | 96 +++++++++++++++++++++++++++++++++++++++++++++- e2e_test.go | 4 +- go.mod | 3 +- go.sum | 2 - 7 files changed, 109 insertions(+), 12 deletions(-) diff --git a/cmd/createIndex.go b/cmd/createIndex.go index 20ed67f..6ce6b41 100644 --- a/cmd/createIndex.go +++ b/cmd/createIndex.go @@ -110,7 +110,7 @@ func newCreateIndexCmd() *cobra.Command { return nil }, RunE: func(_ *cobra.Command, _ []string) error { - hosts, isLoadBalancer := parseBothHostSeedsFlag(*createIndexFlags.seeds, *createIndexFlags.host) + hosts, isLoadBalancer := parseBothHostSeedsFlag(createIndexFlags.seeds, createIndexFlags.host) logger.Debug("parsed flags", slog.String(flagNameHost, createIndexFlags.host.String()), @@ -138,7 +138,7 @@ func newCreateIndexCmd() *cobra.Command { defer cancel() adminClient, err := avs.NewAdminClient( - ctx, hosts, createIndexFlags.listenerName.Val, isLoadBalancer, logger, + ctx, hosts, createIndexFlags.listenerName.Val, isLoadBalancer, nil, logger, ) if err != nil { logger.Error("failed to create AVS client", slog.Any("error", err)) diff --git a/cmd/dropIndex.go b/cmd/dropIndex.go index 39907df..28fc395 100644 --- a/cmd/dropIndex.go +++ b/cmd/dropIndex.go @@ -79,12 +79,14 @@ func newDropIndexCommand() *cobra.Command { slog.Duration(flagNameTimeout, dropIndexFlags.timeout), ) - hosts, isLoadBalancer := parseBothHostSeedsFlag(*dropIndexFlags.seeds, *dropIndexFlags.host) + hosts, isLoadBalancer := parseBothHostSeedsFlag(dropIndexFlags.seeds, dropIndexFlags.host) ctx, cancel := context.WithTimeout(context.Background(), dropIndexFlags.timeout) defer cancel() - adminClient, err := avs.NewAdminClient(ctx, hosts, nil, isLoadBalancer, logger) + adminClient, err := avs.NewAdminClient( + ctx, hosts, nil, isLoadBalancer, nil, logger, + ) if err != nil { logger.Error("failed to create AVS client", slog.Any("error", err)) return err diff --git a/cmd/listIndex.go b/cmd/listIndex.go index 2b90ffc..d9f4008 100644 --- a/cmd/listIndex.go +++ b/cmd/listIndex.go @@ -71,12 +71,14 @@ func newListIndexCmd() *cobra.Command { slog.Duration(flagNameTimeout, listIndexFlags.timeout), ) - hosts, isLoadBalancer := parseBothHostSeedsFlag(*listIndexFlags.seeds, *listIndexFlags.host) + hosts, isLoadBalancer := parseBothHostSeedsFlag(listIndexFlags.seeds, listIndexFlags.host) ctx, cancel := context.WithTimeout(context.Background(), listIndexFlags.timeout) defer cancel() - adminClient, err := avs.NewAdminClient(ctx, hosts, listIndexFlags.listenerName.Val, isLoadBalancer, logger) + adminClient, err := avs.NewAdminClient( + ctx, hosts, listIndexFlags.listenerName.Val, isLoadBalancer, nil, logger, + ) if err != nil { logger.Error("failed to create AVS client", slog.Any("error", err)) return err diff --git a/cmd/utils.go b/cmd/utils.go index 6a22f95..e33b22c 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -2,11 +2,105 @@ package cmd import ( "asvec/cmd/flags" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" avs "github.com/aerospike/aerospike-proximus-client-go" ) -func parseBothHostSeedsFlag(seeds flags.SeedsSliceFlag, host flags.HostPortFlag) (avs.HostPortSlice, bool) { +func newTLSConfig(rootCA [][]byte, cert []byte, key []byte, keyPass []byte, tlsProtoMin int, tlsProtoMax int) (*tls.Config, error) { + if len(rootCA) == 0 && len(cert) == 0 && len(key) == 0 { + return nil, nil + } + + var ( + clientPool []tls.Certificate + serverPool *x509.CertPool + err error + ) + + serverPool = loadCACerts(rootCA) + + if len(cert) > 0 || len(key) > 0 { + clientPool, err = loadServerCertAndKey(cert, key, keyPass) + if err != nil { + return nil, fmt.Errorf("failed to load client authentication certificate and key `%s`", err) + } + } + + tlsConfig := &tls.Config{ //nolint:gosec // aerospike default tls version is TLSv1.2 + Certificates: clientPool, + RootCAs: serverPool, + InsecureSkipVerify: false, + PreferServerCipherSuites: true, + MinVersion: uint16(tlsProtoMin), + MaxVersion: uint16(tlsProtoMax), + } + + return tlsConfig, nil +} + +// loadCACerts returns CA set of certificates (cert pool) +// reads CA certificate based on the certConfig and adds it to the pool +func loadCACerts(certsBytes [][]byte) *x509.CertPool { + certificates, err := x509.SystemCertPool() + if certificates == nil || err != nil { + certificates = x509.NewCertPool() + } + + for _, cert := range certsBytes { + if len(cert) > 0 { + certificates.AppendCertsFromPEM(cert) + } + } + + return certificates +} + +// loadServerCertAndKey reads server certificate and associated key file based on certConfig and keyConfig +// returns parsed server certificate +// if the private key is encrypted, it will be decrypted using key file passphrase +func loadServerCertAndKey(certFileBytes, keyFileBytes, keyPassBytes []byte) ([]tls.Certificate, error) { + var certificates []tls.Certificate + + // Decode PEM data + keyBlock, _ := pem.Decode(keyFileBytes) + + if keyBlock == nil { + return nil, fmt.Errorf("failed to decode PEM data for key or certificate") + } + + // Check and Decrypt the Key Block using passphrase + if x509.IsEncryptedPEMBlock(keyBlock) { //nolint:staticcheck,lll // This needs to be addressed by aerospike as multiple projects require this functionality + decryptedDERBytes, err := x509.DecryptPEMBlock(keyBlock, keyPassBytes) //nolint:staticcheck,lll // This needs to be addressed by aerospike as multiple projects require this functionality + if err != nil { + return nil, fmt.Errorf("failed to decrypt PEM Block: `%s`", err) + } + + keyBlock.Bytes = decryptedDERBytes + keyBlock.Headers = nil + } + + // Encode PEM data + keyPEM := pem.EncodeToMemory(keyBlock) + + if keyPEM == nil { + return nil, fmt.Errorf("failed to encode PEM data for key or certificate") + } + + cert, err := tls.X509KeyPair(certFileBytes, keyPEM) + if err != nil { + return nil, fmt.Errorf("failed to add certificate and key to the pool: `%s`", err) + } + + certificates = append(certificates, cert) + + return certificates, nil +} + +func parseBothHostSeedsFlag(seeds *flags.SeedsSliceFlag, host *flags.HostPortFlag) (avs.HostPortSlice, bool) { isLoadBalancer := false hosts := avs.HostPortSlice{} diff --git a/e2e_test.go b/e2e_test.go index 7d8bf3f..9dadc2f 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -74,7 +74,9 @@ func (suite *CmdTestSuite) SetupSuite() { defer cancel() for { - suite.avsClient, err = avs.NewAdminClient(ctx, avs.HostPortSlice{suite.avsHostPort}, nil, true, logger) + suite.avsClient, err = avs.NewAdminClient( + ctx, avs.HostPortSlice{suite.avsHostPort}, nil, true, nil, logger, + ) if err != nil { fmt.Printf("unable to create avs client %v", err) diff --git a/go.mod b/go.mod index 5f42fff..76d251b 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,7 @@ module asvec go 1.21.7 -//replace github.com/aerospike/aerospike-proximus-client-go => github.com/aerospike/aerospike-proximus-client-go VEC-155-admin-client -//replace github.com/aerospike/aerospike-proximus-client-go => /Users/jesseschmidt/Developer/aerospike-proximus-client-go +replace github.com/aerospike/aerospike-proximus-client-go => /Users/jesseschmidt/Developer/aerospike-proximus-client-go require ( github.com/aerospike/aerospike-proximus-client-go v0.0.0-20240603230632-86a0ebaa8aa9 diff --git a/go.sum b/go.sum index a31c1ab..16c4edc 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/aerospike/aerospike-client-go/v7 v7.4.0 h1:g8/7v8RHhQhTArhW3C7Au7o+u8j8x5eySZL6MXfpHKU= github.com/aerospike/aerospike-client-go/v7 v7.4.0/go.mod h1:pPKnWiS8VDJcH4IeB1b8SA2TWnkjcVLHwAAJ+BHfGK8= -github.com/aerospike/aerospike-proximus-client-go v0.0.0-20240603230632-86a0ebaa8aa9 h1:qVpPCrbp0pNNmP1CPqln6HkzhVmFmOOVZYLq4IDlidI= -github.com/aerospike/aerospike-proximus-client-go v0.0.0-20240603230632-86a0ebaa8aa9/go.mod h1:N0kxd4FoYDbLOEwm8vWH6wKUkoR5v0Wp/v0+tUqoUMg= github.com/aerospike/tools-common-go v0.0.0-20240425222921-596724ec5926 h1:CqkNasGC/7x5JvYjCSuAVX/rG+nUgRQtXfxIURXo5OE= github.com/aerospike/tools-common-go v0.0.0-20240425222921-596724ec5926/go.mod h1:Ig1lRynXx0tXNOY3MdtanTsKz1ifG/2AyDFMXn3RMTc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=