Skip to content

Настройка JupyterHub + Keycloak

В этой статье мы рассмотрим шаги по интеграции JupyterHub с Keycloak для аутентификации пользователей, а также настройки локального Spawner для создания пользователей с уникальными идентификаторами. 1. Интеграция с Keycloak

Настройки OIDC клиента в jupyterhub https://gitlab.com/truedev.ru/docker-services/jupyterhub/-/blob/main/config/jupyterhub_config.py?ref_type=heads

  1. Использование локального Spawner

По умолчанию будет использоваться LocalSpawner и чтобы избежать ошибки о том, что пользователь не найден, нам придется перед запуском создавать пользователя через pre_spawn_hook.

Но проблема тут в том, что при перезапуске контейнера пользователи будут заново создаваться начиная с id 1000 и права их локальных директории могут иметь отличные от первоначальных и поэтому сервер под пользователем не запуститься если права на директории не совпадут с id заново созданного пользователя после перезапуска контейнера.

Для решения этой проблемы нужен уникальный id, с которым будет создаваться пользователь, этот id мы возьмем из auth_state OIDC.

Чтобы получить ответ auth_state нужно: - выставить JUPYTERHUB_CRYPT_KEY=$(openssl rand -hex 32) - c.GenericOAuthenticator.enable_auth_state = True

Далее создаем auth_state_hook и вызываем его:

def userdata_hook(spawner, auth_state):
    print("Данные в auth_state:", auth_state)
    spawner.userdata = auth_state
c.Spawner.auth_state_hook = userdata_hook

При логине через Keycloak в логах мы увидим ответ в auth_state

Что возвращается в OIDC ответе:
{
    'access_token': '...', 
    'refresh_token': '...', 
    'id_token': '...',
    'scope': ['openid', 'email', 'mattermost', 'gitlab', 'groups', 'profile'
    ],
    'token_response': {'access_token': '...', 'expires_in': 300, 'refresh_expires_in': 1800, 'refresh_token': '..', 'token_type': 'Bearer', 'id_token': '...', 'not-before-policy': 0, 'session_state': '563ea4f4-dd7f-42c1-823e-7e4ac5140897', 'scope': 'openid email mattermost gitlab groups profile'
    },
    'oauth_user': {
        'sub': '5c178977-b5e7-49fa-8935-651056045431',
        'email_verified': False,
        'name': '...',
        'groups': ['/admins', '/chat', '/gitlab', '/ipausers'
        ],
        'preferred_username': '...',
        'id': 1034200016,
        'given_name': '...',
        'family_name': '...',
        'email': '...',
        'username': '...'
    }
}

Т.к мы настроили в keycloak mapping для uidNumber, который является уникальным идентификатором пользователя для freeipa и мапится в id, расположенный в oauth_user, то мы создаем пользователя, используя этот идентификатор через pre_spawn_hook:

## it needs to create users in local system( docker)
from subprocess import check_call
def pre_spawn_hook(spawner):
    username = spawner.user.name
    uid = spawner.userdata.get("oauth_user", {}).get("id")
    print("UID пользователя:", uid)

    try:
        check_call(['useradd', '-ms', '/bin/bash', '-u', str(uid), username])
    except Exception as e:
        print(f'{e}')
c.Spawner.pre_spawn_hook = pre_spawn_hook

Не работают(не стартует сервер внутри jupyterhub) если имена пользователей начинаются на цифру, например 4username

general:
bugfixes: