diff --git a/data/data/install.openshift.io_installconfigs.yaml b/data/data/install.openshift.io_installconfigs.yaml index e2179723198..1c3319650ce 100644 --- a/data/data/install.openshift.io_installconfigs.yaml +++ b/data/data/install.openshift.io_installconfigs.yaml @@ -2748,6 +2748,12 @@ spec: azure: description: Azure is the configuration used when installing on Azure. properties: + allowSharedKeyAccess: + description: |- + AllowSharedKeyAccess specifies if shared access key should be enabled for the storage account. + Default value is true. + Disabling this will require a new permission "Storage Blob Data Contributor" in azure. + type: boolean armEndpoint: description: ARMEndpoint is the endpoint for the Azure API when installing on Azure Stack. diff --git a/pkg/explain/printer_test.go b/pkg/explain/printer_test.go index bda110fed47..6b4b4364144 100644 --- a/pkg/explain/printer_test.go +++ b/pkg/explain/printer_test.go @@ -289,6 +289,11 @@ cluster itself may not include these tags.`, }, { path: []string{"platform", "azure"}, desc: `FIELDS: + allowSharedKeyAccess + AllowSharedKeyAccess specifies if shared access key should be enabled for the storage account. +Default value is true. +Disabling this will require a new permission "Storage Blob Data Contributor" in azure. + armEndpoint ARMEndpoint is the endpoint for the Azure API when installing on Azure Stack. diff --git a/pkg/infrastructure/azure/azure.go b/pkg/infrastructure/azure/azure.go index 26ce2399fa1..9732dce1d7e 100644 --- a/pkg/infrastructure/azure/azure.go +++ b/pkg/infrastructure/azure/azure.go @@ -20,6 +20,8 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service" "github.com/coreos/stream-metadata-go/arch" "github.com/google/uuid" "github.com/sirupsen/logrus" @@ -295,7 +297,8 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput installConfig := in.InstallConfig.Config platform := installConfig.Platform.Azure subscriptionID := session.Credentials.SubscriptionID - cloudConfiguration := session.CloudConfig + p.CloudConfiguration = session.CloudConfig + p.TokenCredential = session.TokenCreds var architecture armcompute.Architecture if installConfig.ControlPlane.Architecture == types.ArchitectureARM64 { @@ -348,22 +351,30 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput tags[k] = ptr.To(v) } - tokenCredential := session.TokenCreds storageURL := fmt.Sprintf("https://%s.blob.%s", storageAccountName, session.Environment.StorageEndpointSuffix) blobURL := fmt.Sprintf("%s/%s/%s", storageURL, containerName, blobName) + sharedKey := true + if in.InstallConfig.Config.Azure.AllowSharedKeyAccess != nil { + sharedKey = *in.InstallConfig.Config.Azure.AllowSharedKeyAccess + } + if sharedKey { + logrus.Info("Shared key access is enabled for storage account; Managed Identity-based access will not be used for blob operations") + } + // Create storage account createStorageAccountOutput, err := CreateStorageAccount(ctx, &CreateStorageAccountInput{ - SubscriptionID: subscriptionID, - ResourceGroupName: resourceGroupName, - StorageAccountName: storageAccountName, - CloudName: platform.CloudName, - Region: platform.Region, - AuthType: session.AuthType, - Tags: tags, - CustomerManagedKey: platform.CustomerManagedKey, - TokenCredential: tokenCredential, - CloudConfiguration: cloudConfiguration, + SubscriptionID: subscriptionID, + ResourceGroupName: resourceGroupName, + StorageAccountName: storageAccountName, + CloudName: platform.CloudName, + Region: platform.Region, + AuthType: session.AuthType, + AllowSharedKeyAccess: sharedKey, + Tags: tags, + CustomerManagedKey: platform.CustomerManagedKey, + TokenCredential: p.TokenCredential, + CloudConfiguration: p.CloudConfiguration, }) if err != nil { return err @@ -395,13 +406,16 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput // Upload the image to the container if _, ok := os.LookupEnv("OPENSHIFT_INSTALL_SKIP_IMAGE_UPLOAD"); !ok { _, err = CreatePageBlob(ctx, &CreatePageBlobInput{ - StorageURL: storageURL, - BlobURL: blobURL, - ImageURL: imageURL, - ImageLength: imageLength, - StorageAccountName: storageAccountName, - StorageAccountKeys: storageAccountKeys, - CloudConfiguration: cloudConfiguration, + StorageURL: storageURL, + BlobURL: blobURL, + ImageURL: imageURL, + ImageLength: imageLength, + CloudEnvironment: in.InstallConfig.Azure.CloudName, + AllowSharedKeyAccess: sharedKey, + TokenCredential: session.TokenCreds, + StorageAccountName: storageAccountName, + StorageAccountKeys: storageAccountKeys, + CloudConfiguration: p.CloudConfiguration, }) if err != nil { return err @@ -414,8 +428,8 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput GalleryName: galleryName, Region: platform.Region, Tags: tags, - TokenCredential: tokenCredential, - CloudConfiguration: cloudConfiguration, + TokenCredential: p.TokenCredential, + CloudConfiguration: p.CloudConfiguration, }) if err != nil { return err @@ -433,8 +447,8 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput Offer: "rhcos", SKU: "basic", Tags: tags, - TokenCredential: tokenCredential, - CloudConfiguration: cloudConfiguration, + TokenCredential: p.TokenCredential, + CloudConfiguration: p.CloudConfiguration, Architecture: architecture, OSType: armcompute.OperatingSystemTypesLinux, OSState: armcompute.OperatingSystemStateTypesGeneralized, @@ -462,8 +476,8 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput Offer: "rhcos-gen2", SKU: "gen2", Tags: tags, - TokenCredential: tokenCredential, - CloudConfiguration: cloudConfiguration, + TokenCredential: p.TokenCredential, + CloudConfiguration: p.CloudConfiguration, Architecture: architecture, OSType: armcompute.OperatingSystemTypesLinux, OSState: armcompute.OperatingSystemStateTypesGeneralized, @@ -507,10 +521,10 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput } } - networkClientFactory, err := armnetwork.NewClientFactory(subscriptionID, session.TokenCreds, + networkClientFactory, err := armnetwork.NewClientFactory(subscriptionID, p.TokenCredential, &arm.ClientOptions{ ClientOptions: policy.ClientOptions{ - Cloud: cloudConfiguration, + Cloud: p.CloudConfiguration, }, }, ) @@ -830,7 +844,6 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([] bootstrapIgnData := in.BootstrapIgnData subscriptionID := session.Credentials.SubscriptionID - cloudConfiguration := session.CloudConfig ignitionContainerName := "ignition" blobName := "bootstrap.ign" @@ -853,16 +866,50 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([] logrus.Debugf("BlobIgnitionContainer.ID=%s", *blobIgnitionContainer.ID) sasURL := "" + now := time.Now().UTC().Add(-10 * time.Second) + expiry := now.Add(1 * time.Hour) + info := service.KeyInfo{ + Start: to.Ptr(now.UTC().Format(sas.TimeFormat)), + Expiry: to.Ptr(expiry.UTC().Format(sas.TimeFormat)), + } + serviceClient, err := service.NewClient(fmt.Sprintf("https://%s.blob.%s/", p.StorageAccountName, session.Environment.StorageEndpointSuffix), + session.TokenCreds, + &service.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: p.CloudConfiguration, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to create service client: %w", err) + } + + sharedKey := true + if in.InstallConfig.Config.Azure.AllowSharedKeyAccess != nil { + sharedKey = *in.InstallConfig.Config.Azure.AllowSharedKeyAccess + } if in.InstallConfig.Config.Azure.CustomerManagedKey == nil { logrus.Debugf("Creating a Block Blob for ignition shim") sasURL, err = CreateBlockBlob(ctx, &CreateBlockBlobInput{ - StorageURL: p.StorageURL, - BlobURL: blobURL, - StorageAccountName: p.StorageAccountName, - StorageAccountKeys: p.StorageAccountKeys, - CloudConfiguration: cloudConfiguration, - BootstrapIgnData: bootstrapIgnData, + StorageURL: p.StorageURL, + BlobURL: blobURL, + AuthType: session.AuthType, + TokenCredential: session.TokenCreds, + StorageAccountName: p.StorageAccountName, + StorageAccountKeys: p.StorageAccountKeys, + AllowSharedKeyAccess: sharedKey, + CloudConfiguration: p.CloudConfiguration, + BootstrapIgnData: bootstrapIgnData, + CloudEnvironment: in.InstallConfig.Azure.CloudName, + ContainerName: ignitionContainerName, + BlobName: blobName, + StorageSuffix: session.Environment.StorageEndpointSuffix, + ARMEndpoint: in.InstallConfig.Azure.ARMEndpoint, + Session: session, + Region: in.InstallConfig.Config.Azure.Region, + Tags: p.Tags, + ResourceGroupName: p.ResourceGroupName, }) if err != nil { return nil, fmt.Errorf("failed to create BlockBlob for ignition shim: %w", err) @@ -875,19 +922,40 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([] } sasURL, err = CreatePageBlob(ctx, &CreatePageBlobInput{ - StorageURL: p.StorageURL, - BlobURL: blobURL, - ImageURL: "", - StorageAccountName: p.StorageAccountName, - BootstrapIgnData: bootstrapIgnData, - ImageLength: lengthBootstrapFile, - StorageAccountKeys: p.StorageAccountKeys, - CloudConfiguration: cloudConfiguration, + StorageURL: p.StorageURL, + BlobURL: blobURL, + ImageURL: "", + CloudEnvironment: in.InstallConfig.Azure.CloudName, + AllowSharedKeyAccess: sharedKey, + TokenCredential: session.TokenCreds, + StorageAccountName: p.StorageAccountName, + BootstrapIgnData: bootstrapIgnData, + ImageLength: lengthBootstrapFile, + StorageAccountKeys: p.StorageAccountKeys, + CloudConfiguration: p.CloudConfiguration, }) if err != nil { return nil, fmt.Errorf("failed to create PageBlob for ignition shim: %w", err) } } + if sasURL == "" && !sharedKey { + udc, err := serviceClient.GetUserDelegationCredential(context.Background(), info, nil) + if err != nil { + return nil, fmt.Errorf("failed to create user delegation credentials: %w", err) + } + sasQueryParams, err := sas.BlobSignatureValues{ + Protocol: sas.ProtocolHTTPS, + StartTime: time.Now().UTC().Add(time.Second * -10), + ExpiryTime: time.Now().UTC().Add(1 * time.Hour), + Permissions: to.Ptr(sas.ContainerPermissions{Read: true}).String(), + ContainerName: "ignition", + BlobName: blobName, + }.SignWithUserDelegation(udc) + if err != nil { + return nil, fmt.Errorf("failed to sign blob %s: %w", blobURL, err) + } + sasURL = fmt.Sprintf("https://%s.blob.%s/ignition/%s?%s", p.StorageAccountName, session.Environment.StorageEndpointSuffix, blobName, sasQueryParams.Encode()) + } ignShim, err := bootstrap.GenerateIgnitionShimWithCertBundleAndProxy(sasURL, in.InstallConfig.Config.AdditionalTrustBundle, in.InstallConfig.Config.Proxy) if err != nil { return nil, fmt.Errorf("failed to create ignition shim: %w", err) diff --git a/pkg/infrastructure/azure/storage.go b/pkg/infrastructure/azure/storage.go index 36a05637a17..0a872bab687 100644 --- a/pkg/infrastructure/azure/storage.go +++ b/pkg/infrastructure/azure/storage.go @@ -21,6 +21,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service" "github.com/sirupsen/logrus" azic "github.com/openshift/installer/pkg/asset/installconfig/azure" @@ -35,16 +36,17 @@ var ( // CreateStorageAccountInput contains the input parameters for creating a // storage account. type CreateStorageAccountInput struct { - SubscriptionID string - ResourceGroupName string - StorageAccountName string - Region string - AuthType azic.AuthenticationType - Tags map[string]*string - CustomerManagedKey *aztypes.CustomerManagedKey - CloudName aztypes.CloudEnvironment - TokenCredential azcore.TokenCredential - CloudConfiguration cloud.Configuration + SubscriptionID string + ResourceGroupName string + StorageAccountName string + Region string + AuthType azic.AuthenticationType + AllowSharedKeyAccess bool + Tags map[string]*string + CustomerManagedKey *aztypes.CustomerManagedKey + CloudName aztypes.CloudEnvironment + TokenCredential azcore.TokenCredential + CloudConfiguration cloud.Configuration } // CreateStorageAccountOutput contains the return values after creating a @@ -61,7 +63,6 @@ func CreateStorageAccount(ctx context.Context, in *CreateStorageAccountInput) (* minimumTLSVersion := armstorage.MinimumTLSVersionTLS10 cloudConfiguration := in.CloudConfiguration - /* XXX: Do we support other clouds? */ switch in.CloudName { case aztypes.PublicCloud: minimumTLSVersion = armstorage.MinimumTLSVersionTLS12 @@ -69,10 +70,7 @@ func CreateStorageAccount(ctx context.Context, in *CreateStorageAccountInput) (* minimumTLSVersion = armstorage.MinimumTLSVersionTLS12 } - allowSharedKeyAccess := true - if in.AuthType == azic.ManagedIdentityAuth { - allowSharedKeyAccess = false - } + allowSharedKeyAccess := in.CloudName == aztypes.StackCloud || in.AllowSharedKeyAccess storageClientFactory, err := armstorage.NewClientFactory( in.SubscriptionID, @@ -228,14 +226,17 @@ func CreateBlobContainer(ctx context.Context, in *CreateBlobContainerInput) (*Cr // CreatePageBlobInput containers the input parameters used for creating a page // blob. type CreatePageBlobInput struct { - StorageURL string - BlobURL string - ImageURL string - StorageAccountName string - BootstrapIgnData []byte - ImageLength int64 - StorageAccountKeys []armstorage.AccountKey - CloudConfiguration cloud.Configuration + StorageURL string + BlobURL string + ImageURL string + StorageAccountName string + AllowSharedKeyAccess bool + CloudEnvironment aztypes.CloudEnvironment + BootstrapIgnData []byte + ImageLength int64 + TokenCredential azcore.TokenCredential + StorageAccountKeys []armstorage.AccountKey + CloudConfiguration cloud.Configuration } // CreatePageBlobOutput contains the return values after creating a page blob. @@ -246,26 +247,43 @@ type CreatePageBlobOutput struct { // CreatePageBlob creates a blob and uploads a file from a URL to it. func CreatePageBlob(ctx context.Context, in *CreatePageBlobInput) (string, error) { - logrus.Debugf("Getting page blob credentials") - - // XXX: Should try all of them until one is successful - sharedKeyCredential, err := azblob.NewSharedKeyCredential(in.StorageAccountName, *in.StorageAccountKeys[0].Value) - if err != nil { - return "", fmt.Errorf("failed to get shared credentials for storage account: %w", err) - } - logrus.Debugf("Getting page blob client") - pageBlobClient, err := pageblob.NewClientWithSharedKeyCredential( - in.BlobURL, - sharedKeyCredential, - &pageblob.ClientOptions{ - ClientOptions: azcore.ClientOptions{ - Cloud: in.CloudConfiguration, + + var pageBlobClient *pageblob.Client + var err error + if in.AllowSharedKeyAccess { + if len(in.StorageAccountKeys) == 0 || in.StorageAccountKeys[0].Value == nil { + return "", fmt.Errorf("missing storage account key for shared-key page blob upload") + } + sharedKeyCredential, err := azblob.NewSharedKeyCredential(in.StorageAccountName, *in.StorageAccountKeys[0].Value) + if err != nil { + return "", fmt.Errorf("failed to get shared credentials for storage account: %w", err) + } + pageBlobClient, err = pageblob.NewClientWithSharedKeyCredential( + in.BlobURL, + sharedKeyCredential, + &pageblob.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: in.CloudConfiguration, + }, }, - }, - ) - if err != nil { - return "", fmt.Errorf("failed to get page blob client: %w", err) + ) + if err != nil { + return "", fmt.Errorf("failed to get page blob client: %w", err) + } + } else { + pageBlobClient, err = pageblob.NewClient( + in.BlobURL, + in.TokenCredential, + &pageblob.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: in.CloudConfiguration, + }, + }, + ) + if err != nil { + return "", fmt.Errorf("failed to get page blob client: %w", err) + } } logrus.Debugf("Creating Page blob and uploading image to it") @@ -297,13 +315,14 @@ func CreatePageBlob(ctx context.Context, in *CreatePageBlobInput) (string, error return "", fmt.Errorf("failed to upload page blob image from URL %s: %w", in.ImageURL, err) } } - - // Is this addition OK for when CreatePageBlob() is called from InfraReady() - sasURL, err := pageBlobClient.GetSASURL(sas.BlobPermissions{Read: true}, time.Now().Add(time.Minute*60), &blob.GetSASURLOptions{}) - if err != nil { - return "", fmt.Errorf("failed to get Page Blob SAS URL: %w", err) + if in.CloudEnvironment == aztypes.StackCloud || in.AllowSharedKeyAccess { + sasURL, err := pageBlobClient.GetSASURL(sas.BlobPermissions{Read: true}, time.Now().Add(time.Minute*60), &blob.GetSASURLOptions{}) + if err != nil { + return "", fmt.Errorf("failed to get Page Blob SAS URL: %w", err) + } + return sasURL, nil } - return sasURL, nil + return "", nil } func doUploadPages(ctx context.Context, pageBlobClient *pageblob.Client, imageData []byte, imageLength int64) error { @@ -445,12 +464,25 @@ func doUploadPagesFromURL(ctx context.Context, pageBlobClient *pageblob.Client, // CreateBlockBlobInput containers the input parameters used for creating a // block blob. type CreateBlockBlobInput struct { - StorageURL string - BlobURL string - StorageAccountName string - BootstrapIgnData []byte - StorageAccountKeys []armstorage.AccountKey - CloudConfiguration cloud.Configuration + StorageURL string + BlobURL string + StorageAccountName string + AllowSharedKeyAccess bool + BootstrapIgnData []byte + AuthType azic.AuthenticationType + TokenCredential azcore.TokenCredential + StorageAccountKeys []armstorage.AccountKey + CloudConfiguration cloud.Configuration + CloudEnvironment aztypes.CloudEnvironment + ContainerName string + BlobName string + StorageSuffix string + ARMEndpoint string + Region string + ResourceGroupName string + Session *azic.Session + Tags map[string]*string + UserDelegatedCreds *service.UserDelegationCredential } // CreateBlockBlobOutput contains the return values after creating a block @@ -462,15 +494,83 @@ type CreateBlockBlobOutput struct { // CreateBlockBlob creates a block blob and uploads a file from a URL to it. func CreateBlockBlob(ctx context.Context, in *CreateBlockBlobInput) (string, error) { - logrus.Debugf("Getting block blob credentials") + if in.CloudEnvironment == aztypes.StackCloud { + return createBlockBlobOnStack(ctx, in) + } + return createBlockBlob(ctx, in) +} + +func createBlockBlob(ctx context.Context, in *CreateBlockBlobInput) (string, error) { + logrus.Debugf("Getting block blob client") + var blockBlobClient *blockblob.Client + var err error + if in.AllowSharedKeyAccess { + if len(in.StorageAccountKeys) == 0 || in.StorageAccountKeys[0].Value == nil { + return "", fmt.Errorf("missing storage account key for shared-key block blob upload") + } + sharedKeyCredential, err := azblob.NewSharedKeyCredential(in.StorageAccountName, *in.StorageAccountKeys[0].Value) + if err != nil { + return "", fmt.Errorf("failed to get shared credentials for storage account: %w", err) + } + blockBlobClient, err = blockblob.NewClientWithSharedKeyCredential( + in.BlobURL, + sharedKeyCredential, + &blockblob.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: in.CloudConfiguration, + }, + }, + ) + if err != nil { + return "", fmt.Errorf("failed to get block blob client: %w", err) + } + } else { + blockBlobClient, err = blockblob.NewClient( + in.BlobURL, + in.TokenCredential, + &blockblob.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: in.CloudConfiguration, + }, + }, + ) + if err != nil { + return "", fmt.Errorf("failed to get block blob client: %w", err) + } + } + + logrus.Debugf("Creating block blob") + + accessTier := blob.AccessTierHot + _, err = blockBlobClient.Upload(ctx, streaming.NopCloser(bytes.NewReader(in.BootstrapIgnData)), &blockblob.UploadOptions{ + Tier: &accessTier, + }) + if err != nil { + return "", fmt.Errorf("failed to create block blob: %w", err) + } + if in.AllowSharedKeyAccess { + sasURL, err := blockBlobClient.GetSASURL(sas.BlobPermissions{Read: true}, time.Now().Add(time.Minute*60), &blob.GetSASURLOptions{}) + if err != nil { + return "", fmt.Errorf("failed to get SAS URL: %w", err) + } + return sasURL, nil + } + + return "", nil +} + +func createBlockBlobOnStack(ctx context.Context, in *CreateBlockBlobInput) (string, error) { + logrus.Debugf("Getting block blob credentials for stack") - // XXX: Should try all of them until one is successful + if len(in.StorageAccountKeys) == 0 || in.StorageAccountKeys[0].Value == nil { + return "", fmt.Errorf("missing storage account key for shared-key block blob upload on stack") + } sharedKeyCredential, err := azblob.NewSharedKeyCredential(in.StorageAccountName, *in.StorageAccountKeys[0].Value) if err != nil { - return "", fmt.Errorf("failed to get shared crdentials for storage account: %w", err) + return "", fmt.Errorf("failed to get shared credentials for storage account: %w", err) } - logrus.Debugf("Getting block blob client") + logrus.Debugf("Getting block blob client for stack") blockBlobClient, err := blockblob.NewClientWithSharedKeyCredential( in.BlobURL, sharedKeyCredential, @@ -481,10 +581,10 @@ func CreateBlockBlob(ctx context.Context, in *CreateBlockBlobInput) (string, err }, ) if err != nil { - return "", fmt.Errorf("failed to get page blob client: %w", err) + return "", fmt.Errorf("failed to get block blob client: %w", err) } - logrus.Debugf("Creating block blob") + logrus.Debugf("Creating block blob on stack") accessTier := blob.AccessTierHot _, err = blockBlobClient.Upload(ctx, streaming.NopCloser(bytes.NewReader(in.BootstrapIgnData)), &blockblob.UploadOptions{ @@ -498,7 +598,6 @@ func CreateBlockBlob(ctx context.Context, in *CreateBlockBlobInput) (string, err if err != nil { return "", fmt.Errorf("failed to get SAS URL: %w", err) } - return sasURL, nil } diff --git a/pkg/types/azure/platform.go b/pkg/types/azure/platform.go index bedbc4abfbf..45285271440 100644 --- a/pkg/types/azure/platform.go +++ b/pkg/types/azure/platform.go @@ -43,6 +43,13 @@ type Platform struct { // +optional BaseDomainResourceGroupName string `json:"baseDomainResourceGroupName,omitempty"` + // AllowSharedKeyAccess specifies if shared access key should be enabled for the storage account. + // Default value is true. + // Disabling this will require a new permission "Storage Blob Data Contributor" in azure. + // + // +optional + AllowSharedKeyAccess *bool `json:"allowSharedKeyAccess,omitempty"` + // DefaultMachinePlatform is the default configuration used when // installing on Azure for machine pools which do not define their own // platform configuration. diff --git a/pkg/types/azure/validation/platform.go b/pkg/types/azure/validation/platform.go index ea5f55fe386..cc2035cbe4c 100644 --- a/pkg/types/azure/validation/platform.go +++ b/pkg/types/azure/validation/platform.go @@ -120,6 +120,9 @@ func ValidatePlatform(p *azure.Platform, publish types.PublishingStrategy, fldPa // check if configured userTags are valid. allErrs = append(allErrs, validateUserTags(p.UserTags, fldPath.Child("userTags"))...) + if p.CloudName == azure.StackCloud && p.AllowSharedKeyAccess != nil && !*p.AllowSharedKeyAccess { + allErrs = append(allErrs, field.Invalid(fldPath.Child("allowSharedKeyAccess"), p.AllowSharedKeyAccess, "disabling shared access key creation is unsupported in Azure stack hub")) + } switch cloud := p.CloudName; cloud { case azure.StackCloud: allErrs = append(allErrs, validateAzureStack(p, fldPath)...)