KubeSpawner and LDAPauthentication run under users LDAP UID

Hello,

I’m using LDAP authentication to authenticate users for our Jupyterhub running in Kubernetes (using z2jh helm chart).

Currently, I’m trying to get the singleuser servers to run as the user, with the proper UID and GID provided from LDAP. I can set that up just fine using auth_state_attributes and can pull that in through extraConfig to set NB_USER, NB_UID, NB_GID but am still running into issues getting the singleuser server to run with those respectively.

I’ve tried following the solution provided in Starting single-user notebook with our custom ldap docker image - #4 by manics but I’m still unable to actually get the UID and GID correct.

When trying to run with the singleuser UID set to 0, I get
[C 2021-04-29 19:24:22.134 SingleUserNotebookApp notebookapp:2204] Running as root is not recommended. Use --allow-root to bypass.

extraEnv: GRANT_SUDO: "yes" NOTEBOOK_ARGS: "--allow-root"

Doesn’t seem to resolve it. Any ideas what the issue might be? Also are there ways to do this without starting as root? Possibly with custom images?

Hi! Please could you give us:

  • Your full Z2JH config with secrets redacted
  • Your version of Z2JH, and details of any customisations

Thanks!

Sure thing:

Here’s our Z2JH config:

hub:
  config:
    Authenticator:
      enable_auth_state: true
    CryptKeeper:
      keys:
        - SECRET
    LDAPAuthenticator:
      bind_dn_template:
      - HIDDEN
      server_address: HOSTNAME
      auth_state_attributes: [uidNumber,gidNumber,uid]
      user_search_base: HIDDEN
  db:
    pvc:
      storageClassName: storage-k8s
  extraConfig:
    configClass: |
      c.JupyterHub.authenticator_class = LDAPAuthenticatorExtend
    extendedLDAP: |
      from tornado import gen
      from ldapauthenticator import LDAPAuthenticator
      class LDAPAuthenticatorExtend(LDAPAuthenticator):
        @gen.coroutine
        def pre_spawn_start(self, user, spawner):
          self.log.debug('running preSpawn hook')
          auth_state = yield spawner.user.get_auth_state()
          self.log.debug('pre_spawn_start auth_state:%s' % auth_state)
          spawner.environment["NB_UID"] = str(auth_state["uidNumber"][0])
          spawner.environment["NB_GID"] = str(auth_state["gidNumber"][0])
          spawner.environment["NB_USER"] = str(auth_state["uid"][0])
          c.KubeSpawner.uid = str(auth_state["uidNumber"][0])
        
    logging: |
      c.JupyterHub.log_level = 'DEBUG'
      c.KubeSpawner.debug = True
      c.LocalProcessSpawner.debug = True
ingress:
  annotations:
    acme.cert-manager.io/http01-edit-in-place: "true"
    cert-manager.io/cluster-issuer: letsencrypt-http01-staging
    kubernetes.io/ingress.class: jupyterhub-ingress
  enabled: true
  hosts:
  - HOSTNAME
  tls:
  - hosts:
    - HOSTNAME
    secretName: SECRETNAME
proxy:
  secretToken: SECRET
  service:
    type: ClusterIP
singleuser:
  defaultUrl: /lab
  uid: 0
  extraEnv:
    GRANT_SUDO: "yes"
    NOTEBOOK_ARGS: "--allow-root"
  storage:
    extraVolumeMounts:
    - mountPath: /home/{username}
      name: nfs-home-{username}-volume
    - mountPath: /data
      name: data-mount-volume
    extraVolumes:
    - name: nfs-home-{username}-volume
      nfs:
        path: /path/to/home
        server: HOSTNAME
    - name: data-mount-volume
      hostPath:
        path: /mnt/data
    type: none
  profilelist:
    - display_name: "Default environment"
      description: "2 CPUs/4GB Memory"
      kubespawner_override:
        cpu_guarantee: 2
        mem_guarantee: 4

Z2JH Version: jupyterhub-0.11.1

No other specific customizations.

I suppose to note, if I don’t set the UID to 0 for the singleuser server, it starts and the NB_UID and NB_GID will be set in the environment, but the container won’t be running under that UID. Setting the singleuser UID to 0 and the pod doesn’t start.

Thanks!

The magic to automatically switch user inside the singleuser image happens in a script:

Z2JH overrides the script, and runs jupyterhub-singleuser instead.

If you revert the override by setting it to empty:

singleuser:
  cmd:

the start.sh script should be automatically called to take care of switching user, and you won’t need NOTEBOOK_ARGS: "--allow-root" any more.

1 Like

Thanks much! That does allow the notebook to run as the specified user.

Now though, it seems that the singleuser.defaultUrl in the Z2JH config is no longer used.

I’ve tried a few different ways to set it, like c.Spawner.default_url = '/lab and setting certain environment variables, but doesn’t seem to work. Will probably have to see how that gets set into the singleuser command. Perhaps the additional flags aren’t being set?

Seems like you need to specify singleuser.cmd: "start-notebook.sh", otherwise arguments aren’t passed to the init script.

Yes, unfortunately it’s a bit complicated… If you want to use the default command from the Docker image you can unset singleuser.cmd, but if you want to use the default command with other Spawner parameters you have to explicitly add the default, which as you figured out is start-notebook.sh.

See this issue for more details:

Old thread, but hoping @manics you can shed some light in my z2jh deployment if you get a chance.

I have managed with your examples to get NB_UID etc resolving to the right user attributes inside the pod, when I set “cmd:” as blank with uid=0, I get a permissions issue when the default script tries to run as it seems to be trying to write stuff to the NFS folder (/home//.local) before switching to running as the user (My NFS user folders are tied down with 700 permissions.).

I copied your start.sh above to my container, and called it from the cmd: option, and the logs show it ran ok (no errors at least), the container completes successfully (according to k8s) but the pod doesn’t stay running.

So what’s running when I leave cmd: as blank? And how do I get the pod to stay running if I call your start.sh using cmd: option?

thanks

I’m looking to implement exactly this though we haven’t been able to get this working. Is someone using this now?