diff --git a/pkg/providers/vsphere/validator.go b/pkg/providers/vsphere/validator.go index 1c3addcf7c97b..db7202db722e1 100644 --- a/pkg/providers/vsphere/validator.go +++ b/pkg/providers/vsphere/validator.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net" + "path/filepath" anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/config" @@ -419,47 +420,81 @@ func (v *Validator) validateControlPlaneIpUniqueness(spec *Spec) error { return nil } -func (v *Validator) validateUserPrivs(ctx context.Context, spec *Spec, vuc *config.VSphereUserConfig) error { +func (v *Validator) collectSpecMachineConfigs(ctx context.Context, spec *Spec) ([]*anywherev1.VSphereMachineConfig, error) { controlPlaneMachineConfig := spec.controlPlaneMachineConfig() + machineConfigs := []*anywherev1.VSphereMachineConfig{controlPlaneMachineConfig} - privObjs := []RequiredAccess{ + for _, workerNodeGroupConfiguration := range spec.Cluster.Spec.WorkerNodeGroupConfigurations { + workerNodeGroupMachineConfig := spec.workerMachineConfig(workerNodeGroupConfiguration) + machineConfigs = append(machineConfigs, workerNodeGroupMachineConfig) + } + + if spec.Cluster.Spec.ExternalEtcdConfiguration != nil { + etcdMachineConfig := spec.etcdMachineConfig() + machineConfigs = append(machineConfigs, etcdMachineConfig) + } + + return machineConfigs, nil +} + +func (v *Validator) validateUserPrivs(ctx context.Context, spec *Spec, vuc *config.VSphereUserConfig) error { + machineConfigs, err := v.collectSpecMachineConfigs(ctx, spec) + if err != nil { + return err + } + + ras := []RequiredAccess{ // validate global root priv settings are correct { objectType: vsphereTypeFolder, privsContent: config.VSphereGlobalPrivsFile, path: vsphereRootPath, }, - - // validate object-level priv settings are correct - { - objectType: vsphereTypeDatastore, - privsContent: config.VSphereUserPrivsFile, - path: controlPlaneMachineConfig.Spec.Datastore, - }, - { - objectType: vsphereTypeResourcePool, - privsContent: config.VSphereUserPrivsFile, - path: controlPlaneMachineConfig.Spec.ResourcePool, - }, { objectType: vsphereTypeNetwork, privsContent: config.VSphereUserPrivsFile, path: spec.datacenterConfig.Spec.Network, }, + } + + var newRas []RequiredAccess + for _, mc := range machineConfigs { + newRas = []RequiredAccess{ + // validate object-level priv settings are correct + { + objectType: vsphereTypeDatastore, + privsContent: config.VSphereUserPrivsFile, + path: mc.Spec.Datastore, + }, + { + objectType: vsphereTypeResourcePool, + privsContent: config.VSphereUserPrivsFile, + path: mc.Spec.ResourcePool, + }, + // validate Administrator role (all privs) on VM folder and Template folder + { + objectType: vsphereTypeFolder, + privsContent: config.VSphereAdminPrivsFile, + path: mc.Spec.Folder, + }, + // ToDo: add more sophisticated validation around a scenario where someone has uploaded templates + // on their own and does not want to allow EKSA user write access to templates + // Verify privs on the template + { + objectType: "VirtualMachine", + privsContent: config.VSphereAdminPrivsFile, + path: mc.Spec.Template, + }, + // Verify privs on the template directory + { + objectType: vsphereTypeFolder, + privsContent: config.VSphereAdminPrivsFile, + path: filepath.Dir(mc.Spec.Template), + }, + } + + ras = append(ras, newRas...) - // validate Administrator role (all privs) on VM folder and Template folder - { - objectType: vsphereTypeFolder, - privsContent: config.VSphereAdminPrivsFile, - path: controlPlaneMachineConfig.Spec.Folder, - }, - // Not implementing a check on the template because I'm not sure if we always require/provide/allow template permissions - // https://anywhere.eks.amazonaws.com/docs/reference/vsphere/vsphere-preparation/#deploy-an-ova-template - // { - // objectType: "VirtualMachine", - // privsContent: config.VSphereAdminPrivsFile, - // path: controlPlaneMachineConfig.Spec.Template, - // }, } host := spec.datacenterConfig.Spec.Server @@ -477,34 +512,45 @@ func (v *Validator) validateUserPrivs(ctx context.Context, spec *Spec, vuc *conf return err } - return v.validatePrivs(ctx, privObjs, vsc) + return v.validatePrivs(ctx, ras, vsc) } func (v *Validator) validateCSIUserPrivs(ctx context.Context, spec *Spec, vuc *config.VSphereUserConfig) error { - controlPlaneMachineConfig := spec.controlPlaneMachineConfig() - - privObjs := []RequiredAccess{ - { // CNS-Datastore role - objectType: vsphereTypeDatastore, - privsContent: config.VSphereCnsDatastorePrivsFile, - path: controlPlaneMachineConfig.Spec.Datastore, - }, + ras := []RequiredAccess{ { // CNS-SEARCH-AND-SPBM role objectType: vsphereTypeFolder, privsContent: config.VSphereCnsSearchAndSpbmPrivsFile, path: vsphereRootPath, }, - { // CNS-VM role - objectType: vsphereTypeFolder, - privsContent: config.VSphereCnsVmPrivsFile, - path: controlPlaneMachineConfig.Spec.Folder, - }, - // CNS-HOST-CONFIG-STORAGE role - { - objectType: vsphereTypeDatastore, - privsContent: config.VSphereCnsHostConfigStorageFile, - path: controlPlaneMachineConfig.Spec.Datastore, - }, + } + + machineConfigs, err := v.collectSpecMachineConfigs(ctx, spec) + if err != nil { + return err + } + + var newRas []RequiredAccess + for _, mc := range machineConfigs { + newRas = []RequiredAccess{ + { // CNS-Datastore role + objectType: vsphereTypeDatastore, + privsContent: config.VSphereCnsDatastorePrivsFile, + path: mc.Spec.Datastore, + }, + + { // CNS-VM role + objectType: vsphereTypeFolder, + privsContent: config.VSphereCnsVmPrivsFile, + path: mc.Spec.Folder, + }, + // CNS-HOST-CONFIG-STORAGE role + { + objectType: vsphereTypeDatastore, + privsContent: config.VSphereCnsHostConfigStorageFile, + path: mc.Spec.Datastore, + }, + } + ras = append(ras, newRas...) } host := spec.datacenterConfig.Spec.Server @@ -522,7 +568,7 @@ func (v *Validator) validateCSIUserPrivs(ctx context.Context, spec *Spec, vuc *c return err } - return v.validatePrivs(ctx, privObjs, vsc) + return v.validatePrivs(ctx, ras, vsc) } func (v *Validator) validateCPUserPrivs(ctx context.Context, spec *Spec, vuc *config.VSphereUserConfig) error {