Skip to content

Commit a25920a

Browse files
authored
[2.13] add validation for etcd s3 cloud credential (#985)
1 parent caf743e commit a25920a

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
@@ -580,6 +580,10 @@ following:
580580
- Equal to another data directory
581581
- Attempts to nest another data directory
582582

583+
##### Etcd S3 CloudCredential Secret
584+
585+
Prevent the creation of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
586+
583587
#### On Update
584588

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

599-
#### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
603+
##### cluster.spec.clusterAgentDeploymentCustomization and cluster.spec.fleetAgentDeploymentCustomization
600604

601605
The `DeploymentCustomization` fields are of 3 types:
602606
- `appendTolerations`: adds tolerations to the appropriate deployment (cluster-agent/fleet-agent)
@@ -611,7 +615,7 @@ A `Toleration` is matched to a regex which is provided by upstream [apimachinery
611615

612616
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.
613617

614-
#### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
618+
##### cluster.spec.clusterAgentDeploymentCustomization.schedulingCustomization
615619

616620
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.
617621

@@ -637,10 +641,16 @@ the format expected by Go, and helps to prevent subtle issues elsewhere when wri
637641

638642
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.
639643

644+
##### Etcd S3 CloudCredential Secret
645+
646+
Prevent the update of objects if the secret specified in `.spec.rkeConfig.etcd.s3.cloudCredentialName` does not exist.
647+
640648
### Mutation Checks
641649

642650
#### On Create
643651

652+
##### Creator ID Annotation
653+
644654
When a cluster is created `field.cattle.io/creatorId` is set to the Username from the request.
645655

646656
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)