Run the single user pod with ads uid & gid

Hi ,

How to configure Jupyterhub single user pod to run with ads uid & gid?

Thanks

Does ads mean Active Directory? If so have a look at

That example is for LDAP, but you should be able to modify it to work with AD. In addition to the overridden authenticator you need to use a jupyter/docker-stacks image which can switch UID and GID at startup.

Thanks @manics
Yes ads means Active Directory. Is there any pre-built image with the above configuration?
My environment is little complicated, where i can’t build the images directly.

The JupyterHub Helm chart contains all required dependencies, and everything else should be configurable using the configuration files, including extending the authenticator.

For other platforms you’ll need to manage the dependencies yourself.

@manics I’m stuck at this phase when building a docker image with the below dockerfile.

Collecting package metadata (current_repodata.json): ...working... done
Solving environment: ...working... failed with initial frozen solve. Retrying with flexible solve.
Collecting package metadata (repodata.json): ...working... done
Solving environment: ...working...

Those singleuser images are pre built and available on quay.io:
Running a Container — Docker Stacks documentation

It’s the hub image that you’ll need to customise unless you’re using the Z2JH Helm chart.

Hi @manics

I’m using default Z2JH helm chart. I didn’t make any changes to the script.
I made changes to the authenticator and i can see the UID & GID in the logs. But not able to login to the jupyterhub.

[D 2025-06-18 16:13:29.532 JupyterHub ldapauthenticator:532] Attempting to bind user-account
[D 2025-06-18 16:13:30.238 JupyterHub ldapauthenticator:559] Successfully bound user-account
[D 2025-06-18 16:13:30.238 JupyterHub ldapauthenticator:446] Looking up user with:
        search_base = 'DC=ads,DC=abc,DC=com'
        search_filter = '(sAMAccountName=userid)'
        attributes = '[cn]'
[D 2025-06-18 16:13:30.278 JupyterHub ldapauthenticator:532] Attempting to bind CN=username,OU=abc,OU=People,DC=ads,DC=abc,DC=com
[D 2025-06-18 16:13:31.073 JupyterHub ldapauthenticator:559] Successfully bound CN=username,OU=abc,OU=People,DC=ads,DC=abc,DC=com
[D 2025-06-18 16:13:31.112 JupyterHub ldapauthenticator:701] username:username attributes:{'uidNumber': [00000], 'gidNumber': [00000], 'uid': ['userid']}
[W 2025-06-18 16:13:31.113 JupyterHub metrics:456] Event loop was unresponsive for at least 1.54s!
[W 2025-06-18 16:13:31.113 JupyterHub auth:739] User 'userid' not allowed.
[D 2025-06-18 16:13:31.113 JupyterHub log:192] 200 GET /hub/health (@0.0.0.0) 0.50ms
[W 2025-06-18 16:13:31.113 JupyterHub base:979] Failed login for rgadh1
[W 2025-06-18 16:13:31.114 JupyterHub log:192] 403 POST /hub/login?next=%2Fhub%2F (@::ffff:) 1585.39ms
[D 2025-06-18 16:13:33.094 JupyterHub log:192] 200 GET /hub/health (@) 0.40ms
[D 2025-06-18 16:13:35.094 JupyterHub log:192] 200 GET /hub/health (@) 0.55ms
[D 2025-06-18 16:13:35.094 JupyterHub log:192] 200 GET /hub/health (@) 0.54ms

It sounds like you’re missing some configuration to allow the user.

I’m using below config.

hub:
  config:
    # JupyterHub:
    #   authenticator_class: ldapauthenticator.LDAPAuthenticator
    Authenticator:
      enable_auth_state: true
      allow_all: true
    LDAPAuthenticator:
      # See https://github.com/rroemhild/docker-test-openldap#ldap-structure
      # for users
      server_address: ldap-test-openldap
      lookup_dn: True
      bind_dn_template: "cn={username},ou=people,dc=planetexpress,dc=com"
      user_search_base: "ou=people,dc=planetexpress,dc=com"
      user_attribute: uid
      lookup_dn_user_dn_attribute: cn
      escape_userdn: True
      auth_state_attributes: ["uid", "cn", "mail", "ou"]
      use_lookup_dn_username: False
  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
singleuser:
  uid: 0
  extraEnv:
    GRANT_SUDO: "yes"
    NOTEBOOK_ARGS: "--allow-root"

Is that your actual configuration, or an example? It doesn’t match your logs.

Assuming you’ve anonymised your config, I think you’ll need to talk to your Active Directory administrator to check the settings are correct.