Skip to content

Commit 668fdbf

Browse files
committed
feat: support Managed Identity and Service Principal Name auth
fix test failure fix go syntax issue
1 parent 07e93d3 commit 668fdbf

File tree

4 files changed

+113
-183
lines changed

4 files changed

+113
-183
lines changed

pkg/blobfuse/blobfuse.go

Lines changed: 110 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -167,42 +167,6 @@ func appendDefaultMountOptions(mountOptions []string) []string {
167167
return allMountOptions
168168
}
169169

170-
// get storage account from secrets map
171-
// returns <accountName, accountKey, accountSasToken>
172-
func getStorageAccountFromSecretsMap(secrets map[string]string) (string, string, string, error) {
173-
if secrets == nil {
174-
return "", "", "", fmt.Errorf("unexpected: getStorageAccountFromSecretsMap secrets is nil")
175-
}
176-
177-
var accountName, accountKey, accountSasToken string
178-
for k, v := range secrets {
179-
switch strings.ToLower(k) {
180-
case "accountname":
181-
accountName = v
182-
case "azurestorageaccountname": // for compatibility with built-in blobfuse plugin
183-
accountName = v
184-
case "accountkey":
185-
accountKey = v
186-
case "azurestorageaccountkey": // for compatibility with built-in blobfuse plugin
187-
accountKey = v
188-
case "azurestorageaccountsastoken":
189-
accountSasToken = v
190-
}
191-
}
192-
193-
if accountName == "" {
194-
return "", "", "", fmt.Errorf("could not find accountname or azurestorageaccountname field secrets(%v)", secrets)
195-
}
196-
if accountKey == "" && accountSasToken == "" {
197-
return "", "", "", fmt.Errorf("could not find accountkey, azurestorageaccountkey or azurestorageaccountsastoken field in secrets(%v)", secrets)
198-
}
199-
if accountKey != "" && accountSasToken != "" {
200-
return "", "", "", fmt.Errorf("could not specify Access Key and SAS Token together")
201-
}
202-
203-
return accountName, accountKey, accountSasToken, nil
204-
}
205-
206170
// A container name must be a valid DNS name, conforming to the following naming rules:
207171
// 1. Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
208172
// 2. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.
@@ -243,8 +207,118 @@ func isSASToken(key string) bool {
243207
return strings.Contains(key, "?sv=")
244208
}
245209

210+
// GetAuthEnv return <accountName, containerName, authEnv, error>
211+
func (d *Driver) GetAuthEnv(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, []string, error) {
212+
var (
213+
accountName string
214+
accountKey string
215+
accountSasToken string
216+
containerName string
217+
keyVaultURL string
218+
keyVaultSecretName string
219+
keyVaultSecretVersion string
220+
authEnv []string
221+
err error
222+
)
223+
224+
for k, v := range attrib {
225+
switch strings.ToLower(k) {
226+
case "containername":
227+
containerName = v
228+
case "keyvaulturl":
229+
keyVaultURL = v
230+
case "keyvaultsecretname":
231+
keyVaultSecretName = v
232+
case "keyvaultsecretversion":
233+
keyVaultSecretVersion = v
234+
case "storageaccountname":
235+
accountName = v
236+
case "azurestorageidentityclientid":
237+
authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_CLIENT_ID="+v)
238+
case "azurestorageidentityobjectid":
239+
authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_OBJECT_ID="+v)
240+
case "azurestorageidentityresourceid":
241+
authEnv = append(authEnv, "AZURE_STORAGE_IDENTITY_RESOURCE_ID="+v)
242+
case "msiendpoint":
243+
authEnv = append(authEnv, "MSI_ENDPOINT="+v)
244+
case "azurestoragespnclientid":
245+
authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+v)
246+
case "azurestoragespntenantid":
247+
authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+v)
248+
case "azurestorageaadendpoint":
249+
authEnv = append(authEnv, "AZURE_STORAGE_AAD_ENDPOINT="+v)
250+
}
251+
}
252+
253+
// 1. If keyVaultURL is not nil, preferentially use the key stored in key vault.
254+
// 2. Then if secrets map is not nil, use the key stored in the secrets map.
255+
// 3. Finally if both keyVaultURL and secrets map are nil, get the key from Azure.
256+
if keyVaultURL != "" {
257+
key, err := d.getKeyVaultSecretContent(ctx, keyVaultURL, keyVaultSecretName, keyVaultSecretVersion)
258+
if err != nil {
259+
return accountName, containerName, authEnv, err
260+
}
261+
if isSASToken(key) {
262+
accountSasToken = key
263+
} else {
264+
accountKey = key
265+
}
266+
} else {
267+
if len(secrets) == 0 {
268+
var resourceGroupName string
269+
resourceGroupName, accountName, containerName, err = GetContainerInfo(volumeID)
270+
if err != nil {
271+
return accountName, containerName, authEnv, err
272+
}
273+
274+
if resourceGroupName == "" {
275+
resourceGroupName = d.cloud.ResourceGroup
276+
}
277+
278+
accountKey, err = d.cloud.GetStorageAccesskey(accountName, resourceGroupName)
279+
if err != nil {
280+
return accountName, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %v", accountName, resourceGroupName, err)
281+
}
282+
} else {
283+
for k, v := range secrets {
284+
switch strings.ToLower(k) {
285+
case "accountname":
286+
accountName = v
287+
case "azurestorageaccountname": // for compatibility with built-in blobfuse plugin
288+
accountName = v
289+
case "accountkey":
290+
accountKey = v
291+
case "azurestorageaccountkey": // for compatibility with built-in blobfuse plugin
292+
accountKey = v
293+
case "azurestorageaccountsastoken":
294+
accountSasToken = v
295+
case "msisecret":
296+
authEnv = append(authEnv, "MSI_SECRET="+v)
297+
case "azurestoragespnclientsecret":
298+
authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_SECRET="+v)
299+
}
300+
}
301+
}
302+
}
303+
304+
if containerName == "" {
305+
err = fmt.Errorf("could not find containerName from attributes(%v) or volumeID(%v)", attrib, volumeID)
306+
}
307+
308+
if accountSasToken != "" {
309+
authEnv = append(authEnv, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
310+
}
311+
312+
if accountKey != "" {
313+
authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
314+
}
315+
316+
return accountName, containerName, authEnv, err
317+
}
318+
246319
// GetStorageAccountAndContainer: get storage account and container info
247320
// returns <accountName, accountKey, accountSasToken, containerName>
321+
// only for e2e testing
248322
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
249323
var (
250324
accountName string
@@ -304,11 +378,6 @@ func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID str
304378
if err != nil {
305379
return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %v", accountName, resourceGroupName, err)
306380
}
307-
} else {
308-
accountName, accountKey, accountSasToken, err = getStorageAccountFromSecretsMap(secrets)
309-
if err != nil {
310-
return "", "", "", "", err
311-
}
312381
}
313382
}
314383

pkg/blobfuse/blobfuse_test.go

Lines changed: 0 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -118,139 +118,6 @@ func TestGetContainerInfo(t *testing.T) {
118118
}
119119
}
120120

121-
func TestGetStorageAccountFromSecretsMap(t *testing.T) {
122-
emptyAccountKeyMap := map[string]string{
123-
"accountname": "testaccount",
124-
"accountkey": "",
125-
}
126-
127-
emptyAccountNameMap := map[string]string{
128-
"accountname": "",
129-
"accountkey": "testkey",
130-
}
131-
132-
emptyAzureAccountKeyMap := map[string]string{
133-
"azurestorageaccountname": "testaccount",
134-
"azurestorageaccountkey": "",
135-
}
136-
137-
emptyAzureAccountNameMap := map[string]string{
138-
"azurestorageaccountname": "",
139-
"azurestorageaccountkey": "testkey",
140-
}
141-
142-
emptyAccountSasTokenMap := map[string]string{
143-
"azurestorageaccountname": "testaccount",
144-
"azurestorageaccountsastoken": "",
145-
}
146-
147-
accesskeyAndaccountSasTokenMap := map[string]string{
148-
"azurestorageaccountname": "testaccount",
149-
"azurestorageaccountkey": "testkey",
150-
"azurestorageaccountsastoken": "testkey",
151-
}
152-
153-
tests := []struct {
154-
options map[string]string
155-
expected1 string
156-
expected2 string
157-
expected3 string
158-
expected4 error
159-
}{
160-
{
161-
options: map[string]string{
162-
"accountname": "testaccount",
163-
"accountkey": "testkey",
164-
},
165-
expected1: "testaccount",
166-
expected2: "testkey",
167-
expected3: "",
168-
expected4: nil,
169-
},
170-
{
171-
options: map[string]string{
172-
"azurestorageaccountname": "testaccount",
173-
"azurestorageaccountkey": "testkey",
174-
},
175-
expected1: "testaccount",
176-
expected2: "testkey",
177-
expected3: "",
178-
expected4: nil,
179-
},
180-
{
181-
options: map[string]string{
182-
"accountname": "",
183-
"accountkey": "",
184-
},
185-
expected1: "",
186-
expected2: "",
187-
expected3: "",
188-
expected4: fmt.Errorf("could not find accountname or azurestorageaccountname field secrets(map[accountname: accountkey:])"),
189-
},
190-
{
191-
options: emptyAccountKeyMap,
192-
expected1: "",
193-
expected2: "",
194-
expected3: "",
195-
expected4: fmt.Errorf("could not find accountkey, azurestorageaccountkey or azurestorageaccountsastoken field in secrets(%v)", emptyAccountKeyMap),
196-
},
197-
{
198-
options: emptyAccountNameMap,
199-
expected1: "",
200-
expected2: "",
201-
expected3: "",
202-
expected4: fmt.Errorf("could not find accountname or azurestorageaccountname field secrets(%v)", emptyAccountNameMap),
203-
},
204-
{
205-
options: emptyAzureAccountKeyMap,
206-
expected1: "",
207-
expected2: "",
208-
expected3: "",
209-
expected4: fmt.Errorf("could not find accountkey, azurestorageaccountkey or azurestorageaccountsastoken field in secrets(%v)", emptyAzureAccountKeyMap),
210-
},
211-
{
212-
options: emptyAzureAccountNameMap,
213-
expected1: "",
214-
expected2: "",
215-
expected3: "",
216-
expected4: fmt.Errorf("could not find accountname or azurestorageaccountname field secrets(%v)", emptyAzureAccountNameMap),
217-
},
218-
{
219-
options: emptyAccountSasTokenMap,
220-
expected1: "",
221-
expected2: "",
222-
expected3: "",
223-
expected4: fmt.Errorf("could not find accountkey, azurestorageaccountkey or azurestorageaccountsastoken field in secrets(%v)", emptyAzureAccountKeyMap),
224-
},
225-
{
226-
options: accesskeyAndaccountSasTokenMap,
227-
expected1: "",
228-
expected2: "",
229-
expected3: "",
230-
expected4: fmt.Errorf("could not specify Access Key and SAS Token together"),
231-
},
232-
{
233-
options: nil,
234-
expected1: "",
235-
expected2: "",
236-
expected3: "",
237-
expected4: fmt.Errorf("unexpected: getStorageAccountFromSecretsMap secrets is nil"),
238-
},
239-
}
240-
241-
for _, test := range tests {
242-
result1, result2, result3, result4 := getStorageAccountFromSecretsMap(test.options)
243-
if !reflect.DeepEqual(result1, test.expected1) || !reflect.DeepEqual(result2, test.expected2) || !reflect.DeepEqual(result3, test.expected3) {
244-
t.Errorf("input: %q, getStorageAccountFromSecretsMap result1: %q, expected1: %q, result2: %q, expected2: %q, result3: %q, expected3: %q, result4: %q, expected4: %q", test.options, result1, test.expected1, result2, test.expected2,
245-
result3, test.expected3, result4, test.expected4)
246-
} else {
247-
if result1 == "" || (result2 == "" && result3 == "") {
248-
assert.Error(t, result4)
249-
}
250-
}
251-
}
252-
}
253-
254121
func TestGetValidContainerName(t *testing.T) {
255122
tests := []struct {
256123
volumeName string

pkg/blobfuse/controllerserver.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7272
resourceGroup = v
7373
case "containername":
7474
containerName = v
75-
default:
76-
return nil, fmt.Errorf("invalid option %q", k)
7775
}
7876
}
7977

pkg/blobfuse/nodeserver.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
126126
attrib := req.GetVolumeContext()
127127
secrets := req.GetSecrets()
128128

129-
accountName, accountKey, accountSasToken, containerName, err := d.GetStorageAccountAndContainer(ctx, volumeID, attrib, secrets)
129+
accountName, containerName, authEnv, err := d.GetAuthEnv(ctx, volumeID, attrib, secrets)
130130
if err != nil {
131131
return nil, err
132132
}
@@ -144,14 +144,10 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
144144
klog.V(2).Infof("target %v\nfstype %v\n\nvolumeId %v\ncontext %v\nmountflags %v\nmountOptions %v\nargs %v\nblobStorageEndPoint %v",
145145
targetPath, fsType, volumeID, attrib, mountFlags, mountOptions, args, blobStorageEndPoint)
146146
cmd := exec.Command("blobfuse", strings.Split(args, " ")...)
147-
cmd.Env = append(os.Environ(), "AZURE_STORAGE_ACCOUNT="+accountName)
148147

149-
if accountSasToken != "" {
150-
cmd.Env = append(cmd.Env, "AZURE_STORAGE_SAS_TOKEN="+accountSasToken)
151-
} else {
152-
cmd.Env = append(cmd.Env, "AZURE_STORAGE_ACCESS_KEY="+accountKey)
153-
}
148+
cmd.Env = append(os.Environ(), "AZURE_STORAGE_ACCOUNT="+accountName)
154149
cmd.Env = append(cmd.Env, "AZURE_STORAGE_BLOB_ENDPOINT="+blobStorageEndPoint)
150+
cmd.Env = append(cmd.Env, authEnv...)
155151

156152
output, err := cmd.CombinedOutput()
157153
if err != nil {

0 commit comments

Comments
 (0)