@@ -252,44 +252,74 @@ func isUserAllowedForDeletion(username string) (bool, error) {
252252}
253253
254254func validatePvCSIServiceAccount (username string ) (bool , error ) {
255+ ctx := context .TODO ()
256+ log := logger .GetLogger (ctx )
257+
258+ log .Infof ("Validating PvCSI service account: username=%s" , username )
259+
255260 // Expected format: "system:serviceaccount:namespace:service-account-name"
256261 // Parse the username to extract namespace and service account name
257262 const prefix = "system:serviceaccount:"
258263 if ! strings .HasPrefix (username , prefix ) {
264+ log .Infof ("Username doesn't have service account prefix, returning false" )
259265 return false , nil
260266 }
261267
262268 remaining := strings .TrimPrefix (username , prefix )
263269 parts := strings .Split (remaining , ":" )
270+ log .Infof ("Parsed service account parts: %v (count: %d)" , parts , len (parts ))
271+
264272 if len (parts ) != 2 {
273+ log .Infof ("Invalid service account format - expected 2 parts, got %d, returning false" , len (parts ))
265274 return false , nil
266275 }
267276
268277 namespace := parts [0 ]
269278 serviceAccountName := parts [1 ]
279+ log .Infof ("Extracted namespace=%s, serviceAccountName=%s" , namespace , serviceAccountName )
270280
271281 // Check explicit service account names first
272282 if isExplicitPvCSIServiceAccount (namespace , serviceAccountName ) {
283+ log .Infof ("Service account is explicit PvCSI account, returning true" )
273284 return true , nil
274285 }
275286
276287 // For vmware-system-csi namespace, validate ProviderServiceAccount by getting
277288 // the actual vSphere cluster name and constructing the expected service account name
278289 if namespace == "vmware-system-csi" {
290+ log .Infof ("vmware-system-csi namespace detected, validating as ProviderServiceAccount" )
279291 return validateProviderServiceAccount (serviceAccountName )
280292 }
281293
294+ // For any namespace, check if service account follows guest cluster PvCSI pattern
295+ // Guest cluster PvCSI service accounts follow the pattern: {cluster-name}-pvcsi
296+ if strings .HasSuffix (serviceAccountName , "-pvcsi" ) {
297+ log .Infof ("Service account ends with -pvcsi, validating as guest cluster PvCSI account" )
298+ return validateGuestClusterPvCSIServiceAccount (namespace , serviceAccountName )
299+ }
300+
301+ log .Infof ("Service account doesn't match any PvCSI patterns, returning false" )
282302 return false , nil
283303}
284304
285305// isExplicitPvCSIServiceAccount checks for known explicit service account names
286306func isExplicitPvCSIServiceAccount (namespace , serviceAccountName string ) bool {
307+ ctx := context .TODO ()
308+ log := logger .GetLogger (ctx )
309+
310+ log .Infof ("Checking explicit PvCSI service account: namespace=%s, serviceAccount=%s" , namespace , serviceAccountName )
311+
287312 switch namespace {
288313 case "vmware-system-csi" :
289- return serviceAccountName == "vsphere-csi-controller" || serviceAccountName == "vsphere-csi-node"
314+ isExplicit := serviceAccountName == "vsphere-csi-controller" || serviceAccountName == "vsphere-csi-node"
315+ log .Infof ("vmware-system-csi namespace check: serviceAccount=%s, isExplicit=%t" , serviceAccountName , isExplicit )
316+ return isExplicit
290317 case "kube-system" :
291- return serviceAccountName == "pvcsi"
318+ isExplicit := serviceAccountName == "pvcsi"
319+ log .Infof ("kube-system namespace check: serviceAccount=%s, isExplicit=%t" , serviceAccountName , isExplicit )
320+ return isExplicit
292321 default :
322+ log .Infof ("Non-explicit namespace: %s, serviceAccount=%s, returning false" , namespace , serviceAccountName )
293323 return false
294324 }
295325}
@@ -396,3 +426,46 @@ func validateAgainstAllClusters(ctx context.Context, serviceAccountName string)
396426 // Service account name doesn't match any existing cluster
397427 return false , nil
398428}
429+
430+ // validateGuestClusterPvCSIServiceAccount validates PvCSI service accounts in guest cluster namespaces
431+ // Guest cluster PvCSI service accounts follow the pattern: {cluster-name}-pvcsi
432+ // This function validates that:
433+ // 1. The service account name matches the expected pattern
434+ // 2. The service account exists in a legitimate guest cluster namespace
435+ // 3. No colon injection attacks are possible
436+ func validateGuestClusterPvCSIServiceAccount (namespace , serviceAccountName string ) (bool , error ) {
437+ ctx := context .TODO ()
438+ log := logger .GetLogger (ctx )
439+
440+ // Validate service account name format: must end with "-pvcsi" and not contain colons
441+ if ! strings .HasSuffix (serviceAccountName , "-pvcsi" ) {
442+ log .Debugf ("Guest cluster service account %s doesn't end with -pvcsi" , serviceAccountName )
443+ return false , nil
444+ }
445+
446+ // Extract cluster name from service account name
447+ // Expected format: {cluster-name}-pvcsi
448+ clusterNameFromSA := strings .TrimSuffix (serviceAccountName , "-pvcsi" )
449+
450+ // Validate cluster name doesn't contain colon (prevents colon injection)
451+ if strings .Contains (clusterNameFromSA , ":" ) {
452+ log .Warnf ("Security: Rejected service account %s with colon injection attempt in namespace %s" , serviceAccountName , namespace )
453+ return false , nil
454+ }
455+
456+ // For guest cluster service accounts, we validate by checking if the service account
457+ // follows the expected naming pattern and the namespace seems legitimate
458+ // This is a more permissive validation than supervisor cluster validation
459+
460+ // Basic validation: cluster name should be reasonable length and not empty
461+ if len (clusterNameFromSA ) == 0 || len (clusterNameFromSA ) > 253 {
462+ log .Debugf ("Guest cluster service account has invalid cluster name length: %s" , clusterNameFromSA )
463+ return false , nil
464+ }
465+
466+ // Log for audit purposes
467+ log .Infof ("Validated guest cluster PvCSI service account: %s in namespace %s (cluster: %s)" ,
468+ serviceAccountName , namespace , clusterNameFromSA )
469+
470+ return true , nil
471+ }
0 commit comments