diff --git a/docs/driver-parameters.md b/docs/driver-parameters.md index b23cd1c49..3efd5163a 100644 --- a/docs/driver-parameters.md +++ b/docs/driver-parameters.md @@ -103,7 +103,7 @@ volumeAttributes.keyVaultSecretVersion | Azure Key Vault secret version | existi kubectl create secret generic azure-secret --from-literal=azurestorageaccountname="xxx" --from-literal azurestorageaccountkey="xxx" --type=Opaque kubectl create secret generic azure-secret --from-literal=azurestorageaccountname="xxx" --from-literal azurestorageaccountsastoken="xxx" --type=Opaque kubectl create secret generic azure-secret --from-literal msisecret="xxx" --type=Opaque -kubectl create secret generic azure-secret --from-literal azurestoragespnclientsecret="xxx" --type=Opaque +kubectl create secret generic azure-secret --from-literal azurestoragespnclientsecret="xxx" azurestoragespnclientid="xxx" azurestoragespntenantid="xxx" --type=Opaque ``` ### Tips diff --git a/pkg/blob/blob.go b/pkg/blob/blob.go index 7a780e52f..40a3864a6 100644 --- a/pkg/blob/blob.go +++ b/pkg/blob/blob.go @@ -75,6 +75,8 @@ const ( softDeleteContainersField = "softdeletecontainers" enableBlobVersioningField = "enableblobversioning" getAccountKeyFromSecretField = "getaccountkeyfromsecret" + storageSPNClientIDField = "azurestoragespnclientid" + storageSPNTenantIDField = "azurestoragespntenantid" keyVaultURLField = "keyvaulturl" keyVaultSecretNameField = "keyvaultsecretname" keyVaultSecretVersionField = "keyvaultsecretversion" @@ -369,6 +371,8 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr accountSasToken string msiSecret string storageSPNClientSecret string + storageSPNClientID string + storageSPNTenantID string secretName string pvcNamespace string keyVaultURL string @@ -416,10 +420,10 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_RESOURCE_ID="+v) case "msiendpoint": authEnv = append(authEnv, "MSI_ENDPOINT="+v) - case "azurestoragespnclientid": - authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+v) - case "azurestoragespntenantid": - authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+v) + case storageSPNClientIDField: + storageSPNClientID = v + case storageSPNTenantIDField: + storageSPNTenantID = v case "azurestorageaadendpoint": authEnv = append(authEnv, "AZURE_STORAGE_AAD_ENDPOINT="+v) } @@ -463,11 +467,17 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr } if secretName != "" { // read from k8s secret first - var name string - name, accountKey, accountSasToken, msiSecret, storageSPNClientSecret, err = d.GetInfoFromSecret(ctx, secretName, secretNamespace) + var name, spnClientID, spnTenantID string + name, accountKey, accountSasToken, msiSecret, storageSPNClientSecret, spnClientID, spnTenantID, err = d.GetInfoFromSecret(ctx, secretName, secretNamespace) if name != "" { accountName = name } + if spnClientID != "" { + storageSPNClientID = spnClientID + } + if spnTenantID != "" { + storageSPNTenantID = spnTenantID + } if err != nil && strings.EqualFold(azureStorageAuthType, "msi") { klog.V(2).Infof("ignore error(%v) since secret is optional for auth type(%s)", err, azureStorageAuthType) err = nil @@ -499,6 +509,10 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr msiSecret = v case storageSPNClientSecretField: storageSPNClientSecret = v + case storageSPNClientIDField: + storageSPNClientID = v + case storageSPNTenantIDField: + storageSPNTenantID = v } } } @@ -527,6 +541,16 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+storageSPNClientSecret) } + if storageSPNClientID != "" { + klog.V(2).Infof("storageSPNClientID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNClientID, accountName, containerName) + authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+storageSPNClientID) + } + + if storageSPNTenantID != "" { + klog.V(2).Infof("storageSPNTenantID(%s) is not empty, use it to access storage account(%s), container(%s)", storageSPNTenantID, accountName, containerName) + authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+storageSPNTenantID) + } + return rgName, accountName, accountKey, containerName, authEnv, err } @@ -757,7 +781,7 @@ func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure. if secretName == "" { secretName = fmt.Sprintf(secretNameTemplate, accountOptions.Name) } - _, accountKey, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint + _, accountKey, _, _, _, _, _, err := d.GetInfoFromSecret(ctx, secretName, secretNamespace) //nolint if err != nil { klog.V(2).Infof("could not get account(%s) key from secret(%s) namespace(%s), error: %v, use cluster identity to get account key instead", accountOptions.Name, secretName, secretNamespace, err) accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup) @@ -766,15 +790,15 @@ func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure. } // GetInfoFromSecret get info from k8s secret -// return -func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, error) { +// return +func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, string, string, string, string, error) { if d.cloud.KubeClient == nil { - return "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName) + return "", "", "", "", "", "", "", fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName) } secret, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{}) if err != nil { - return "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err) + return "", "", "", "", "", "", "", fmt.Errorf("could not get secret(%v): %w", secretName, err) } accountName := strings.TrimSpace(string(secret.Data[defaultSecretAccountName][:])) @@ -782,9 +806,11 @@ func (d *Driver) GetInfoFromSecret(ctx context.Context, secretName, secretNamesp accountSasToken := strings.TrimSpace(string(secret.Data[accountSasTokenField][:])) msiSecret := strings.TrimSpace(string(secret.Data[msiSecretField][:])) spnClientSecret := strings.TrimSpace(string(secret.Data[storageSPNClientSecretField][:])) + spnClientID := strings.TrimSpace(string(secret.Data[storageSPNClientIDField][:])) + spnTenantID := strings.TrimSpace(string(secret.Data[storageSPNTenantIDField][:])) klog.V(4).Infof("got storage account(%s) from secret", accountName) - return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, nil + return accountName, accountKey, accountSasToken, msiSecret, spnClientSecret, spnClientID, spnTenantID, nil } // getSubnetResourceID get default subnet resource ID from cloud provider config diff --git a/pkg/blob/blob_test.go b/pkg/blob/blob_test.go index daab602d7..d22f78f96 100644 --- a/pkg/blob/blob_test.go +++ b/pkg/blob/blob_test.go @@ -1047,7 +1047,7 @@ func TestGetInfoFromSecret(t *testing.T) { d.cloud.KubeClient = nil secretName := "foo" secretNamespace := "bar" - _, _, _, _, _, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) + _, _, _, _, _, _, _, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) expectedErr := fmt.Errorf("could not get account key from secret(%s): KubeClient is nil", secretName) if assert.Error(t, err) { assert.Equal(t, expectedErr, err) @@ -1062,7 +1062,7 @@ func TestGetInfoFromSecret(t *testing.T) { d.cloud.KubeClient = fakeClient secretName := "" secretNamespace := "" - _, _, _, _, _, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) + _, _, _, _, _, _, _, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) // expectedErr := fmt.Errorf("could not get secret(%v): %w", secretName, err) assert.Error(t, err) // could not check what type of error, needs fix /*if assert.Error(t, err) { @@ -1095,12 +1095,14 @@ func TestGetInfoFromSecret(t *testing.T) { if secretCreateErr != nil { t.Error("failed to create secret") } - an, ak, accountSasToken, msiSecret, storageSPNClientSecret, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) + an, ak, accountSasToken, msiSecret, storageSPNClientSecret, storageSPNClientID, storageSPNTenantID, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) assert.Equal(t, accountName, an, "accountName should match") assert.Equal(t, accountKey, ak, "accountKey should match") assert.Equal(t, "", accountSasToken, "accountSasToken should be empty") assert.Equal(t, "", msiSecret, "msiSecret should be empty") assert.Equal(t, "", storageSPNClientSecret, "storageSPNClientSecret should be empty") + assert.Equal(t, "", storageSPNClientID, "storageSPNClientID should be empty") + assert.Equal(t, "", storageSPNTenantID, "storageSPNTenantID should be empty") assert.Equal(t, nil, err, "error should be nil") }, }, @@ -1116,6 +1118,8 @@ func TestGetInfoFromSecret(t *testing.T) { accountSasTokenValue := "foo" msiSecretValue := "msiSecret" storageSPNClientSecretValue := "storageSPNClientSecret" + storageSPNClientIDValue := "storageSPNClientID" + storageSPNTenantIDValue := "storageSPNTenantID" secret := &v1api.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: secretNamespace, @@ -1126,6 +1130,8 @@ func TestGetInfoFromSecret(t *testing.T) { accountSasTokenField: []byte(accountSasTokenValue), msiSecretField: []byte(msiSecretValue), storageSPNClientSecretField: []byte(storageSPNClientSecretValue), + storageSPNClientIDField: []byte(storageSPNClientIDValue), + storageSPNTenantIDField: []byte(storageSPNTenantIDValue), }, Type: "Opaque", } @@ -1133,12 +1139,14 @@ func TestGetInfoFromSecret(t *testing.T) { if secretCreateErr != nil { t.Error("failed to create secret") } - an, ak, accountSasToken, msiSecret, storageSPNClientSecret, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) + an, ak, accountSasToken, msiSecret, storageSPNClientSecret, storageSPNClientID, storageSPNTenantID, err := d.GetInfoFromSecret(context.TODO(), secretName, secretNamespace) assert.Equal(t, accountName, an, "accountName should match") assert.Equal(t, "", ak, "accountKey should be empty") assert.Equal(t, accountSasTokenValue, accountSasToken, "sasToken should match") assert.Equal(t, msiSecretValue, msiSecret, "msiSecret should match") assert.Equal(t, storageSPNClientSecretValue, storageSPNClientSecret, "storageSPNClientSecret should match") + assert.Equal(t, storageSPNClientIDValue, storageSPNClientID, "storageSPNClientID should match") + assert.Equal(t, storageSPNTenantIDValue, storageSPNTenantID, "storageSPNTenantID should match") assert.Equal(t, nil, err, "error should be nil") }, },