diff --git a/kubespawner/objects.py b/kubespawner/objects.py index 9c5a59de..76ba5648 100644 --- a/kubespawner/objects.py +++ b/kubespawner/objects.py @@ -30,6 +30,7 @@ def make_pod( node_selector=None, run_as_uid=None, fs_gid=None, + supplemental_gids=None, run_privileged=False, env={}, working_dir=None, @@ -83,6 +84,15 @@ def make_pod( volume types that support this (such as GCE). This should be a group that the uid the process is running as should be a member of, so that it can read / write to the volumes mounted. + supplemental_gids: + A list of GIDs that should be set as additional supplemental groups to + the user that the container runs as. You may have to set this if you are + deploying to an environment with RBAC/SCC enforced and pods run with a + 'restricted' SCC which results in the image being run as an assigned + user ID. The supplemental group IDs would need to include the + corresponding group ID of the user ID the image normally would run as. + The image must setup all directories/files any application needs access + to, as group writable. run_privileged: Whether the container should be run in privileged mode. env: @@ -143,6 +153,8 @@ def make_pod( security_context = V1PodSecurityContext() if fs_gid is not None: security_context.fs_group = int(fs_gid) + if supplemental_gids is not None and supplemental_gids: + security_context.supplemental_groups = [int(gid) for gid in supplemental_gids] if run_as_uid is not None: security_context.run_as_user = int(run_as_uid) pod.spec.security_context = security_context diff --git a/kubespawner/spawner.py b/kubespawner/spawner.py index dc9fd297..56985bc6 100644 --- a/kubespawner/spawner.py +++ b/kubespawner/spawner.py @@ -449,6 +449,32 @@ def _hub_connect_port_default(self): """ ) + singleuser_supplemental_gids = Union([ + List(), + Callable() + ], + allow_none=True, + config=True, + help=""" + A list of GIDs that should be set as additional supplemental groups to the + user that the container runs as. + + Instead of a list of integers, this could also be a callable that takes as one + parameter the current spawner instance and returns a list of integers. The + callable will be called asynchronously if it returns a future, rather than + a list. Note that the interface of the spawner class is not deemed stable + across versions, so using this functionality might cause your JupyterHub + or kubespawner upgrades to break. + + You may have to set this if you are deploying to an environment with RBAC/SCC + enforced and pods run with a 'restricted' SCC which results in the image being + run as an assigned user ID. The supplemental group IDs would need to include + the corresponding group ID of the user ID the image normally would run as. The + image must setup all directories/files any application needs access to, as group + writable. + """ + ) + singleuser_privileged = Bool( False, config=True, @@ -833,6 +859,11 @@ def get_pod_manifest(self): else: singleuser_fs_gid = self.singleuser_fs_gid + if callable(self.singleuser_supplemental_gids): + singleuser_supplemental_gids = yield gen.maybe_future(self.singleuser_supplemental_gids(self)) + else: + singleuser_supplemental_gids = self.singleuser_supplemental_gids + if self.cmd: real_cmd = self.cmd + self.get_args() else: @@ -851,6 +882,7 @@ def get_pod_manifest(self): node_selector=self.singleuser_node_selector, run_as_uid=singleuser_uid, fs_gid=singleuser_fs_gid, + supplemental_gids=singleuser_supplemental_gids, run_privileged=self.singleuser_privileged, env=self.get_env(), volumes=self._expand_all(self.volumes),