Skip to content

Commit a6ef7de

Browse files
authored
[2.12] add validation for etcd s3 cloud credential (#1022)
1 parent 914c3c0 commit a6ef7de

File tree

4 files changed

+361
-21
lines changed

4 files changed

+361
-21
lines changed

docs.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ following:
574574
- Equal to another data directory
575575
- Attempts to nest another data directory
576576

577+
##### Etcd S3 CloudCredential Secret
578+
579+
Prevent the creation of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
580+
577581
#### On Update
578582

579583
##### Creator ID Annotation
@@ -590,7 +594,7 @@ section. A secondary validator will ensure that the effective data directory for
590594
from the one chosen during cluster creation. Additionally, the changing of a data directory for the `system-agent`,
591595
kubernetes distro (RKE2/K3s), and CAPR components is also prohibited.
592596

593-
#### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
597+
##### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
594598

595599
The `DeploymentCustomization` fields are of 3 types:
596600
- `appendTolerations`: adds tolerations to the appropriate deployment (cluster-agent/fleet-agent)
@@ -605,7 +609,7 @@ A `Toleration` is matched to a regex which is provided by upstream [apimachinery
605609

606610
For the `Affinity` based rules, the `podAffinity`/`podAntiAffinity` are validated via label selectors via [this apimachinery function](https://github.com/kubernetes/apimachinery/blob/02a41040d88da08de6765573ae2b1a51f424e1ca/pkg/apis/meta/v1/validation/validation.go#L56) whereas the `nodeAffinity` `nodeSelectorTerms` are validated via the same `Toleration` function.
607611

608-
#### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
612+
##### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
609613

610614
The `SchedulingCustomization` subfield of the `DeploymentCustomization` field defines the properties of a Pod Disruption Budget and Priority Class which will be automatically deployed by Rancher for the cattle-cluster-agent.
611615

@@ -631,10 +635,16 @@ the format expected by Go, and helps to prevent subtle issues elsewhere when wri
631635

632636
The only exception to this check is if the existing cluster already has a `NO_PROXY` variable which includes spaces in its value. In this case, update operations are permitted. If `NO_PROXY` is later updated to value which does not contain spaces, this exception will no longer occur.
633637

638+
##### Etcd S3 CloudCredential Secret
639+
640+
Prevent the update of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
641+
634642
### Mutation Checks
635643

636644
#### On Create
637645

646+
##### Creator ID Annotation
647+
638648
When a cluster is created `field.cattle.io/creatorId` is set to the Username from the request.
639649

640650
If `field.cattle.io/no-creator-rbac` annotation is set, `field.cattle.io/creatorId` does not get set.

pkg/resources/provisioning.cattle.io/v1/cluster/Cluster.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ following:
2424
- Equal to another data directory
2525
- Attempts to nest another data directory
2626

27+
#### Etcd S3 CloudCredential Secret
28+
29+
Prevent the creation of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
30+
2731
### On Update
2832

2933
#### Creator ID Annotation
@@ -40,7 +44,7 @@ section. A secondary validator will ensure that the effective data directory for
4044
from the one chosen during cluster creation. Additionally, the changing of a data directory for the `system-agent`,
4145
kubernetes distro (RKE2/K3s), and CAPR components is also prohibited.
4246

43-
### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
47+
#### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
4448

4549
The `DeploymentCustomization` fields are of 3 types:
4650
- `appendTolerations`: adds tolerations to the appropriate deployment (cluster-agent/fleet-agent)
@@ -55,7 +59,7 @@ A `Toleration` is matched to a regex which is provided by upstream [apimachinery
5559

5660
For the `Affinity` based rules, the `podAffinity`/`podAntiAffinity` are validated via label selectors via [this apimachinery function](https://github.com/kubernetes/apimachinery/blob/02a41040d88da08de6765573ae2b1a51f424e1ca/pkg/apis/meta/v1/validation/validation.go#L56) whereas the `nodeAffinity` `nodeSelectorTerms` are validated via the same `Toleration` function.
5761

58-
### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
62+
#### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
5963

6064
The `SchedulingCustomization` subfield of the `DeploymentCustomization` field defines the properties of a Pod Disruption Budget and Priority Class which will be automatically deployed by Rancher for the cattle-cluster-agent.
6165

@@ -81,10 +85,16 @@ the format expected by Go, and helps to prevent subtle issues elsewhere when wri
8185

8286
The only exception to this check is if the existing cluster already has a `NO_PROXY` variable which includes spaces in its value. In this case, update operations are permitted. If `NO_PROXY` is later updated to value which does not contain spaces, this exception will no longer occur.
8387

88+
#### Etcd S3 CloudCredential Secret
89+
90+
Prevent the update of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
91+
8492
## Mutation Checks
8593

8694
### On Create
8795

96+
#### Creator ID Annotation
97+
8898
When a cluster is created `field.cattle.io/creatorId` is set to the Username from the request.
8999

90100
If `field.cattle.io/no-creator-rbac` annotation is set, `field.cattle.io/creatorId` does not get set.

pkg/resources/provisioning.cattle.io/v1/cluster/validator.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func NewProvisioningClusterValidator(client *clients.Clients) *ProvisioningClust
5353
admitter: provisioningAdmitter{
5454
sar: client.K8s.AuthorizationV1().SubjectAccessReviews(),
5555
mgmtClusterClient: client.Management.Cluster(),
56+
secretClient: client.Core.Secret(),
5657
secretCache: client.Core.Secret().Cache(),
5758
psactCache: client.Management.PodSecurityAdmissionConfigurationTemplate().Cache(),
5859
featureCache: client.Management.Feature().Cache(),
@@ -87,6 +88,7 @@ func (p *ProvisioningClusterValidator) Admitters() []admission.Admitter {
8788
type provisioningAdmitter struct {
8889
sar authorizationv1.SubjectAccessReviewInterface
8990
mgmtClusterClient v3.ClusterClient
91+
secretClient corev1controller.SecretController
9092
secretCache corev1controller.SecretCache
9193
psactCache v3.PodSecurityAdmissionConfigurationTemplateCache
9294
featureCache v3.FeatureCache
@@ -154,6 +156,10 @@ func (p *provisioningAdmitter) Admit(request *admission.Request) (*admissionv1.A
154156
if response, err = p.validatePriorityClass(oldCluster, cluster); err != nil || !response.Allowed {
155157
return response, err
156158
}
159+
160+
if response, err = p.validateS3Secret(oldCluster, cluster); err != nil || !response.Allowed {
161+
return response, err
162+
}
157163
}
158164

159165
if err := p.validatePSACT(request, response, cluster); err != nil || response.Result != nil {
@@ -697,6 +703,51 @@ func (p *provisioningAdmitter) validatePodDisruptionBudget(oldCluster, cluster *
697703
return admission.ResponseAllowed(), nil
698704
}
699705

706+
// validateS3Secret checks if the S3 cloud credential secret referenced in the cluster exists.
707+
func (p *provisioningAdmitter) validateS3Secret(oldCluster, cluster *v1.Cluster) (*admissionv1.AdmissionResponse, error) {
708+
if cluster.Name == localCluster {
709+
return admission.ResponseAllowed(), nil
710+
}
711+
712+
rkeConfig := cluster.Spec.RKEConfig
713+
if rkeConfig == nil || rkeConfig.ETCD == nil || rkeConfig.ETCD.S3 == nil || rkeConfig.ETCD.S3.CloudCredentialName == "" {
714+
return admission.ResponseAllowed(), nil
715+
}
716+
717+
var (
718+
oldSecret string
719+
newSecret string
720+
)
721+
if oldCluster.Spec.RKEConfig != nil &&
722+
oldCluster.Spec.RKEConfig.ETCD != nil &&
723+
oldCluster.Spec.RKEConfig.ETCD.S3 != nil {
724+
oldSecret = oldCluster.Spec.RKEConfig.ETCD.S3.CloudCredentialName
725+
}
726+
newSecret = cluster.Spec.RKEConfig.ETCD.S3.CloudCredentialName
727+
728+
// Skip if the credential names remain the same
729+
if oldSecret == newSecret {
730+
return admission.ResponseAllowed(), nil
731+
}
732+
733+
// check if the secret exists
734+
namespace, name := getCloudCredentialSecretInfo(cluster.Namespace, newSecret)
735+
_, err := p.secretCache.Get(namespace, name)
736+
if apierrors.IsNotFound(err) {
737+
// Since the secret can be created alongside the cluster via the UI, there's a chance it's not yet present in the cache.
738+
// Therefore, we perform an additional check directly against the API server.
739+
_, err = p.secretClient.Get(namespace, name, metav1.GetOptions{})
740+
}
741+
if apierrors.IsNotFound(err) {
742+
msg := fmt.Sprintf("etcd s3 cloud credential secret %s/%s is not found", namespace, name)
743+
return admission.ResponseBadRequest(msg), nil
744+
} else if err != nil {
745+
return nil, fmt.Errorf("failed to get etcd s3 cloud credential secret %s/%s: %w", namespace, name, err)
746+
}
747+
748+
return admission.ResponseAllowed(), nil
749+
}
750+
700751
func validateAgentDeploymentCustomization(customization *v1.AgentDeploymentCustomization, path *field.Path) field.ErrorList {
701752
if customization == nil {
702753
return nil

0 commit comments

Comments
 (0)