Skip to content

Commit 084c063

Browse files
Merge pull request #10575 from rna-afk/cherry_pick_sas_4.20
OCPBUGS-86552: azure: Add field in installconfig to disallow shared access key
2 parents b7add4f + f5d6c80 commit 084c063

6 files changed

Lines changed: 262 additions & 137 deletions

File tree

data/data/install.openshift.io_installconfigs.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4801,6 +4801,12 @@ spec:
48014801
azure:
48024802
description: Azure is the configuration used when installing on Azure.
48034803
properties:
4804+
allowSharedKeyAccess:
4805+
description: |-
4806+
AllowSharedKeyAccess specifies if shared access key should be enabled for the storage account.
4807+
Default value is true.
4808+
Disabling this will require a new permission "Storage Blob Data Contributor" in azure.
4809+
type: boolean
48044810
armEndpoint:
48054811
description: ARMEndpoint is the endpoint for the Azure API when
48064812
installing on Azure Stack.

pkg/explain/printer_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ cluster itself may not include these tags.
293293
}, {
294294
path: []string{"platform", "azure"},
295295
desc: `FIELDS:
296+
allowSharedKeyAccess <boolean>
297+
AllowSharedKeyAccess specifies if shared access key should be enabled for the storage account.
298+
Default value is true.
299+
Disabling this will require a new permission "Storage Blob Data Contributor" in azure.
300+
296301
armEndpoint <string>
297302
ARMEndpoint is the endpoint for the Azure API when installing on Azure Stack.
298303

pkg/infrastructure/azure/azure.go

Lines changed: 110 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
1818
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2"
1919
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
20+
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
21+
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
2022
"github.com/coreos/stream-metadata-go/arch"
2123
"github.com/sirupsen/logrus"
2224
corev1 "k8s.io/api/core/v1"
@@ -105,8 +107,8 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
105107
installConfig := in.InstallConfig.Config
106108
platform := installConfig.Platform.Azure
107109
subscriptionID := session.Credentials.SubscriptionID
108-
cloudConfiguration := session.CloudConfig
109-
tokenCredential := session.TokenCreds
110+
p.CloudConfiguration = session.CloudConfig
111+
p.TokenCredential = session.TokenCreds
110112
p.ResourceGroupName = platform.ClusterResourceGroupName(in.InfraID)
111113

112114
userTags := platform.UserTags
@@ -119,15 +121,15 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
119121

120122
opts := &arm.ClientOptions{
121123
ClientOptions: policy.ClientOptions{
122-
Cloud: cloudConfiguration,
124+
Cloud: p.CloudConfiguration,
123125
},
124126
}
125127
computeClientOpts := opts
126128
if platform.CloudName == aztypes.StackCloud {
127129
opts.APIVersion = stackAPIVersion
128130
computeClientOpts = &arm.ClientOptions{
129131
ClientOptions: policy.ClientOptions{
130-
Cloud: cloudConfiguration,
132+
Cloud: p.CloudConfiguration,
131133
APIVersion: stackComputeAPIVersion,
132134
},
133135
}
@@ -140,7 +142,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
140142
region: platform.Region,
141143
resourceGroupName: p.ResourceGroupName,
142144
subscriptionID: subscriptionID,
143-
tokenCredential: tokenCredential,
145+
tokenCredential: p.TokenCredential,
144146
infraID: in.InfraID,
145147
clientOpts: p.clientOptions,
146148
tags: p.Tags,
@@ -154,7 +156,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
154156

155157
// Creating a dummy nsg for existing vnets installation to appease the ingress operator.
156158
if in.InstallConfig.Config.Azure.VirtualNetwork != "" {
157-
networkClientFactory, err := armnetwork.NewClientFactory(subscriptionID, tokenCredential, p.clientOptions)
159+
networkClientFactory, err := armnetwork.NewClientFactory(subscriptionID, p.TokenCredential, p.clientOptions)
158160
if err != nil {
159161
return fmt.Errorf("failed to create azure network factory: %w", err)
160162
}
@@ -230,20 +232,29 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
230232
var storageClientFactory *armstorage.ClientFactory
231233
var storageAccountKeys []armstorage.AccountKey
232234

235+
sharedKey := true
236+
if in.InstallConfig.Config.Azure.AllowSharedKeyAccess != nil {
237+
sharedKey = *in.InstallConfig.Config.Azure.AllowSharedKeyAccess
238+
}
239+
if sharedKey {
240+
logrus.Info("Shared key access is enabled for storage account; Managed Identity-based access will not be used for blob operations")
241+
}
242+
233243
var createStorageAccountOutput *CreateStorageAccountOutput
234244
if platform.CloudName != aztypes.StackCloud {
235245
// Create storage account
236246
createStorageAccountOutput, err = CreateStorageAccount(ctx, &CreateStorageAccountInput{
237-
SubscriptionID: subscriptionID,
238-
ResourceGroupName: resourceGroupName,
239-
StorageAccountName: storageAccountName,
240-
CloudName: platform.CloudName,
241-
Region: platform.Region,
242-
AuthType: session.AuthType,
243-
Tags: tags,
244-
CustomerManagedKey: platform.CustomerManagedKey,
245-
TokenCredential: tokenCredential,
246-
ClientOpts: p.clientOptions,
247+
SubscriptionID: subscriptionID,
248+
ResourceGroupName: resourceGroupName,
249+
StorageAccountName: storageAccountName,
250+
CloudName: platform.CloudName,
251+
Region: platform.Region,
252+
AuthType: session.AuthType,
253+
AllowSharedKeyAccess: sharedKey,
254+
Tags: tags,
255+
CustomerManagedKey: platform.CustomerManagedKey,
256+
TokenCredential: p.TokenCredential,
257+
ClientOpts: p.clientOptions,
247258
})
248259
if err != nil {
249260
return err
@@ -276,13 +287,16 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
276287
logrus.Debugf("BlobContainer.ID=%s", *blobContainer.ID)
277288

278289
_, err = CreatePageBlob(ctx, &CreatePageBlobInput{
279-
StorageURL: storageURL,
280-
BlobURL: blobURL,
281-
ImageURL: imageURL,
282-
ImageLength: imageLength,
283-
StorageAccountName: storageAccountName,
284-
StorageAccountKeys: storageAccountKeys,
285-
ClientOpts: p.clientOptions,
290+
StorageURL: storageURL,
291+
BlobURL: blobURL,
292+
ImageURL: imageURL,
293+
ImageLength: imageLength,
294+
CloudEnvironment: in.InstallConfig.Azure.CloudName,
295+
AllowSharedKeyAccess: sharedKey,
296+
TokenCredential: session.TokenCreds,
297+
StorageAccountName: storageAccountName,
298+
StorageAccountKeys: storageAccountKeys,
299+
ClientOpts: p.clientOptions,
286300
})
287301
if err != nil {
288302
return err
@@ -295,7 +309,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
295309
GalleryName: galleryName,
296310
Region: platform.Region,
297311
Tags: tags,
298-
TokenCredential: tokenCredential,
312+
TokenCredential: p.TokenCredential,
299313
ClientOpts: p.clientOptions,
300314
})
301315
if err != nil {
@@ -314,7 +328,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
314328
Offer: "rhcos",
315329
SKU: "basic",
316330
Tags: tags,
317-
TokenCredential: tokenCredential,
331+
TokenCredential: p.TokenCredential,
318332
ClientOpts: p.clientOptions,
319333
Architecture: architecture,
320334
OSType: armcompute.OperatingSystemTypesLinux,
@@ -343,7 +357,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
343357
Offer: "rhcos-gen2",
344358
SKU: "gen2",
345359
Tags: tags,
346-
TokenCredential: tokenCredential,
360+
TokenCredential: p.TokenCredential,
347361
ClientOpts: p.clientOptions,
348362
Architecture: architecture,
349363
OSType: armcompute.OperatingSystemTypesLinux,
@@ -389,7 +403,7 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
389403
}
390404

391405
if installConfig.Azure.CloudName == aztypes.StackCloud {
392-
client, err := armcompute.NewImagesClient(subscriptionID, tokenCredential, p.computeClientOptions)
406+
client, err := armcompute.NewImagesClient(subscriptionID, p.TokenCredential, p.computeClientOptions)
393407
if err != nil {
394408
return fmt.Errorf("error creating stack managed images client: %w", err)
395409
}
@@ -427,7 +441,6 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
427441
lbClient: lbClient,
428442
tags: p.Tags,
429443
}
430-
431444
intLoadBalancer, err := updateInternalLoadBalancer(ctx, lbInput)
432445
if err != nil {
433446
return fmt.Errorf("failed to update internal load balancer: %w", err)
@@ -740,25 +753,50 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
740753
}
741754

742755
sasURL := ""
756+
now := time.Now().UTC().Add(-10 * time.Second)
757+
expiry := now.Add(1 * time.Hour)
758+
info := service.KeyInfo{
759+
Start: to.Ptr(now.UTC().Format(sas.TimeFormat)),
760+
Expiry: to.Ptr(expiry.UTC().Format(sas.TimeFormat)),
761+
}
743762

763+
serviceClient, err := service.NewClient(fmt.Sprintf("https://%s.blob.%s/", p.StorageAccountName, session.Environment.StorageEndpointSuffix),
764+
session.TokenCreds,
765+
&service.ClientOptions{
766+
ClientOptions: azcore.ClientOptions{
767+
Cloud: p.CloudConfiguration,
768+
},
769+
},
770+
)
771+
if err != nil {
772+
return nil, fmt.Errorf("failed to create service client: %w", err)
773+
}
774+
775+
sharedKey := true
776+
if in.InstallConfig.Config.Azure.AllowSharedKeyAccess != nil {
777+
sharedKey = *in.InstallConfig.Config.Azure.AllowSharedKeyAccess
778+
}
744779
if in.InstallConfig.Config.Azure.CustomerManagedKey == nil {
745780
logrus.Debugf("Creating a Block Blob for ignition shim")
746781
sasURL, err = CreateBlockBlob(ctx, &CreateBlockBlobInput{
747-
StorageURL: p.StorageURL,
748-
BlobURL: blobURL,
749-
StorageAccountName: p.StorageAccountName,
750-
StorageAccountKeys: p.StorageAccountKeys,
751-
ClientOpts: p.clientOptions,
752-
BootstrapIgnData: bootstrapIgnData,
753-
CloudEnvironment: in.InstallConfig.Azure.CloudName,
754-
ContainerName: ignitionContainerName,
755-
BlobName: blobName,
756-
StorageSuffix: session.Environment.StorageEndpointSuffix,
757-
ARMEndpoint: in.InstallConfig.Azure.ARMEndpoint,
758-
Session: session,
759-
Region: in.InstallConfig.Config.Azure.Region,
760-
Tags: p.Tags,
761-
ResourceGroupName: p.ResourceGroupName,
782+
StorageURL: p.StorageURL,
783+
BlobURL: blobURL,
784+
AuthType: session.AuthType,
785+
TokenCredential: session.TokenCreds,
786+
StorageAccountName: p.StorageAccountName,
787+
StorageAccountKeys: p.StorageAccountKeys,
788+
AllowSharedKeyAccess: sharedKey,
789+
ClientOpts: p.clientOptions,
790+
BootstrapIgnData: bootstrapIgnData,
791+
CloudEnvironment: in.InstallConfig.Azure.CloudName,
792+
ContainerName: ignitionContainerName,
793+
BlobName: blobName,
794+
StorageSuffix: session.Environment.StorageEndpointSuffix,
795+
ARMEndpoint: in.InstallConfig.Azure.ARMEndpoint,
796+
Session: session,
797+
Region: in.InstallConfig.Config.Azure.Region,
798+
Tags: p.Tags,
799+
ResourceGroupName: p.ResourceGroupName,
762800
})
763801
if err != nil {
764802
return nil, fmt.Errorf("failed to create BlockBlob for ignition shim: %w", err)
@@ -771,19 +809,40 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
771809
}
772810

773811
sasURL, err = CreatePageBlob(ctx, &CreatePageBlobInput{
774-
StorageURL: p.StorageURL,
775-
BlobURL: blobURL,
776-
ImageURL: "",
777-
StorageAccountName: p.StorageAccountName,
778-
BootstrapIgnData: bootstrapIgnData,
779-
ImageLength: lengthBootstrapFile,
780-
StorageAccountKeys: p.StorageAccountKeys,
781-
ClientOpts: p.clientOptions,
812+
StorageURL: p.StorageURL,
813+
BlobURL: blobURL,
814+
ImageURL: "",
815+
CloudEnvironment: in.InstallConfig.Azure.CloudName,
816+
AllowSharedKeyAccess: sharedKey,
817+
TokenCredential: session.TokenCreds,
818+
StorageAccountName: p.StorageAccountName,
819+
BootstrapIgnData: bootstrapIgnData,
820+
ImageLength: lengthBootstrapFile,
821+
StorageAccountKeys: p.StorageAccountKeys,
822+
ClientOpts: p.clientOptions,
782823
})
783824
if err != nil {
784825
return nil, fmt.Errorf("failed to create PageBlob for ignition shim: %w", err)
785826
}
786827
}
828+
if sasURL == "" && !sharedKey {
829+
udc, err := serviceClient.GetUserDelegationCredential(context.Background(), info, nil)
830+
if err != nil {
831+
return nil, fmt.Errorf("failed to create user delegation credentials: %w", err)
832+
}
833+
sasQueryParams, err := sas.BlobSignatureValues{
834+
Protocol: sas.ProtocolHTTPS,
835+
StartTime: time.Now().UTC().Add(time.Second * -10),
836+
ExpiryTime: time.Now().UTC().Add(1 * time.Hour),
837+
Permissions: to.Ptr(sas.ContainerPermissions{Read: true}).String(),
838+
ContainerName: "ignition",
839+
BlobName: blobName,
840+
}.SignWithUserDelegation(udc)
841+
if err != nil {
842+
return nil, fmt.Errorf("failed to sign blob %s: %w", blobURL, err)
843+
}
844+
sasURL = fmt.Sprintf("https://%s.blob.%s/ignition/%s?%s", p.StorageAccountName, session.Environment.StorageEndpointSuffix, blobName, sasQueryParams.Encode())
845+
}
787846
ignShim, err := bootstrap.GenerateIgnitionShimWithCertBundleAndProxy(sasURL, in.InstallConfig.Config.AdditionalTrustBundle, in.InstallConfig.Config.Proxy)
788847
if err != nil {
789848
return nil, fmt.Errorf("failed to create ignition shim: %w", err)

0 commit comments

Comments
 (0)