Citrix SecurSpaces™

Configure SecurSpaces for AWS Mount Points

Complete the AWS preparation first. The steps here depend on a file system, its mount targets, and the IAM roles already existing.

Create the StorageClass

You author the StorageClass by hand, one per backing file system. SecurSpaces ships none and creates none. A missing StorageClass is the most common cause of an empty create dialog.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sn-efs-mountpoint
provisioner: efs.csi.aws.com
mountOptions:
  - tls
parameters:
  provisioningMode: efs-ap
  fileSystemId: fs-0abc123
  basePath: "/dyn"
  directoryPerms: "700"
  gidRangeStart: "50000"
  gidRangeEnd: "51000"
reclaimPolicy: Delete
volumeBindingMode: Immediate
<!--NeedCopy-->

Required values:

  • provisioner: efs.csi.aws.com for both backends.
  • parameters.provisioningMode — efs-ap for Amazon EFS, s3files-ap for Amazon S3 Files. This parameter, not any option in the interface, selects the backend. Users choose the backend by choosing the StorageClass, so give each class a clear name.
  • parameters.fileSystemId — the id of the file system you created. It must be present, and it must match exactly. SecurSpaces also uses these ids to tell the file systems it shares from external ones, so a blank or mistyped value costs more than the one class. Check it before anyone uses the class.
  • mountOptions: [tls] — a top-level list, a sibling of parameters, not a key inside it.
  • volumeBindingMode: Immediate — EFS and S3 Files are zoneless. A class set to WaitForFirstConsumer can never bind and the create is rejected.

Optional values include basePath, subPathPattern, directoryPerms, and the pair gidRangeStart and gidRangeEnd, which bound the POSIX group id the access point enforces. Set both or neither. Keep a custom range wide enough for every Mount Point planned on that file system, because each access point consumes one group id.

One file system and one StorageClass serve many Mount Points. Add more only when you exceed the access point ceiling, need throughput isolation, or need hard tenant isolation.

Check the file system id

Nothing validates fileSystemId against AWS when you author the class. SecurSpaces accepts whatever you write and acts on it only when a user creates or attaches a Mount Point. A blank value and a wrong value then fail in different ways.

A blank value fails closed, and the effect is not limited to the class that carries it. One Mount Point StorageClass with no fileSystemId makes SecurSpaces treat its whole picture of which file systems it shares as unreliable, so it refuses rather than guess: the attach mode in which a user types raw coordinates is denied for every class on the cluster, including attaches onto file systems unrelated to the incomplete one.

A wrong value does not immediately switch the protection off. SecurSpaces recognizes the file systems it shares from two independent signals: the ids declared on your Mount Point StorageClasses, and the file systems it has already provisioned Mount Points on in that region. A typo alone does not un-share the real file system. The risk is the window in which the second signal is empty — a file system SecurSpaces has never provisioned on, or one where every Mount Point it created there failed its post-bind identity check. In that window a wrong id makes the real shared file system look external, and a pre-built volume covering the whole of it becomes attachable by an ordinary user.

Read the id back from AWS before anyone uses the class:

aws efs describe-file-systems --file-system-id <fs-id> --query 'FileSystems[0].FileSystemId'
<!--NeedCopy-->

Encryption in transit

EFS and S3 Files are mounted over NFS on port 2049, and the CSI driver only encrypts that traffic when the mount carries the tls option. Set it on the StorageClass and on every PersistentVolume you author. Without it, every byte read or written travels unencrypted.

SecurSpaces keys strictly on the literal tls mount option. It does not accept parameters.encryptInTransit, a TLS-enabled access point, or an EFS file system policy that enforces SecureTransport as substitutes.

How strictly this is enforced differs by mode:

  • Attach Existing fails closed. At bind time SecurSpaces reads the volume you named and refuses the attach unless it is an efs.csi.aws.com volume whose mount options contain tls.
  • Create New is best-effort. A StorageClass without tls is normally hidden from the Create New list, and naming it anyway is rejected. All of these checks depend on reading the StorageClass from the Kubernetes API.

Warning

The StorageClass read is made by the workspace service. If it cannot read StorageClasses, every Create New encryption check is skipped, a StorageClass without tls becomes usable, and the mount runs unencrypted with no error. This is the one failure in this article that fails in the permissive direction.

Cluster permissions

StorageClasses and PersistentVolumes are both cluster-scoped, so the grant that lets the workspace service read them has to be a ClusterRole. The chart installs it as a ClusterRole and a ClusterRoleBinding, both named <release>-workspace-api-clusterwide, bound to the workspace service account <release>-workspace-api. For everything else the platform reaches on the cluster, see Trust boundaries.

Mount Points depend on these read permissions:

Resource Verbs What depends on it
storageclasses get, list Listing the classes offered in Create New, and the encryption in transit check
persistentvolumes get, list Reading back the volume a new Mount Point was given, and listing the volumes offered by Attach Existing

Two Helm settings take the grant away. Setting platform.serviceKubernetesConfig.serviceAccounts.wsAPIServiceAccount to your own service account means the chart installs none of the workspace service RBAC objects, so you must create and bind the equivalent permissions yourself. Setting platform.serviceKubernetesConfig.rbac.createWorkspaceClusterRoles to false removes the whole ClusterRole and its binding: not only the StorageClass read, but the PersistentVolume read, the PersistentVolume write verbs that allowPersistentVolumeAccess would otherwise add, and read access to nodes and node metrics. The namespace-scoped Role the chart installs alongside it is not affected by that setting.

Verify each verb separately. Kubernetes list does not imply get:

kubectl auth can-i get storageclasses --as=system:serviceaccount:<namespace>:<release>-workspace-api
kubectl auth can-i list storageclasses --as=system:serviceaccount:<namespace>:<release>-workspace-api
kubectl auth can-i get persistentvolumes --as=system:serviceaccount:<namespace>:<release>-workspace-api
kubectl auth can-i list persistentvolumes --as=system:serviceaccount:<namespace>:<release>-workspace-api
<!--NeedCopy-->

--as impersonates another identity, which needs cluster-admin or an impersonate grant of your own. On a default install you can also confirm that the objects carrying the grant are present:

kubectl get clusterrole,clusterrolebinding <release>-workspace-api-clusterwide
<!--NeedCopy-->

If you supplied wsAPIServiceAccount, neither of those objects exists and the chart never creates <release>-workspace-api, so the commands above report no for an account nothing runs under. Substitute the account you supplied, which you can read from the running pod:

kubectl -n <namespace> get pod -l app=<release>-workspace-api -o jsonpath='{.items[0].spec.serviceAccountName}'
<!--NeedCopy-->

What each missing permission costs:

  • Missing get on storageclasses skips the encryption checks and allows an unencrypted mount.
  • Missing list on storageclasses breaks discovery entirely: the create list is empty and the feature disappears.
  • Missing get on persistentvolumes lets every Create New Mount Point reach ready without its volume being read back, so the Mount Point holds no record of the volume it was provisioned on.
  • Missing list on persistentvolumes breaks the Attach Existing volume picker, which shows Failed to load available volumes and a Retry link. That is a different state from No volumes available to attach, which means the read succeeded and no volume matched the scope label.

Enable the feature

Mount Point Storage is enabled at System Configuration > Integrations > Mount Point Storage. System Configuration is an administrator-only area. A security officer who is not also an administrator cannot enable it.

  • The cloud provider is detected, not chosen. SecurSpaces derives whether Mount Points are backed by AWS or Azure from the CSI provisioners of the StorageClasses on the cluster. To expose the AWS options, create AWS Mount Point StorageClasses first.
  • Turn on the file storage switch. While it is off, the create flow is hidden from users.
  • Use one cloud per cluster. If both an AWS and an Azure Mount Point StorageClass are present, the provider cannot be resolved, the switches are forced off, and the feature is withdrawn. The page explains this and lists the providers it detected. Remove one cloud’s classes and revisit the page. No restart is needed, but allow about 30 seconds for the configuration cache.
  • allowPersistentVolumeAccess governs one attach mode. The mode in which a user types raw coordinates and SecurSpaces builds the volume requires platform.serviceKubernetesConfig.rbac.allowPersistentVolumeAccess, which defaults to false. While it is off, that mode is hidden, but attaching a volume you authored and creating new Mount Points both still work. The change takes effect when helm upgrade completes, with no pod restart.

Note

Only this page explains why Mount Points are unavailable. In the project and workspace views the option is simply hidden. Triage any report of missing Mount Points by opening this page first.

Next

Configure SecurSpaces for AWS Mount Points