Skip to content

Commit

Permalink
pkg/storage/azure: configure registry with workload identity
Browse files Browse the repository at this point in the history
  • Loading branch information
flavianmissi committed Jun 20, 2023
1 parent c209320 commit 8bb474a
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
24 changes: 22 additions & 2 deletions pkg/storage/azure/azure.go
Expand Up @@ -406,7 +406,8 @@ func (d *driver) ConfigEnv() (envs envvar.List, err error) {
}

key := cfg.AccountKey
if key == "" {
federated_token := cfg.FederatedTokenFile
if key == "" && federated_token == "" {
storageAccountsClient, err := d.storageAccountsClient(cfg, environment)
if err != nil {
return nil, err
Expand All @@ -418,11 +419,30 @@ func (d *driver) ConfigEnv() (envs envvar.List, err error) {
}
}

if key != "" {
envs = append(envs,
envvar.EnvVar{Name: "REGISTRY_STORAGE_AZURE_ACCOUNTKEY", Value: key, Secret: true},
)
}

// the AZURE_ vars used to configure workload identity are taken
// from https://github.com/distribution/distribution/blob/6a57630cf40122000083e60bcb7e97c50a904c5e/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go#LL86C43-L86C63
if federated_token != "" {
envs = append(envs,
// NOTE: these env vars are not prepended with REGISTRY_STORAGE
// because they're exported for the azure-sdk, not the registry.
// we do this as a transparent way to support workload identity in the registry.
envvar.EnvVar{Name: "AZURE_CLIENT_ID", Value: cfg.ClientID},
envvar.EnvVar{Name: "AZURE_TENANT_ID", Value: cfg.TenantID},
envvar.EnvVar{Name: "AZURE_FEDERATED_TOKEN_FILE", Value: federated_token},
envvar.EnvVar{Name: "AZURE_AUTHORITY_HOST", Value: environment.ActiveDirectoryEndpoint},
)
}

envs = append(envs,
envvar.EnvVar{Name: "REGISTRY_STORAGE", Value: "azure"},
envvar.EnvVar{Name: "REGISTRY_STORAGE_AZURE_CONTAINER", Value: d.Config.Container},
envvar.EnvVar{Name: "REGISTRY_STORAGE_AZURE_ACCOUNTNAME", Value: d.Config.AccountName},
envvar.EnvVar{Name: "REGISTRY_STORAGE_AZURE_ACCOUNTKEY", Value: key, Secret: true},
)

if d.Config.CloudName != "" {
Expand Down
77 changes: 77 additions & 0 deletions pkg/storage/azure/azure_test.go
Expand Up @@ -339,6 +339,83 @@ func TestConfigEnv(t *testing.T) {
}
}

func TestConfigEnvWorkloadIdentity(t *testing.T) {
ctx := context.Background()

config := &imageregistryv1.ImageRegistryConfigStorageAzure{}

testBuilder := cirofake.NewFixturesBuilder()
testBuilder.AddInfraConfig(&configv1.Infrastructure{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
Status: configv1.InfrastructureStatus{
PlatformStatus: &configv1.PlatformStatus{
Type: configv1.AzurePlatformType,
Azure: &configv1.AzurePlatformStatus{
ResourceGroupName: "resourcegroup",
CloudName: configv1.AzureUSGovernmentCloud,
},
},
},
})
testBuilder.AddSecrets(&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: defaults.CloudCredentialsName,
Namespace: defaults.ImageRegistryOperatorNamespace,
},
Data: map[string][]byte{
"azure_client_id": []byte("client_id"),
"azure_federated_token_file": []byte("/path/to/file"),
"azure_region": []byte("region"),
"azure_subscription_id": []byte("subscription_id"),
"azure_tenant_id": []byte("tenant_id"),
},
})

listers := testBuilder.BuildListers()

authorizer := autorest.NullAuthorizer{}
sender := mocks.NewSender()
sender.AppendResponse(mocks.NewResponseWithContent(`{"nameAvailable":true}`))
sender.AppendResponse(mocks.NewResponseWithContent(`?`))
sender.AppendResponse(mocks.NewResponseWithContent(`{"name":"account"}`))
sender.AppendResponse(mocks.NewResponseWithContent(`{"keys":[{"value":"firstKey"}]}`))
sender.AppendResponse(mocks.NewResponseWithContent(`{"keys":[{"value":"firstKey"}]}`))
httpSender := pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc {
return func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
return pipeline.NewHTTPResponse(mocks.NewResponseWithContent(`{}`)), nil
}
})

d := NewDriver(ctx, config, &listers.StorageListers)
d.authorizer = authorizer
d.sender = sender
d.httpSender = httpSender

envvars, err := d.ConfigEnv()
if err != nil {
t.Fatal(err)
}

expectedVars := map[string]interface{}{
"REGISTRY_STORAGE": "azure",
"AZURE_CLIENT_ID": "client_id",
"AZURE_TENANT_ID": "tenant_id",
"AZURE_FEDERATED_TOKEN_FILE": "/path/to/file",
"AZURE_AUTHORITY_HOST": "https://login.microsoftonline.com/", // default for configv1.AzureUSGovernmentCloud
}
for key, value := range expectedVars {
e := findEnvVar(envvars, key)
if e == nil {
t.Fatalf("envvar %s not found, %v", key, envvars)
}
if e.Value != value {
t.Errorf("%s: got %#+v, want %#+v", key, e.Value, value)
}
}
}

func TestConfigEnvWithUserKey(t *testing.T) {
ctx := context.Background()

Expand Down

0 comments on commit 8bb474a

Please sign in to comment.