Skip to content

Commit 65af37e

Browse files
authored
Merge pull request #2652 from kubernetes-sigs/mi
feat: support SMB mount with managed identity
2 parents 8c2e466 + 22ec372 commit 65af37e

File tree

14 files changed

+232
-20
lines changed

14 files changed

+232
-20
lines changed
197 Bytes
Binary file not shown.

charts/latest/azurefile-csi-driver/templates/csi-azurefile-node.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,18 @@ spec:
9696
value: "{{ .Values.linux.kubelet }}"
9797
- name: MIGRATE_K8S_REPO
9898
value: "{{ .Values.node.azurefileProxy.migrateK8sRepo }}"
99+
- name: ENABLE_MI_AUTH
100+
value: "{{ .Values.node.enableManagedIdentityAuth }}"
99101
volumeMounts:
100102
- name: host-usr
101103
mountPath: /host/usr
102104
- name: host-etc
103105
mountPath: /host/etc
106+
{{- if .Values.node.enableManagedIdentityAuth }}
107+
- name: mountpoint-dir
108+
mountPath: /var/lib/kubelet/
109+
mountPropagation: Bidirectional
110+
{{- end }}
104111
containers:
105112
- name: liveness-probe
106113
volumeMounts:
@@ -140,6 +147,31 @@ spec:
140147
- name: registration-dir
141148
mountPath: /registration
142149
resources: {{- toYaml .Values.linux.resources.nodeDriverRegistrar | nindent 12 }}
150+
{{- if .Values.node.enableManagedIdentityAuth }}
151+
- name: azfilesrefresh
152+
{{- if hasPrefix "/" .Values.image.azurefile.repository }}
153+
image: "{{ .Values.image.baseRepo }}{{ .Values.image.azurefile.repository }}:{{ .Values.image.azurefile.tag }}"
154+
{{- else }}
155+
image: "{{ .Values.image.azurefile.repository }}:{{ .Values.image.azurefile.tag }}"
156+
{{- end }}
157+
imagePullPolicy: {{ .Values.image.azurefile.pullPolicy }}
158+
command:
159+
- "azfilesrefresh"
160+
securityContext:
161+
privileged: true
162+
capabilities:
163+
drop:
164+
- ALL
165+
volumeMounts:
166+
- mountPath: /var/lib/kubelet/
167+
mountPropagation: Bidirectional
168+
name: mountpoint-dir
169+
- name: host-etc
170+
mountPath: /etc
171+
- name: log-dir
172+
mountPath: /var/log/
173+
resources: {{- toYaml .Values.linux.resources.azfilesrefresh | nindent 12 }}
174+
{{- end }}
143175
- name: azurefile
144176
{{- if hasPrefix "/" .Values.image.azurefile.repository }}
145177
image: "{{ .Values.image.baseRepo }}{{ .Values.image.azurefile.repository }}:{{ .Values.image.azurefile.tag }}"
@@ -228,6 +260,10 @@ spec:
228260
- mountPath: /run/kata-containers/shared/direct-volumes
229261
name: kata-direct-volumes
230262
{{- end }}
263+
{{- if .Values.node.enableManagedIdentityAuth }}
264+
- name: log-dir
265+
mountPath: /var/log/
266+
{{- end }}
231267
resources: {{- toYaml .Values.linux.resources.azurefile | nindent 12 }}
232268
volumes:
233269
- name: host-usr
@@ -270,4 +306,10 @@ spec:
270306
path: /run/kata-containers/shared/direct-volumes/
271307
type: DirectoryOrCreate
272308
{{- end }}
309+
{{- if .Values.node.enableManagedIdentityAuth }}
310+
- hostPath:
311+
path: /var/log/
312+
type: DirectoryOrCreate
313+
name: log-dir
314+
{{- end }}
273315
{{- end -}}

charts/latest/azurefile-csi-driver/values.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ controller:
8888
requests:
8989
cpu: 10m
9090
memory: 20Mi
91+
azfilesrefresh:
92+
limits:
93+
cpu: 1
94+
memory: 100Mi
95+
requests:
96+
cpu: 10m
97+
memory: 20Mi
9198
azurefile:
9299
limits:
93100
cpu: 2
@@ -120,6 +127,7 @@ node:
120127
allowEmptyCloudConfig: true
121128
allowInlineVolumeKeyAccessWithIdentity: false
122129
enableKataCCMount: false
130+
enableManagedIdentityAuth: true
123131
metricsPort: 29615
124132
livenessProbe:
125133
healthPort: 29613

deploy/csi-azurefile-node.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,16 @@ spec:
5858
value: "true"
5959
- name: INSTALL_AZNFS_MOUNT
6060
value: "true"
61+
- name: ENABLE_MI_AUTH
62+
value: "true"
6163
volumeMounts:
6264
- name: host-usr
6365
mountPath: /host/usr
6466
- name: host-etc
6567
mountPath: /host/etc
68+
- name: mountpoint-dir
69+
mountPath: /var/lib/kubelet/
70+
mountPropagation: Bidirectional
6671
containers:
6772
- name: liveness-probe
6873
volumeMounts:
@@ -163,12 +168,40 @@ spec:
163168
name: device-dir
164169
- mountPath: /run/kata-containers/shared/direct-volumes
165170
name: kata-direct-volumes
171+
- name: host-etc
172+
mountPath: /etc
173+
- name: log-dir
174+
mountPath: /var/log/
166175
resources:
167176
limits:
168177
memory: 400Mi
169178
requests:
170179
cpu: 10m
171180
memory: 20Mi
181+
- name: azfilesrefresh
182+
image: mcr.microsoft.com/k8s/csi/azurefile-csi:latest
183+
imagePullPolicy: IfNotPresent
184+
command:
185+
- "azfilesrefresh"
186+
securityContext:
187+
privileged: true
188+
capabilities:
189+
drop:
190+
- ALL
191+
volumeMounts:
192+
- mountPath: /var/lib/kubelet/
193+
mountPropagation: Bidirectional
194+
name: mountpoint-dir
195+
- name: host-etc
196+
mountPath: /etc
197+
- name: log-dir
198+
mountPath: /var/log/
199+
resources:
200+
limits:
201+
memory: 100Mi
202+
requests:
203+
cpu: 10m
204+
memory: 20Mi
172205
volumes:
173206
- name: host-usr
174207
hostPath:
@@ -200,4 +233,8 @@ spec:
200233
hostPath:
201234
path: /run/kata-containers/shared/direct-volumes/
202235
type: DirectoryOrCreate
236+
- hostPath:
237+
path: /var/log/
238+
type: DirectoryOrCreate
239+
name: log-dir
203240
---

pkg/azurefile-proxy/init.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,28 @@
1717
set -xe
1818

1919
MIGRATE_K8S_REPO=${MIGRATE_K8S_REPO:-false}
20+
ENABLE_MI_AUTH=${ENABLE_MI_AUTH:-true}
2021

2122
KUBELET_PATH=${KUBELET_PATH:-/var/lib/kubelet}
2223
if [ "$KUBELET_PATH" != "/var/lib/kubelet" ];then
2324
echo "kubelet path is $KUBELET_PATH, update azurefile-proxy.service...."
2425
sed -i "s#--azurefile-proxy-endpoint[^ ]*#--azurefile-proxy-endpoint=unix:/${KUBELET_PATH}/plugins/file.csi.azure.com/azurefile-proxy.sock#" /azurefile-proxy/azurefile-proxy.service
2526
echo "azurefile-proxy endpoint is updated to unix:/$KUBELET_PATH/plugins/file.csi.azure.com/azurefile-proxy.sock"
26-
fi
27+
fi
2728

2829
HOST_CMD="nsenter --mount=/proc/1/ns/mnt"
2930

31+
if [ "$ENABLE_MI_AUTH" = "true" ];then
32+
echo "set up /etc/krb5.conf on host"
33+
printf '[libdefaults]\ndefault_ccache_name = FILE:/var/lib/kubelet/kerberos/krb5cc_%s\n' "%{uid}" > /host/etc/krb5.conf
34+
35+
mkdir -p /var/lib/kubelet/kerberos
36+
37+
echo "set up /etc/azfilesauth/config.yaml on host"
38+
mkdir -p /host/etc/azfilesauth
39+
printf 'USER_UID: 0\nKRB5_CC_NAME: /var/lib/kubelet/kerberos/krb5cc_0\nLOG_DESTINATION: "file"\n' > /host/etc/azfilesauth/config.yaml
40+
fi
41+
3042
DISTRIBUTION=$($HOST_CMD cat /etc/os-release | grep ^ID= | cut -d'=' -f2 | tr -d '"')
3143
ARCH=$($HOST_CMD uname -m)
3244
echo "Linux distribution: $DISTRIBUTION, Arch: $ARCH"

pkg/azurefile/azurefile.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ const (
167167
defaultConfidentialContainerLabel = "kubernetes.azure.com/kata-cc-isolation"
168168
runtimeClassHandlerField = "runtimeclasshandler"
169169
defaultRuntimeClassHandler = "kata-cc"
170+
mountWithManagedIdentityField = "mountwithmanagedidentity"
170171

171172
accountNotProvisioned = "StorageAccountIsNotProvisioned"
172173
// this is a workaround fix for 429 throttling issue, will update cloud provider for better fix later
@@ -804,7 +805,7 @@ func (d *Driver) GetAccountInfo(ctx context.Context, volumeID string, secrets, r
804805

805806
var protocol, accountKey, secretName, pvcNamespace string
806807
// getAccountKeyFromSecret indicates whether get account key only from k8s secret
807-
var getAccountKeyFromSecret, getLatestAccountKey bool
808+
var getAccountKeyFromSecret, getLatestAccountKey, mountWithManagedIdentity bool
808809
var clientID, tenantID, serviceAccountToken string
809810

810811
for k, v := range reqContext {
@@ -837,6 +838,10 @@ func (d *Driver) GetAccountInfo(ctx context.Context, volumeID string, secrets, r
837838
}
838839
case clientIDField:
839840
clientID = v
841+
case mountWithManagedIdentityField:
842+
if mountWithManagedIdentity, err = strconv.ParseBool(v); err != nil {
843+
return rgName, accountName, accountKey, fileShareName, diskName, subsID, fmt.Errorf("invalid %s: %s in volume context", mountWithManagedIdentityField, v)
844+
}
840845
case tenantIDField:
841846
tenantID = v
842847
case strings.ToLower(serviceAccountTokenField):
@@ -866,7 +871,11 @@ func (d *Driver) GetAccountInfo(ctx context.Context, volumeID string, secrets, r
866871
}
867872
}
868873

869-
// if client id is specified, we only use service account token to get account key
874+
if mountWithManagedIdentity {
875+
klog.V(2).Infof("mountWithManagedIdentity is true, use managed identity auth")
876+
return rgName, accountName, accountKey, fileShareName, diskName, subsID, nil
877+
}
878+
870879
if clientID != "" {
871880
klog.V(2).Infof("clientID(%s) is specified, use service account token to get account key", clientID)
872881
accountKey, err := d.cloud.GetStorageAccesskeyFromServiceAccountToken(ctx, subsID, accountName, rgName, clientID, tenantID, serviceAccountToken)

pkg/azurefile/azurefile_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,20 @@ func TestGetAccountInfo(t *testing.T) {
824824
expectFileShareName: "test_sharename",
825825
expectDiskName: "test_diskname",
826826
},
827+
{
828+
volumeID: "invalid_mountWithManagedIdentityField_value##",
829+
rgName: "vol_2",
830+
secrets: emptySecret,
831+
reqContext: map[string]string{
832+
shareNameField: "test_sharename",
833+
mountWithManagedIdentityField: "invalid",
834+
},
835+
expectErr: true,
836+
err: fmt.Errorf("invalid %s: %s in volume context", mountWithManagedIdentityField, "invalid"),
837+
expectAccountName: "",
838+
expectFileShareName: "test_sharename",
839+
expectDiskName: "test_diskname",
840+
},
827841
}
828842

829843
for _, test := range tests {

pkg/azurefile/controllerserver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
227227
fileShareNameReplaceMap[pvNameMetadata] = v
228228
case serverNameField:
229229
case folderNameField:
230+
case mountWithManagedIdentityField:
230231
case clientIDField:
231232
case confidentialContainerLabelField:
232233
case runtimeClassHandlerField:

pkg/azurefile/controllerserver_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ var _ = ginkgo.Describe("TestCreateVolume", func() {
925925
runtimeClassHandlerField: "runtime-handler",
926926
createFolderIfNotExistField: "true",
927927
confidentialContainerLabelField: "confidential-container-label",
928+
mountWithManagedIdentityField: "true",
928929
}
929930

930931
req := &csi.CreateVolumeRequest{

pkg/azurefile/nodeserver.go

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
7676
mountPermissions := d.mountPermissions
7777
context := req.GetVolumeContext()
7878
if context != nil {
79-
// token request
80-
if getValueInMap(context, serviceAccountTokenField) != "" && getValueInMap(context, clientIDField) != "" {
79+
if !strings.EqualFold(getValueInMap(context, mountWithManagedIdentityField), trueValue) && getValueInMap(context, serviceAccountTokenField) != "" && getValueInMap(context, clientIDField) != "" {
8180
klog.V(2).Infof("NodePublishVolume: volume(%s) mount on %s with service account token, clientID: %s", volumeID, target, getValueInMap(context, clientIDField))
8281
_, err := d.NodeStageVolume(ctx, &csi.NodeStageVolumeRequest{
8382
StagingTargetPath: target,
@@ -248,7 +247,7 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
248247
volumeID := req.GetVolumeId()
249248
context := req.GetVolumeContext()
250249

251-
if getValueInMap(context, clientIDField) != "" && getValueInMap(context, serviceAccountTokenField) == "" {
250+
if getValueInMap(context, clientIDField) != "" && !strings.EqualFold(getValueInMap(context, mountWithManagedIdentityField), trueValue) && getValueInMap(context, serviceAccountTokenField) == "" {
252251
klog.V(2).Infof("Skip NodeStageVolume for volume(%s) since clientID %s is provided but service account token is empty", volumeID, getValueInMap(context, clientIDField))
253252
return &csi.NodeStageVolumeResponse{}, nil
254253
}
@@ -277,8 +276,8 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
277276
}
278277
// don't respect fsType from req.GetVolumeCapability().GetMount().GetFsType()
279278
// since it's ext4 by default on Linux
280-
var fsType, server, protocol, ephemeralVolMountOptions, storageEndpointSuffix, folderName string
281-
var ephemeralVol, createFolderIfNotExist, encryptInTransit bool
279+
var fsType, server, protocol, ephemeralVolMountOptions, storageEndpointSuffix, folderName, clientID string
280+
var ephemeralVol, createFolderIfNotExist, encryptInTransit, mountWithManagedIdentity bool
282281
fileShareNameReplaceMap := map[string]string{}
283282

284283
mountPermissions := d.mountPermissions
@@ -314,7 +313,6 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
314313
fileShareNameReplaceMap[pvNameMetadata] = v
315314
case mountPermissionsField:
316315
if v != "" {
317-
var err error
318316
var perm uint64
319317
if perm, err = strconv.ParseUint(v, 8, 32); err != nil {
320318
return nil, status.Errorf(codes.InvalidArgument, "invalid mountPermissions %s", v)
@@ -326,11 +324,17 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
326324
}
327325
}
328326
case encryptInTransitField:
329-
var err error
330327
encryptInTransit, err = strconv.ParseBool(v)
331328
if err != nil {
332329
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Volume context property %q must be a boolean value: %v", k, err))
333330
}
331+
case mountWithManagedIdentityField:
332+
mountWithManagedIdentity, err = strconv.ParseBool(v)
333+
if err != nil {
334+
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Volume context property %q must be a boolean value: %v", k, err))
335+
}
336+
case clientIDField:
337+
clientID = v
334338
}
335339
}
336340

@@ -401,18 +405,29 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
401405
mountOptions = util.JoinMountOptions(mountFlags, []string{"vers=4,minorversion=1,sec=sys"})
402406
mountOptions = appendDefaultNfsMountOptions(mountOptions, d.appendNoResvPortOption, d.appendActimeoOption)
403407
} else {
404-
if accountName == "" || accountKey == "" {
405-
return nil, status.Errorf(codes.Internal, "accountName(%s) or accountKey is empty", accountName)
406-
}
407-
if runtime.GOOS == "windows" {
408-
mountOptions = []string{fmt.Sprintf("AZURE\\%s", accountName)}
409-
sensitiveMountOptions = []string{accountKey}
408+
if mountWithManagedIdentity && runtime.GOOS != "windows" {
409+
if clientID == "" {
410+
clientID = d.cloud.Config.AzureAuthConfig.UserAssignedIdentityID
411+
}
412+
sensitiveMountOptions = []string{"sec=krb5,cruid=0,upcall_target=mount", fmt.Sprintf("username=%s", clientID)}
413+
klog.V(2).Infof("using managed identity %s for volume %s with mount options: %v", clientID, volumeID, sensitiveMountOptions)
410414
} else {
411-
if err := os.MkdirAll(targetPath, os.FileMode(mountPermissions)); err != nil {
412-
return nil, status.Error(codes.Internal, fmt.Sprintf("MkdirAll %s failed with error: %v", targetPath, err))
415+
if accountName == "" || accountKey == "" {
416+
return nil, status.Errorf(codes.Internal, "accountName(%s) or accountKey is empty", accountName)
413417
}
414-
// parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
415-
sensitiveMountOptions = []string{fmt.Sprintf("username=%s,password=%s", accountName, accountKey)}
418+
if runtime.GOOS == "windows" {
419+
mountOptions = []string{fmt.Sprintf("AZURE\\%s", accountName)}
420+
sensitiveMountOptions = []string{accountKey}
421+
} else {
422+
if err := os.MkdirAll(targetPath, os.FileMode(mountPermissions)); err != nil {
423+
return nil, status.Error(codes.Internal, fmt.Sprintf("MkdirAll %s failed with error: %v", targetPath, err))
424+
}
425+
// parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
426+
sensitiveMountOptions = []string{fmt.Sprintf("username=%s,password=%s", accountName, accountKey)}
427+
}
428+
}
429+
430+
if runtime.GOOS != "windows" {
416431
if ephemeralVol {
417432
cifsMountFlags = util.JoinMountOptions(cifsMountFlags, strings.Split(ephemeralVolMountOptions, ","))
418433
}
@@ -456,6 +471,11 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
456471
klog.V(2).Infof("mount with proxy succeeded for %s", cifsMountPath)
457472
} else {
458473
execFunc := func() error {
474+
if mountWithManagedIdentity && protocol != nfs && runtime.GOOS != "windows" {
475+
if out, err := setCredentialCache(server, clientID); err != nil {
476+
return fmt.Errorf("setCredentialCache failed for %s with error: %v, output: %s", server, err, out)
477+
}
478+
}
459479
return SMBMount(d.mounter, source, cifsMountPath, mountFsType, mountOptions, sensitiveMountOptions)
460480
}
461481
timeoutFunc := func() error { return fmt.Errorf("time out") }

0 commit comments

Comments
 (0)