Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 19 additions & 26 deletions internal/controller/cluster/access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,6 @@ func AerospikeAdminCredentials(
desiredState, currentState *asdbv1.AerospikeClusterSpec,
passwordProvider AerospikeUserPasswordProvider,
) (user, pass string, err error) {
var (
enabled bool
currentSecurityErr error
desiredSecurityErr error
)

enabled, desiredSecurityErr = asdbv1.IsSecurityEnabled(desiredState.AerospikeConfig.Value)
if !enabled {
if currentState.AerospikeConfig != nil {
// It is possible that this is a new cluster and the current state is empty.
enabled, currentSecurityErr = asdbv1.IsSecurityEnabled(currentState.AerospikeConfig.Value)
}

if currentSecurityErr != nil && desiredSecurityErr != nil {
return "", "", desiredSecurityErr
}
}

if !enabled {
// Return zero strings if this is not a security enabled cluster.
return "", "", nil
}

if currentState.AerospikeAccessControl == nil {
// We haven't yet set up access control. Use default password.
return asdbv1.AdminUsername, passwordProvider.GetDefaultPassword(desiredState), nil
Expand Down Expand Up @@ -190,6 +167,13 @@ func (r *SingleClusterReconciler) reconcileUsers(
currentUserNames = append(currentUserNames, userName)
}

if len(desired) == 0 {
// If no users are desired, do not drop admin user.
desired[asdbv1.AdminUsername] = asdbv1.AerospikeUserSpec{
Roles: []string{"user-admin", "sys-admin"},
}
}

// List users needed in the desired list.
requiredUserNames := make([]string, 0, len(desired))
for userName := range desired {
Expand All @@ -213,9 +197,18 @@ func (r *SingleClusterReconciler) reconcileUsers(
for userName := range desired {
userSpec := desired[userName]

password, err := passwordProvider.Get(userName, &userSpec)
if err != nil {
return err
var (
password string
err error
)

if userName == asdbv1.AdminUsername && userSpec.SecretName == "" {
password = passwordProvider.GetDefaultPassword(&r.aeroCluster.Spec)
} else {
password, err = passwordProvider.Get(userName, &userSpec)
if err != nil {
return err
}
}

cmd := aerospikeUserCreateUpdate{
Expand Down
17 changes: 12 additions & 5 deletions internal/controller/cluster/aero_info_calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ func (r *SingleClusterReconciler) quiescePods(
return deployment.InfoQuiesce(r.Log, policy, allHostConns, selectedHostConns, r.removedNamespaces(nodesNamespaces))
}

func (r *SingleClusterReconciler) getClusterSecurityConfig(
policy *as.ClientPolicy, podList *corev1.PodList, ignorablePodNames sets.Set[string],
) (map[string]deployment.InfoResult, error) {
hostConns, err := r.newPodsHostConnWithOption(podList.Items, ignorablePodNames)
if err != nil {
return nil, err
}

return deployment.GetInfoOnHosts(r.Log, policy, hostConns, "get-config:context=security")
}

// TODO: Check only for migration
func (r *SingleClusterReconciler) waitForClusterStability(
policy *as.ClientPolicy, allHostConns []*deployment.HostConn,
Expand Down Expand Up @@ -235,11 +246,7 @@ func (r *SingleClusterReconciler) newPodsHostConnWithOption(pods []corev1.Pod, i

func (r *SingleClusterReconciler) newAsConn(pod *corev1.Pod) *deployment.ASConn {
// Use pod IP and direct service port from within the operator for info calls.
tlsName, port := r.getServiceTLSNameAndPortIfConfigured()

if tlsName == "" || port == nil {
port = asdbv1.GetServicePort(r.aeroCluster.Spec.AerospikeConfig)
}
tlsName, port := r.getResolvedTLSNameAndPort()

host := pod.Status.PodIP
asConn := &deployment.ASConn{
Expand Down
62 changes: 30 additions & 32 deletions internal/controller/cluster/rack.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,12 +386,10 @@ func (r *SingleClusterReconciler) upgradeOrRollingRestartRack(
return found, common.ReconcileError(err)
}

// Handle enable security just after updating configMap.
// This code will run only when security is being enabled in an existing cluster
// Update for security is verified by checking the config hash of the pod with the
// config hash present in config map
if err := r.handleEnableSecurity(rackState, ignorablePodNames); err != nil {
return found, common.ReconcileError(err)
if failedPods == nil {
if err := r.handleClusterSecurity(ignorablePodNames); err != nil {
return found, common.ReconcileError(err)
}
}

// Upgrade
Expand Down Expand Up @@ -1775,25 +1773,26 @@ func (r *SingleClusterReconciler) getCurrentRackList() (
return rackList, nil
}

func (r *SingleClusterReconciler) handleEnableSecurity(rackState *RackState, ignorablePodNames sets.Set[string]) error {
if !r.enablingSecurity() {
func (r *SingleClusterReconciler) handleClusterSecurity(
ignorablePodNames sets.Set[string]) error {
if !r.needACLReconcile() {
// No need to proceed if security is not to be enabling
return nil
}

// Get pods where security-enabled config is applied
securityEnabledPods, err := r.getPodsWithUpdatedConfigForRack(rackState)
securityEnabledPod, err := r.getAnyPodWithEnabledSecurity(ignorablePodNames)
if err != nil {
return err
}

if len(securityEnabledPods) == 0 {
// No security-enabled pods found
if securityEnabledPod == nil {
// No security-enabled pod found
return nil
}

// Setup access control.
if err := r.validateAndReconcileAccessControl(securityEnabledPods, ignorablePodNames); err != nil {
if err := r.validateAndReconcileAccessControl(securityEnabledPod, ignorablePodNames); err != nil {
r.Log.Error(err, "Failed to Reconcile access control")
r.Recorder.Eventf(
r.aeroCluster, corev1.EventTypeWarning, "ACLUpdateFailed",
Expand All @@ -1807,41 +1806,40 @@ func (r *SingleClusterReconciler) handleEnableSecurity(rackState *RackState, ign
return nil
}

func (r *SingleClusterReconciler) enablingSecurity() bool {
return r.aeroCluster.Spec.AerospikeAccessControl != nil && r.aeroCluster.Status.AerospikeAccessControl == nil
func (r *SingleClusterReconciler) needACLReconcile() bool {
isAccessControlSpecNil := r.aeroCluster.Spec.AerospikeAccessControl == nil
isAccessControlStatusNil := r.aeroCluster.Status.AerospikeAccessControl == nil

return !r.IsStatusEmpty() && isAccessControlSpecNil != isAccessControlStatusNil
}

func (r *SingleClusterReconciler) getPodsWithUpdatedConfigForRack(rackState *RackState) ([]corev1.Pod, error) {
pods, err := r.getOrderedRackPodList(rackState.Rack.ID)
func (r *SingleClusterReconciler) getAnyPodWithEnabledSecurity(
ignorablePodNames sets.Set[string]) (*corev1.Pod, error) {
podList, err := r.getClusterPodList()
if err != nil {
return nil, fmt.Errorf("failed to list pods: %v", err)
return nil, err
}

if len(pods) == 0 {
// No pod found for the rack
if len(podList.Items) == 0 {
return nil, nil
}

confMap, err := r.getConfigMap(rackState.Rack.ID)
securityConfigs, err := r.getClusterSecurityConfig(r.getClientPolicy(), podList, ignorablePodNames)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get security config: %v", err)
}

requiredConfHash := confMap.Data[aerospikeConfHashFileName]

updatedPods := make([]corev1.Pod, 0, len(pods))

for idx := range pods {
podName := pods[idx].Name
podStatus := r.aeroCluster.Status.Pods[podName]
_, port := r.getResolvedTLSNameAndPort()

if podStatus.AerospikeConfigHash == requiredConfHash {
// Config hash is matching, it means config has been applied
updatedPods = append(updatedPods, *pods[idx])
for idx := range podList.Items {
if security, ok := securityConfigs[hostID(podList.Items[idx].Status.PodIP, int(*port))]; ok {
if securityEnabled, ok := security["enable-security"]; ok && securityEnabled == "true" {
return &podList.Items[idx], nil
}
}
}

return updatedPods, nil
return nil, nil
}

func isContainerNameInStorageVolumeAttachments(
Expand Down
35 changes: 18 additions & 17 deletions internal/controller/cluster/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,29 +328,34 @@ func (r *SingleClusterReconciler) recoverIgnorablePods(ignorablePodNames sets.Se
}

func (r *SingleClusterReconciler) validateAndReconcileAccessControl(
selectedPods []corev1.Pod,
selectedPod *corev1.Pod,
ignorablePodNames sets.Set[string],
) error {
enabled, err := asdbv1.IsSecurityEnabled(r.aeroCluster.Spec.AerospikeConfig.Value)
if err != nil {
return fmt.Errorf("failed to get cluster security status: %v", err)
}
var (
conns []*deployment.HostConn
err error
)

if !enabled {
r.Log.Info("Cluster is not security enabled, please enable security for this cluster.")
return nil
}
// Create client
if selectedPod == nil {
var enabled bool

var conns []*deployment.HostConn
enabled, err = asdbv1.IsSecurityEnabled(r.aeroCluster.Spec.AerospikeConfig.Value)
if err != nil {
return fmt.Errorf("failed to get cluster security status: %v", err)
}

if !enabled {
r.Log.Info("Cluster is not security enabled, please enable security for this cluster.")
return nil
}

// Create client
if selectedPods == nil {
conns, err = r.newAllHostConnWithOption(ignorablePodNames)
if err != nil {
return fmt.Errorf("failed to get host info: %v", err)
}
} else {
conns, err = r.newPodsHostConnWithOption(selectedPods, ignorablePodNames)
conns, err = r.newPodsHostConnWithOption([]corev1.Pod{*selectedPod}, ignorablePodNames)
if err != nil {
return fmt.Errorf("failed to get host info: %v", err)
}
Expand Down Expand Up @@ -512,10 +517,6 @@ func (r *SingleClusterReconciler) getClusterReadinessStatus() (bool, error) {
}

func (r *SingleClusterReconciler) updateAccessControlStatus() error {
if r.aeroCluster.Spec.AerospikeAccessControl == nil {
return nil
}

r.Log.Info("Update access control status for AerospikeCluster")

// Get the old object, it may have been updated in between.
Expand Down
10 changes: 10 additions & 0 deletions internal/controller/cluster/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,16 @@ func (r *SingleClusterReconciler) getServiceTLSNameAndPortIfConfigured() (tlsNam
return tlsName, port
}

func (r *SingleClusterReconciler) getResolvedTLSNameAndPort() (tlsName string, port *int32) {
tlsName, port = r.getServiceTLSNameAndPortIfConfigured()

if tlsName == "" || port == nil {
port = asdbv1.GetServicePort(r.aeroCluster.Spec.AerospikeConfig)
}

return tlsName, port
}

func (r *SingleClusterReconciler) isServiceMetadataUpdated(
service *corev1.Service,
statusMetadata,
Expand Down
47 changes: 22 additions & 25 deletions internal/webhook/v1/aerospikecluster_validating_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (acv *AerospikeClusterCustomValidator) ValidateDelete(_ context.Context, ob
return nil, nil
}

// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type
// ValidateUpdate implement webhook.CustomValidator so a webhook will be registered for the type
func (acv *AerospikeClusterCustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object,
) (admission.Warnings, error) {
aerospikeCluster, ok := newObj.(*asdbv1.AerospikeCluster)
Expand Down Expand Up @@ -147,6 +147,10 @@ func (acv *AerospikeClusterCustomValidator) ValidateUpdate(_ context.Context, ol
return warnings, err
}

if err := validateAccessControlUpdate(&oldObject.Spec, &aerospikeCluster.Spec, &aerospikeCluster.Status); err != nil {
return warnings, err
}

// Validate AerospikeConfig update
if err := validateAerospikeConfigUpdate(
aslog, aerospikeCluster.Spec.AerospikeConfig, oldObject.Spec.AerospikeConfig,
Expand All @@ -159,6 +163,23 @@ func (acv *AerospikeClusterCustomValidator) ValidateUpdate(_ context.Context, ol
return warnings, validateRackUpdate(aslog, oldObject, aerospikeCluster)
}

func validateAccessControlUpdate(
oldSpec *asdbv1.AerospikeClusterSpec,
newSpec *asdbv1.AerospikeClusterSpec,
status *asdbv1.AerospikeClusterStatus,
) error {
// Prevent removal of access control before status is updated
if oldSpec.AerospikeAccessControl != nil &&
newSpec.AerospikeAccessControl == nil &&
status.AerospikeAccessControl == nil {
return fmt.Errorf(
"cannot remove aerospikeAccessControl: " +
"security enablement is in progress and status has not yet been updated to reflect the current configuration")
}

return nil
}

func validate(aslog logr.Logger, cluster *asdbv1.AerospikeCluster) (admission.Warnings, error) {
aslog.V(1).Info("Validate AerospikeCluster spec", "obj.Spec", cluster.Spec)

Expand Down Expand Up @@ -926,26 +947,6 @@ func validateNamespaceConfig(
return nil
}

func validateSecurityConfigUpdateFromStatus(newSpec, currentStatus *asdbv1.AerospikeConfigSpec) error {
if currentStatus != nil {
currentSecurityEnabled, err := asdbv1.IsSecurityEnabled(currentStatus.Value)
if err != nil {
return err
}

desiredSecurityEnabled, err := asdbv1.IsSecurityEnabled(newSpec.Value)
if err != nil {
return err
}

if currentSecurityEnabled && !desiredSecurityEnabled {
return fmt.Errorf("cannot disable cluster security in running cluster")
}
}

return nil
}

func validateAerospikeConfigUpdate(
aslog logr.Logger,
incomingSpec, outgoingSpec, currentStatus *asdbv1.AerospikeConfigSpec,
Expand All @@ -959,10 +960,6 @@ func validateAerospikeConfigUpdate(
return err
}

if err := validateSecurityConfigUpdateFromStatus(incomingSpec, currentStatus); err != nil {
return err
}

return validateNsConfUpdateFromStatus(incomingSpec, currentStatus)
}

Expand Down
Loading