|
9 | 9 | "strings" |
10 | 10 |
|
11 | 11 | vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1" |
| 12 | + "gopkg.in/yaml.v2" |
12 | 13 | v1 "k8s.io/api/core/v1" |
13 | 14 | k8sstoragev1 "k8s.io/api/storage/v1" |
14 | 15 | apierrors "k8s.io/apimachinery/pkg/api/errors" |
@@ -40,6 +41,7 @@ import ( |
40 | 41 | func (h *helper) GetVolumeConfig( |
41 | 42 | ctx context.Context, name string, _ int64, _ map[string]string, _ config.Protocol, _ []config.AccessMode, |
42 | 43 | _ config.VolumeMode, fsType string, requisiteTopology, preferredTopology, _ []map[string]string, |
| 44 | + secrets map[string]string, |
43 | 45 | ) (*storage.VolumeConfig, error) { |
44 | 46 | // Kubernetes CSI passes us the name of what will become a new PV |
45 | 47 | pvName := name |
@@ -91,8 +93,20 @@ func (h *helper) GetVolumeConfig( |
91 | 93 |
|
92 | 94 | // Create the volume config |
93 | 95 | annotations := processPVCAnnotations(pvc, fsType) |
| 96 | + |
| 97 | + // Process annotations from the storage class |
| 98 | + scAnnotations := processSCAnnotations(sc) |
| 99 | + |
94 | 100 | volumeConfig := getVolumeConfig(ctx, pvc, pvName, pvcSize, annotations, sc, requisiteTopology, preferredTopology) |
95 | 101 |
|
| 102 | + // Update the volume config with the Access Control only if the storage class nasType parameter is SMB |
| 103 | + if sc.Parameters[SCParameterNASType] == NASTypeSMB { |
| 104 | + err = h.updateVolumeConfigWithSecureSMBAccessControl(ctx, volumeConfig, sc, annotations, scAnnotations, secrets) |
| 105 | + if err != nil { |
| 106 | + return nil, err |
| 107 | + } |
| 108 | + } |
| 109 | + |
96 | 110 | // Get clone annotations |
97 | 111 | sourceSnapshotName := getAnnotation(annotations, AnnCloneFromSnapshot) |
98 | 112 | sourcePVCName := getAnnotation(annotations, AnnCloneFromPVC) |
@@ -679,6 +693,52 @@ func (h *helper) getSnapshotInternalNameFromAnnotation( |
679 | 693 | return internalName, nil |
680 | 694 | } |
681 | 695 |
|
| 696 | +// updateVolumeConfigWithSecureSMBAccessControl update the volume config with the secure SMB access control from the |
| 697 | +// PVC annotations and the storage class annotations |
| 698 | +func (h *helper) updateVolumeConfigWithSecureSMBAccessControl(ctx context.Context, volumeConfig *storage.VolumeConfig, |
| 699 | + sc *k8sstoragev1.StorageClass, pvcAnnotations, scAnnotations, secret map[string]string, |
| 700 | +) error { |
| 701 | + if sc.Parameters[CSIParameterNodeStageSecretName] == "" || sc.Parameters[CSIParameterNodeStageSecretNamespace] == "" { |
| 702 | + return fmt.Errorf("missing required parameters %s and %s for secure SMB access control", |
| 703 | + CSIParameterNodeStageSecretName, CSIParameterNodeStageSecretNamespace) |
| 704 | + } |
| 705 | + |
| 706 | + // Get the smb share Access Control from PVC annotations |
| 707 | + smbSharePVCAccessControlAnn := getAnnotation(pvcAnnotations, AnnSMBShareAccessControl) |
| 708 | + smbShareACL, err := getSMBShareAccessControlFromPVCAnnotation(smbSharePVCAccessControlAnn) |
| 709 | + if err != nil { |
| 710 | + return fmt.Errorf("failed to parse smb share access control from annotation: %v", err) |
| 711 | + } |
| 712 | + |
| 713 | + adUserFromSC := getAnnotation(scAnnotations, AnnSMBShareAdUser) |
| 714 | + adUserPermissionFromSC := getAnnotation(scAnnotations, AnnSMBShareAdUserPermission) |
| 715 | + |
| 716 | + // Set SecureSMBEnabled to true if adUserFromSC is present. This ensures Secure SMB is enabled only when required. |
| 717 | + if adUserFromSC != "" { |
| 718 | + volumeConfig.SecureSMBEnabled = true |
| 719 | + } |
| 720 | + |
| 721 | + // Check if the adUserPermissionAnn is set, if not set it to the full control |
| 722 | + if adUserPermissionFromSC == "" { |
| 723 | + adUserPermissionFromSC = SMBShareFullControlPermission |
| 724 | + } |
| 725 | + |
| 726 | + // Check if the adUserPermissionAnn is valid |
| 727 | + if !isValidAccessControlPermission(adUserPermissionFromSC) { |
| 728 | + return fmt.Errorf("invalid adUserPermission: %s. Valid adUserPermissions are %s, %s, %s, %s", |
| 729 | + adUserPermissionFromSC, SMBShareFullControlPermission, SMBShareReadPermission, SMBShareChangePermission, SMBShareNoPermission) |
| 730 | + } |
| 731 | + |
| 732 | + // Check if adUser already exists in the smbShareACL, if not add it |
| 733 | + if _, exists := smbShareACL[adUserFromSC]; !exists { |
| 734 | + smbShareACL[adUserFromSC] = adUserPermissionFromSC |
| 735 | + } |
| 736 | + |
| 737 | + volumeConfig.SMBShareACL = smbShareACL |
| 738 | + |
| 739 | + return nil |
| 740 | +} |
| 741 | + |
682 | 742 | // RecordVolumeEvent accepts the name of a CSI volume (i.e. a PV name), finds the associated |
683 | 743 | // PVC, and posts and event message on the PVC object with the K8S API server. |
684 | 744 | func (h *helper) RecordVolumeEvent(ctx context.Context, name, eventType, reason, message string) { |
@@ -743,6 +803,7 @@ func getVolumeConfig( |
743 | 803 | requisiteTopology, preferredTopology []map[string]string, |
744 | 804 | ) *storage.VolumeConfig { |
745 | 805 | var accessModes []config.AccessMode |
| 806 | + smbShareACL := make(map[string]string) |
746 | 807 |
|
747 | 808 | for _, pvcAccessMode := range pvc.Spec.AccessModes { |
748 | 809 | accessModes = append(accessModes, config.AccessMode(pvcAccessMode)) |
@@ -820,6 +881,7 @@ func getVolumeConfig( |
820 | 881 | Namespace: pvc.Namespace, |
821 | 882 | RequestName: pvc.Name, |
822 | 883 | SkipRecoveryQueue: getAnnotation(annotations, AnnSkipRecoveryQueue), |
| 884 | + SMBShareACL: smbShareACL, |
823 | 885 | } |
824 | 886 | } |
825 | 887 |
|
@@ -858,3 +920,68 @@ func processPVCAnnotations(pvc *v1.PersistentVolumeClaim, fsType string) map[str |
858 | 920 |
|
859 | 921 | return annotations |
860 | 922 | } |
| 923 | + |
| 924 | +// processSCAnnotations updates the annotations map with SC annotations. |
| 925 | +func processSCAnnotations(sc *k8sstoragev1.StorageClass) map[string]string { |
| 926 | + annotations := sc.Annotations |
| 927 | + if sc.Annotations == nil { |
| 928 | + annotations = make(map[string]string) |
| 929 | + } |
| 930 | + |
| 931 | + return annotations |
| 932 | +} |
| 933 | + |
| 934 | +// getSMBShareAccessControlFromPVCAnnotation parses the smbShareAccessControl annotation and updates the smbShareACL map |
| 935 | +func getSMBShareAccessControlFromPVCAnnotation(smbShareAccessControlAnn string) (map[string]string, error) { |
| 936 | + // Structure to hold the parsed smbShareAccessControlAnnotation |
| 937 | + parsedData := make(map[string][]string) |
| 938 | + |
| 939 | + // Parse the smbShareAccessControl annotation |
| 940 | + if err := yaml.Unmarshal([]byte(smbShareAccessControlAnn), &parsedData); err != nil { |
| 941 | + return nil, fmt.Errorf("failed to parse smbShareAccessControl annotation: %v", err) |
| 942 | + } |
| 943 | + |
| 944 | + // Convert the parsed data into the smbShareACL map |
| 945 | + smbShareACL := make(map[string]string) |
| 946 | + |
| 947 | + // Define a priority map for access control permissions, |
| 948 | + // to determine the permission for user who is associated with multiple permissions |
| 949 | + permissionPriority := map[string]int{ |
| 950 | + SMBShareFullControlPermission: 4, // Highest priority |
| 951 | + SMBShareChangePermission: 3, |
| 952 | + SMBShareReadPermission: 2, |
| 953 | + SMBShareNoPermission: 1, // Lowest priority |
| 954 | + } |
| 955 | + |
| 956 | + for accessControlPermission, users := range parsedData { |
| 957 | + if !isValidAccessControlPermission(accessControlPermission) { |
| 958 | + return nil, fmt.Errorf("invalid access control permission %s in smbShareAccessControl annotation, "+ |
| 959 | + "valid access control permissions are %s, %s,%s,%s ", accessControlPermission, SMBShareFullControlPermission, |
| 960 | + SMBShareReadPermission, SMBShareChangePermission, SMBShareNoPermission) |
| 961 | + } else { |
| 962 | + for _, user := range users { |
| 963 | + // Check if the user already exists in the smbShareACL map |
| 964 | + if existingPermission, exists := smbShareACL[user]; exists { |
| 965 | + // Compare the priority of the existing permission with the new one |
| 966 | + if permissionPriority[accessControlPermission] > permissionPriority[existingPermission] { |
| 967 | + smbShareACL[user] = accessControlPermission |
| 968 | + } |
| 969 | + } else { |
| 970 | + // If the user doesn't exist, add it to the map |
| 971 | + smbShareACL[user] = accessControlPermission |
| 972 | + } |
| 973 | + } |
| 974 | + } |
| 975 | + } |
| 976 | + return smbShareACL, nil |
| 977 | +} |
| 978 | + |
| 979 | +// isValidAccessControlPermission checks if the provided permission is valid |
| 980 | +func isValidAccessControlPermission(permission string) bool { |
| 981 | + switch permission { |
| 982 | + case SMBShareFullControlPermission, SMBShareReadPermission, SMBShareChangePermission, SMBShareNoPermission: |
| 983 | + return true |
| 984 | + default: |
| 985 | + return false |
| 986 | + } |
| 987 | +} |
0 commit comments