nebari-dev / nebari-dev/data-science-pack

Security(H-01): default user role and japps service carry credential-admin scopes (admin:auth_state/tokens)

Open
#163 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: security priority: high 🔥
Dominant language
Python
Stars
5
Forks
7
Avg merge
1d 20h
Merged PRs (30d)
13

Description

Summary

The pinned jhub-apps dependency grants credential-administration scopes to both the default JupyterHub user role and the japps service. Combined with auth-state persistence, this puts other users' Keycloak tokens within reach of a broad user-facing service and, potentially, of ordinary user tokens.

Severity: High · CWE-250 (Execution with Unnecessary Privileges)
Validation: Confirmed against HEAD f932d80 on 2026-07-14 (assessed at 69c84f7; unchanged since).

Evidence

  • images/jupyterhub/pixi.toml:32 pins jhub-apps = "==2026.5.1rc1", which builds the custom hub image quay.io/nebari/nebari-data-science-pack-jupyterhub (values.yaml:523-525).
  • The pinned install_jhub_apps (jhub-apps 2026.5.1rc1 jhub_apps/configuration.py) redefines the default user role to include admin:auth_state, and creates a japps-service-role with list:users, list:groups, read:users, read:users:activity, admin:servers, admin:server_state, admin:auth_state, access:services, list:services, read:services, and tokens. This is broader than the original assessment enumerated.
  • config/jupyterhub/00-gateway-auth.py:540 sets enable_auth_state = True; 00-gateway-auth.py:370-375 writes the Keycloak access, refresh, and ID tokens into auth state.
  • config/jupyterhub/03-nebi-envs.py:66-76 reads /hub/api/users/{username} with the japps service token to retrieve auth_state; the comment at 03-nebi-envs.py:60-64 states this fallback depends on the service token's admin:auth_state.

Impact

admin:auth_state is read access to a user's stored authentication state, and tokens is authority over user tokens. With auth state holding real Keycloak refresh tokens, a compromise of the user-facing japps service exposes a token that can enumerate users, read their auth state, and mint tokens. Because the default user role also carries admin:auth_state, an ordinary user token may be able to read another user's auth state directly depending on endpoint behavior. That specific token path was not dynamically exercised and should be treated as a required release test rather than a proven fact.

Remediation

  • Remove admin:auth_state from the default user role.
  • Remove tokens and global admin:auth_state from the japps service role; grant only the minimum server-management scopes it demonstrably needs.
  • Replace raw IdP-token access with a narrow hub-side broker that returns a short-lived, audience-bound Nebi credential for the current subject.
  • Keep refresh tokens and the hub OIDC secret inside the smallest authentication component.
  • Rotate hub and Keycloak credentials if broad tokens may have been reachable by untrusted users.

Acceptance criteria

  • The default user role contains no admin:* or tokens scope.
  • Ordinary users receive 403 for another user's auth state and tokens.
  • The japps service receives 403 for cross-user auth state and token creation.
  • Environment listing works without exposing raw refresh tokens to japps.
  • Negative tests cover admin, ordinary-user, service, browser-OAuth, and named-server tokens.

Source: data-science-pack 0.1.0 security assessment (pinned commit 69c84f72df259ec755ed40bfc83f20158c550d55), finding H-01.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the referenced role and auth-state handling in images/jupyterhub/pixi.toml, config/jupyterhub/00-gateway-auth.py, and config/jupyterhub/03-nebi-envs.py. Trace how the japps service retrieves auth_state, then use the acceptance criteria to define the required permission failures, token protection, and working environment listing across the named token types.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authentication, authorization, backend, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.