Skip to content

Disable and remove use of local/shared key auth for SAs #4122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 8, 2025
Merged
28 changes: 27 additions & 1 deletion pkg/deploy/assets/rp-production-global.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@
"kind": "StorageV2",
"properties": {
"allowBlobPublicAccess": false,
"minimumTlsVersion": "TLS1_2"
"minimumTlsVersion": "TLS1_2",
"allowSharedKeyAccess": false
},
"tags": {},
"location": "[resourceGroup().location]",
Expand All @@ -128,6 +129,31 @@
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('rpVersionStorageAccountName'))]"
]
},
{
"properties": {
"metadata": null
},
"name": "[concat(parameters('rpVersionStorageAccountName'), '/default', '/$web')]",
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-09-01",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('rpVersionStorageAccountName'))]"
]
},
{
"name": "[concat(parameters('rpVersionStorageAccountName'), '/default/$web/Microsoft.Authorization/', guid(parameters('rpVersionStorageAccountName')))]",
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"properties": {
"scope": "[concat(resourceId('Microsoft.Storage/storageAccounts', parameters('rpVersionStorageAccountName')), '/blobServices/default/containers/$web')]",
"roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"principalId": "[parameters('globalDevopsServicePrincipalId')]",
"principalType": "ServicePrincipal"
},
"apiVersion": "2018-09-01-preview",
"dependsOn": [
"[concat(resourceId('Microsoft.Storage/storageAccounts', parameters('rpVersionStorageAccountName')), '/blobServices/default/containers/$web')]"
]
}
]
}
9 changes: 9 additions & 0 deletions pkg/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/Azure/ARO-RP/pkg/deploy/vmsscleaner"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/util/arm"
"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/azblob"
"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/azsecrets"
"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/authorization"
"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/compute"
Expand Down Expand Up @@ -62,6 +63,7 @@ type deployer struct {
clusterKeyvault azsecrets.Client
portalKeyvault azsecrets.Client
serviceKeyvault azsecrets.Client
blobsClient azblob.BlobsClient

config *RPConfig
version string
Expand Down Expand Up @@ -102,6 +104,12 @@ func New(ctx context.Context, log *logrus.Entry, _env env.Core, config *RPConfig
*into = client
}

serviceUrl := fmt.Sprintf("https://%s.blob.%s", *config.Configuration.RPVersionStorageAccountName, _env.Environment().StorageEndpointSuffix)
blobsClient, err := azblob.NewBlobsClientUsingEntra(serviceUrl, tokenCredential, _env.Environment().ArmClientOptions())
if err != nil {
return nil, fmt.Errorf("failure to instantiate blobs client using SAS: %v", err)
}

return &deployer{
log: log,
env: _env,
Expand All @@ -124,6 +132,7 @@ func New(ctx context.Context, log *logrus.Entry, _env env.Core, config *RPConfig
clusterKeyvault: clusterKeyVault,
portalKeyvault: portalKeyVault,
serviceKeyvault: serviceKeyVault,
blobsClient: blobsClient,

config: config,
version: version,
Expand Down
14 changes: 14 additions & 0 deletions pkg/deploy/generator/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package generator
// Licensed under the Apache License 2.0.

import (
"fmt"

mgmtdns "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
mgmtkeyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2019-09-01/keyvault"
mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network"
Expand Down Expand Up @@ -105,6 +107,18 @@ func (g *generator) storageAccount(name string, accountProperties *mgmtstorage.A
}
}

func (g *generator) storageAccountBlobContainer(name string, storageAccountName string, containerProperties *mgmtstorage.ContainerProperties) *arm.Resource {
return &arm.Resource{
Resource: &mgmtstorage.BlobContainer{
Name: to.StringPtr("[" + name + "]"),
Type: to.StringPtr("Microsoft.Storage/storageAccounts/blobServices/containers"),
ContainerProperties: containerProperties,
},
DependsOn: []string{fmt.Sprintf("[resourceId('Microsoft.Storage/storageAccounts', %s)]", storageAccountName)},
APIVersion: azureclient.APIVersion("Microsoft.Storage"),
}
}

func (g *generator) virtualNetwork(name, addressPrefix string, subnets *[]mgmtnetwork.Subnet, condition interface{}, dependsOn []string) *arm.Resource {
return &arm.Resource{
Resource: &mgmtnetwork.VirtualNetwork{
Expand Down
1 change: 1 addition & 0 deletions pkg/deploy/generator/resources_miwi_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
var (
storageAccountName string = "parameters('oidcStorageAccountName')"
resourceTypeStorageAccount string = "Microsoft.Storage/storageAccounts"
resourceTypeBlobContainer string = "blobServices/containers"

SharedMSIKeyVaultName = "concat(take(resourceGroup().name,10), '" + env.SharedMSIKeyVaultNameSuffix + "')"
)
Expand Down
13 changes: 13 additions & 0 deletions pkg/deploy/generator/resources_rp.go
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,7 @@ func (g *generator) rpVersionStorageAccount() []*arm.Resource {
&mgmtstorage.AccountProperties{
AllowBlobPublicAccess: to.BoolPtr(false),
MinimumTLSVersion: mgmtstorage.MinimumTLSVersionTLS12,
AllowSharedKeyAccess: to.BoolPtr(false),
},
map[string]*string{},
),
Expand All @@ -1560,5 +1561,17 @@ func (g *generator) rpVersionStorageAccount() []*arm.Resource {
storageAccountName,
fmt.Sprintf("concat(%s, '/Microsoft.Authorization/', guid(resourceId('%s', %s)))", storageAccountName, resourceTypeStorageAccount, storageAccountName),
),
g.storageAccountBlobContainer(
fmt.Sprintf("concat(%s, '/default', '/$web')", storageAccountName),
storageAccountName,
&mgmtstorage.ContainerProperties{},
),
rbac.ResourceRoleAssignmentWithScope(
rbac.RoleStorageBlobDataContributor,
"parameters('globalDevopsServicePrincipalId')",
fmt.Sprintf("%s/%s", resourceTypeStorageAccount, resourceTypeBlobContainer),
fmt.Sprintf("concat(resourceId('Microsoft.Storage/storageAccounts', %s), '/blobServices/default/containers/$web')", storageAccountName),
fmt.Sprintf("concat(%s, '/default/$web/Microsoft.Authorization/', guid(%s))", storageAccountName, storageAccountName),
),
}
}
29 changes: 2 additions & 27 deletions pkg/deploy/saveversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,18 @@ package deploy
import (
"context"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
mgmtstorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-09-01/storage"
"github.com/Azure/go-autorest/autorest/date"

"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/azblob"
"github.com/Azure/ARO-RP/pkg/util/pointerutils"
)

// SaveVersion for current location in shared storage account for environment
func (d *deployer) SaveVersion(ctx context.Context) error {
d.log.Printf("saving RP version %s deployed in %s to storage account %s", d.version, d.config.Location, *d.config.Configuration.RPVersionStorageAccountName)
t := time.Now().UTC().Truncate(time.Second)
res, err := d.globalaccounts.ListAccountSAS(
ctx, *d.config.Configuration.GlobalResourceGroupName, *d.config.Configuration.RPVersionStorageAccountName, mgmtstorage.AccountSasParameters{
Services: mgmtstorage.ServicesB,
ResourceTypes: mgmtstorage.SignedResourceTypesO + mgmtstorage.SignedResourceTypesS,
Permissions: mgmtstorage.PermissionsC + mgmtstorage.PermissionsW, // create and write
Protocols: mgmtstorage.HTTPProtocolHTTPS,
SharedAccessStartTime: &date.Time{Time: t},
SharedAccessExpiryTime: &date.Time{Time: t.Add(24 * time.Hour)},
})
if err != nil {
return err
}

d.log.Infof("instantiating blobs client using SAS token")
sasUrl := fmt.Sprintf("https://%s.blob.%s/?%s", *d.config.Configuration.RPVersionStorageAccountName, d.env.Environment().StorageEndpointSuffix, *res.AccountSasToken)
blobsClient, err := azblob.NewBlobsClientUsingSAS(sasUrl, d.env.Environment().ArmClientOptions())
if err != nil {
d.log.Errorf("failure to instantiate blobs client using SAS: %v", err)
return err
}

d.log.Infof("ensuring static web content is enabled")
_, err = blobsClient.ServiceClient().SetProperties(ctx, &service.SetPropertiesOptions{
_, err := d.blobsClient.ServiceClient().SetProperties(ctx, &service.SetPropertiesOptions{
StaticWebsite: &service.StaticWebsite{Enabled: pointerutils.ToPtr(true)},
})
if err != nil {
Expand All @@ -52,7 +27,7 @@ func (d *deployer) SaveVersion(ctx context.Context) error {

d.log.Infof("uploading RP version")
blobName := fmt.Sprintf("rpversion/%s", d.config.Location)
_, err = blobsClient.UploadBuffer(ctx, "$web", blobName, []byte(d.version), nil)
_, err = d.blobsClient.UploadBuffer(ctx, "$web", blobName, []byte(d.version), nil)
if err != nil {
d.log.Errorf("failure to upload version information: %v", err)
return err
Expand Down
2 changes: 2 additions & 0 deletions pkg/util/azureclient/azuresdk/azblob/blobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
)

// BlobsClient is a minimal interface for Azure BlobsClient
type BlobsClient interface {
DownloadStream(ctx context.Context, containerName string, blobName string, o *azblob.DownloadStreamOptions) (azblob.DownloadStreamResponse, error)
UploadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.UploadBufferOptions) (azblob.UploadBufferResponse, error)
DeleteBlob(ctx context.Context, containerName string, blobName string, o *azblob.DeleteBlobOptions) (azblob.DeleteBlobResponse, error)
ServiceClient() *service.Client
BlobsClientAddons
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/util/mocks/azureclient/azuresdk/azblob/blobs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions pkg/util/rbac/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ func ResourceRoleAssignmentWithName(roleID, spID, resourceType, resourceName, na
return r
}

func ResourceRoleAssignmentWithScope(roleID, spID, resourceType string, scope string, names string, condition ...interface{}) *arm.Resource {
r := &arm.Resource{
Resource: mgmtauthorization.RoleAssignment{
Name: to.StringPtr("[" + names + "]"),
Type: to.StringPtr(resourceType + "/providers/roleAssignments"),
RoleAssignmentPropertiesWithScope: &mgmtauthorization.RoleAssignmentPropertiesWithScope{
Scope: to.StringPtr("[" + scope + "]"),
RoleDefinitionID: to.StringPtr("[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '" + roleID + "')]"),
PrincipalID: to.StringPtr("[" + spID + "]"),
PrincipalType: mgmtauthorization.ServicePrincipal,
},
},
APIVersion: azureclient.APIVersion("Microsoft.Authorization"),
DependsOn: []string{
"[" + scope + "]",
},
}

if len(condition) > 0 {
r.Condition = condition[0]
}

return r
}

// ResourceGroupRoleAssignment returns a Resource granting roleID on the current
// resource group to spID. Argument spID must be a valid ARM expression, e.g.
// "'foo'" or "concat('foo')". Use this function in new ARM templates.
Expand Down
Loading