Skip to content

Commit

Permalink
Add support for default-password-file
Browse files Browse the repository at this point in the history
  • Loading branch information
sud82 committed May 16, 2024
1 parent f539f0d commit b20f8b9
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 10 deletions.
11 changes: 9 additions & 2 deletions api/v1/aerospikecluster_validating_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -1792,9 +1792,12 @@ func validateRequiredFileStorageForMetadata(
func validateRequiredFileStorageForFeatureConf(
configSpec AerospikeConfigSpec, storage *AerospikeStorageSpec,
) error {
// TODO Add validation for feature key file.
featureKeyFilePaths := getFeatureKeyFilePaths(configSpec)
nonCAPaths, caPaths := getTLSFilePaths(configSpec)
defaultPassFilePath := GetDefaultPasswordFilePath(&configSpec)

// TODO: What if default password file is given via Secret Manager?
// How operator will access that file? Should we allow that?

var allPaths []string

Expand All @@ -1813,10 +1816,14 @@ func validateRequiredFileStorageForFeatureConf(
// CA cert related fields are not supported with Secret Manager, so check their mount volume
allPaths = append(allPaths, caPaths...)

if defaultPassFilePath != nil {
allPaths = append(allPaths, *defaultPassFilePath)
}

for _, path := range allPaths {
if !storage.isVolumePresentForAerospikePath(filepath.Dir(path)) {
return fmt.Errorf(
"feature-key-file paths or tls paths are not mounted - create an entry for '%v' in 'storage.volumes'",
"feature-key-file paths or tls paths or default-password-file path are not mounted - create an entry for '%v' in 'storage.volumes'",
path,
)
}
Expand Down
31 changes: 31 additions & 0 deletions api/v1/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ const (
confKeyXdr = "xdr"
confKeyXdrDlogPath = "xdr-digestlog-path"

// Security keys.
confKeySecurity = "security"
confKeySecurityDefaultPasswordFile = "default-password-file"

// Service section keys.
confKeyService = "service"
confKeyWorkDirectory = "work-directory"
Expand Down Expand Up @@ -501,3 +505,30 @@ func getContainerNames(containers []v1.Container) []string {
func GetBool(boolPtr *bool) bool {
return ptr.Deref(boolPtr, false)
}

// GetDefaultPasswordFilePath returns the default-password-fille path if configured.
func GetDefaultPasswordFilePath(aerospikeConfigSpec *AerospikeConfigSpec) *string {
aerospikeConfig := aerospikeConfigSpec.Value

// Get security config.
securityConfTmp, ok := aerospikeConfig[confKeySecurity]
if !ok {
return nil
}

securityConf, ok := securityConfTmp.(map[string]interface{})
if !ok {
// Should never happen.
return nil
}

// Get password file.
passFileTmp, ok := securityConf[confKeySecurityDefaultPasswordFile]
if !ok {
return nil
}

passFile := passFileTmp.(string)

return &passFile
}
1 change: 1 addition & 0 deletions config/samples/secrets/password.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
admin12345
5 changes: 4 additions & 1 deletion controllers/access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func AerospikeAdminCredentials(

if currentState.AerospikeAccessControl == nil {
// We haven't yet set up access control. Use default password.
return asdbv1.AdminUsername, asdbv1.DefaultAdminPassword, nil
return asdbv1.AdminUsername, passwordProvider.GetDefaultPassword(desiredState), nil
}

adminUserSpec, ok := asdbv1.GetUsersFromSpec(currentState)[asdbv1.AdminUsername]
Expand Down Expand Up @@ -396,6 +396,9 @@ type AerospikeUserPasswordProvider interface {
Get(username string, userSpec *asdbv1.AerospikeUserSpec) (
string, error,
)

// GetDefaultPassword returns the default password for cluster using AerospikeClusterSpec.
GetDefaultPassword(spec *asdbv1.AerospikeClusterSpec) string
}

// aerospikeAccessControlReconcileCmd commands needed to Reconcile a single access control entry,
Expand Down
49 changes: 49 additions & 0 deletions controllers/client_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,55 @@ func (pp fromSecretPasswordProvider) Get(
return string(passBytes), nil
}

// GetDefaultPassword returns the default password for cluster using AerospikeClusterSpec.
func (pp fromSecretPasswordProvider) GetDefaultPassword(spec *asdbv1.AerospikeClusterSpec) string {
defaultPasswordFilePath := asdbv1.GetDefaultPasswordFilePath(spec.AerospikeConfig)

// No default password file specified. Give default password.
if defaultPasswordFilePath == nil {
return asdbv1.DefaultAdminPassword
}

// Default password file specified. Get the secret name from the volume
volume := spec.Storage.GetVolumeForAerospikePath(*defaultPasswordFilePath)
secretName := volume.Source.Secret.SecretName

// Get the password from the secret.
passwordFileName := filepath.Base(*defaultPasswordFilePath)

password, err := pp.getPasswordFromSecret(secretName, passwordFileName)
if err != nil {
pkgLog.Error(err, "Failed to get password from secret")

return asdbv1.DefaultAdminPassword
}

return password
}

// GetPasswordFromSecret returns the password from the secret.
func (pp fromSecretPasswordProvider) getPasswordFromSecret(
secretName string, passFileName string,
) (string, error) {
secretNamespcedName := types.NamespacedName{Name: secretName, Namespace: pp.namespace}
secret := &corev1.Secret{}

err := (*pp.k8sClient).Get(context.TODO(), secretNamespcedName, secret)
if err != nil {
return "", fmt.Errorf("failed to get secret %s: %v", secretNamespcedName, err)
}

passBytes, ok := secret.Data[passFileName]
if !ok {
return "", fmt.Errorf(
"failed to get password file in secret %s, fileName %s",
secretNamespcedName, passFileName,
)
}

return string(passBytes), nil
}

func (r *SingleClusterReconciler) getPasswordProvider() fromSecretPasswordProvider {
return fromSecretPasswordProvider{
k8sClient: &r.Client, namespace: r.aeroCluster.Namespace,
Expand Down
46 changes: 46 additions & 0 deletions test/access_control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/rand"
"reflect"
"strings"
"time"

"github.com/go-logr/logr"
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -57,6 +58,7 @@ var aerospikeConfigWithSecurityWithQuota = &asdbv1.AerospikeConfigSpec{

var _ = Describe(
"AccessControl", func() {
ctx := goctx.TODO()

Context(
"AccessControl", func() {
Expand Down Expand Up @@ -2144,6 +2146,50 @@ var _ = Describe(
)
},
)

Context("Using default-password-file", func() {
It("Should use default-password-file when configured", func() {
By("Creating cluster")

aeroCluster := createDummyAerospikeCluster(clusterNamespacedName, 4)
racks := getDummyRackConf(1, 2)
aeroCluster.Spec.RackConfig.Racks = racks
aeroCluster.Spec.RackConfig.Namespaces = []string{"test"}

err := k8sClient.Create(ctx, aeroCluster)
Expect(err).ToNot(HaveOccurred())

By("Cluster is not ready, connect with cluster using default password")

// Get default password from secret.
secretNamespcedName := types.NamespacedName{
Name: aerospikeConfigSecret,
Namespace: aeroCluster.Namespace,
}
passFileName := "password.conf"
pass, err := getPasswordFromSecret(k8sClient, secretNamespcedName, passFileName)
Expect(err).ToNot(HaveOccurred())

// Cluster is not yet ready. Therefore, it should be using default password
Eventually(func() error {
clientPolicy := getClientPolicy(aeroCluster, k8sClient)
clientPolicy.Password = pass

client, cerr := getClientWithPolicy(
pkgLog, aeroCluster, k8sClient, clientPolicy)
if cerr != nil {
return cerr
}

nodes := client.GetNodeNames()
Expect(nodes).ToNot(BeNil())

pkgLog.Info("Connected to cluster", "nodes", nodes)

return nil
}, 2*time.Minute).ShouldNot(HaveOccurred())
})
})
},
)

Expand Down
8 changes: 5 additions & 3 deletions test/cluster_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const (
prevServerVersion = "6.4.0.0"
pre6Version = "5.7.0.17"
version6 = "6.0.0.5"
latestServerVersion = "7.0.0.0"
latestServerVersion = "7.1.0.0"
invalidVersion = "3.0.0.4"
)

Expand Down Expand Up @@ -1123,8 +1123,10 @@ func createDummyAerospikeCluster(
"proto-fd-max": defaultProtofdmax,
"auto-pin": "none",
},
"security": map[string]interface{}{},
"network": getNetworkConfig(),
"security": map[string]interface{}{
"default-password-file": "/etc/aerospike/secret/password.conf",
},
"network": getNetworkConfig(),
"namespaces": []interface{}{
getSCNamespaceConfig("test", "/test/dev/xvdf"),
},
Expand Down
28 changes: 24 additions & 4 deletions test/test_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func (pp FromSecretPasswordProvider) Get(
return string(passbyte), nil
}

// This function is not used in the test code. This is just a dummy implementation.
// Tests do not make client call till cluster is up and reconciled.
// DefaultPassword only comes into play when the cluster is being created.
func (pp FromSecretPasswordProvider) GetDefaultPassword(_ *asdbv1.AerospikeClusterSpec) string {
return asdbv1.DefaultAdminPassword
}

func getPasswordProvider(
aeroCluster *asdbv1.AerospikeCluster, k8sClient client.Client,
) FromSecretPasswordProvider {
Expand All @@ -71,6 +78,18 @@ func getPasswordProvider(
func getClient(
log logr.Logger, aeroCluster *asdbv1.AerospikeCluster,
k8sClient client.Client,
) (*as.Client, error) {
// Create policy using status, status has current connection info
policy := getClientPolicy(
aeroCluster, k8sClient,
)

return getClientWithPolicy(log, aeroCluster, k8sClient, policy)
}

func getClientWithPolicy(
log logr.Logger, aeroCluster *asdbv1.AerospikeCluster,
k8sClient client.Client, policy *as.ClientPolicy,
) (*as.Client, error) {
conns, err := newAllHostConn(log, aeroCluster, k8sClient)
if err != nil {
Expand All @@ -87,10 +106,7 @@ func getClient(
},
)
}
// Create policy using status, status has current connection info
policy := getClientPolicy(
aeroCluster, k8sClient,
)

aeroClient, err := as.NewClientWithPolicyAndHost(
policy, hosts...,
)
Expand Down Expand Up @@ -299,13 +315,17 @@ func appendCACertFromFileOrPath(
if err != nil {
return err
}

if !d.IsDir() {
var caData []byte

if caData, err = os.ReadFile(path); err != nil {
return err
}

serverPool.AppendCertsFromPEM(caData)
}

return nil
},
)
Expand Down
22 changes: 22 additions & 0 deletions test/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package test

import (
"bytes"
"context"
goctx "context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -795,3 +796,24 @@ func getAerospikeConfigFromNode(log logr.Logger, k8sClient client.Client, ctx go

return confs[configContext].(lib.Stats), nil
}

func getPasswordFromSecret(k8sClient client.Client,
secretNamespcedName types.NamespacedName, passFileName string,
) (string, error) {
secret := &corev1.Secret{}

err := k8sClient.Get(context.TODO(), secretNamespcedName, secret)
if err != nil {
return "", fmt.Errorf("failed to get secret %s: %v", secretNamespcedName, err)
}

passBytes, ok := secret.Data[passFileName]
if !ok {
return "", fmt.Errorf(
"failed to get password file in secret %s, fileName %s",
secretNamespcedName, passFileName,
)
}

return string(passBytes), nil
}

0 comments on commit b20f8b9

Please sign in to comment.