Starting single-user notebook with our custom ldap docker image

Hi,

We are having some problems because our user homes are in a NFS storage and, for them to write there their work, we need to “impersonate” them. On regular machines we are using LDAP (with TLS, PAM and SSSD) to authenticate the users, and so is jupyter-hub.

We are using Kubernetes -> z2jh and a Docker image based on the notebook single-user image with all the necessary files and packages to connect to LDAP.

The idea we originally had was using the variable ‘{username}’ and modify the “start.sh” script, so once we had the real $NB_USER we could set up $NB_UID, $NB_GID and so on. We created a docker container where we could copy the necessary files (I know it would be better to binding them on the host) and we did, but we have to keep running SSSD in the background for the container to be able to connect to LDAP.

[jovyan@jupyter-rcruz ~]$ id rcruz
uid=63200(rcruz) gid=50030(x) groups=50030(x),1405(x),1403(x)

Before having to add supervisord or something similar to run SSSD as a background process, we would like to know if there is a better/easier way to impersonate the user that logged into jupyter hub. For instance, using token id login from hub or something else?

We don’t discard adding to /etc/passwd the user with its username, UID and GID, but we don’t know if we can get that from the login in jupyterhub.

Thanks in advance!

1 Like

Is the only requirement to write to NFS as the logged in LDAP user? Would it be sufficient to run Jupyter as that UID?

Yes, every single user should be able to write on that NFS with its own UID.

I’ve done this before. It requires this change to the LDAPAuthenticator.

You can then configure JupyterHub to extract the required LDAP attributes (username, UID), and pass them to the singleuser server by setting appropriate environment variables. If you start the singleuser server as root it will switch to that UID, which means it should be able to write to NFS as that user. I’ve written up some brief instructions:

2 Likes

Wow!. This is exactly what we want.

I’ve problems in order to get group id, but I don’t worry about it right now.

Thanks a lot :slight_smile: !!

Hello @manics,

thanks very much for the changes you made to LDAP Authenticator. They are exactly what I need in my setup to let users access their home folder mounted via NFS.
I am having no luck with setting this up, so maybe you could have some hints.

I am running Jupyterhub 0.9.6 and deploying it with zero-to-jupyterhub-k8s.
Instead of embedding the change in the config file I am installing your fork of LDAP authenticator in the hub container environment. All good until this step.

In my jupyterhub config I embed the following to make sure the uid is propagated to the singleuser container:

class MyLDAPAuthenticator(LDAPAuthenticator):
    @gen.coroutine
    def pre_spawn_start(self, user, spawner):
        auth_state = yield user.get_auth_state()
        self.log.error('pre_spawn_start auth_state:%s' % auth_state)
        if not auth_state:
            return
        # setup environment
        spawner.environment['NB_UID'] = str(
            auth_state['uidNumber'][0])
        spawner.environment['NB_USER'] = auth_state['uid'][0]

and this to select the authentication method:

c.JupyterHub.authenticator_class = 'MyLDAPAuthenticator'
c.LDAPAuthenticator.server_address = get_config('auth.ldap.server.address')
c.LDAPAuthenticator.user_info_attributes = ['uid', 'uidNumber']
set_config_if_not_none(c.LDAPAuthenticator, 'server_port', 'auth.ldap.server.port')
set_config_if_not_none(c.LDAPAuthenticator, 'use_ssl', 'auth.ldap.server.ssl')
set_config_if_not_none(c.LDAPAuthenticator, 'allowed_groups', 'auth.ldap.allowed-groups')
c.LDAPAuthenticator.bind_dn_template = get_config('auth.ldap.dn.templates')
set_config_if_not_none(c.LDAPAuthenticator, 'lookup_dn', 'auth.ldap.dn.lookup')
set_config_if_not_none(c.LDAPAuthenticator, 'lookup_dn_search_filter', 'auth.ldap.dn.search.filter')
set_config_if_not_none(c.LDAPAuthenticator, 'lookup_dn_search_user', 'auth.ldap.dn.search.user')
set_config_if_not_none(c.LDAPAuthenticator, 'lookup_dn_search_password', 'auth.ldap.dn.search.password')
set_config_if_not_none(c.LDAPAuthenticator, 'lookup_dn_user_dn_attribute', 'auth.ldap.dn.user.dn-attribute')
set_config_if_not_none(c.LDAPAuthenticator, 'escape_userdn', 'auth.ldap.dn.user.escape')
set_config_if_not_none(c.LDAPAuthenticator, 'valid_username_regex', 'auth.ldap.dn.user.valid-regex')
set_config_if_not_none(c.LDAPAuthenticator, 'user_search_base', 'auth.ldap.dn.user.search-base')
set_config_if_not_none(c.LDAPAuthenticator, 'user_attribute', 'auth.ldap.dn.user.attribute')

However at hub startup I get the following error:

[C 2019-07-30 14:17:00.347 JupyterHub application:90] Bad config encountered during initialization:
[C 2019-07-30 14:17:00.347 JupyterHub application:91] The 'authenticator_class' trait of <jupyterhub.app.JupyterHub object at 0x3fffb082af98> instance must be a type, but 'MyLDAPAuthenticator' could not be imported

Any Idea why this is happening? I really can’t see why the MyLDAPAuthenticator cannot be found at hub instantiation time.

Any help or suggestion is highly appreciated.

Thanks in Advance,

Christian

Try removing the quotes:

c.JupyterHub.authenticator_class = MyLDAPAuthenticator

Hi @manics,

I solved by embedding the pre_spawn_start directly in the main LDAPAuthenticator class.
It works perfectly now.

Thanks

Hi,

Please share the dockerfile and config.yaml to replicate the same environment.
We are also having the same requirement.

Thank You
Atul