Creating an admin user automatically with NativeAuthenticator

So here’s my situation. I’m provisioning JupyterHub on a Kubernetes cluster using the helm chart (version 1.2.0) which uses the docker image (jupyterhub/k8s-hub:1.2.0). I’m using NativeAuthenticator.

I’ve configured the app such that I’ve made the user with name “root_admin” a valid admin in JupyterHub.

Now my trouble comes from how I can setup this user’s credentials. I want to use secrets provisioned separately so ideally I don’t want to use the UI.

From what I’ve learned, I can use adduser command to add a user. Which is great for the moment, but the docker container starts with the user jovyan so now I don’t have inherent root access to create the new user. I can potentially have the docker container run as root but that can always cause problems down the line. The other thing I’ve thought of is creating a user in the dockerfile who only has permissions to create user accounts, that way we can use this user to create the JuypterHub admin account, only that I don’t know 1 if this is possible or 2 if it poses any security risks.

So I’ve gone back to the original issue where I am unable to automatically create the admin account.

Are there any tips for this? Or advice as to how others provision JupyterHub for their org? Any thoughts or advice will be helpful.

Thanks in advance!

I don’t believe adduser will be relevant. NativeAuthenticator has a bit of a confusing name, because there’s more than one thing it could be native to. It’s not native to the system (system users are ignored), it’s native to jupyterhub (i.e. a totally internal user/password db).

I don’t believe NativeAuthenticator has support for pre-populating the password db. I think this is a reasonable feature request to add a ‘preload passwords’ feature.

But once you have launched NativeAuthenticator, you can add users and passwords with python -m jupyterhub.dbutil shell interactively attached to a running Hub:

kubectl exec -it hub-.... -- bash
# in pod
pip install ipython # needed for dbutil.shell
ipython
# in IPython
from nativeauthenticator import NativeAuthenticator
auth = NativeAuthenticator(db=db) # db variable is pre-populated in the shell
auth.create_user("username", "password")

Or you could create the UserInfo record directly, if you want to only store the bcrypt-hashed password, instead of needing to store the cleartext password at any point:

First, create encrypted fields to be stored at rest

import bcrypt
username = "username"
hashed_password = bcrypt.hashpw("cleartext password".encode(), bcrypt.gensalt())

Then add them to the db (again, following dbutil.shell above, or by running a Python script that connects an sqlalchemy session to db_url, which is `jupyterhub):

# connect to db if not using jupyterhub.dbutil shell
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from nativeauthenticator.orm import UserInfo

db_url = "sqlite:///jupyterhub.sqlite" # may be different!
db = sessionmaker(bind=create_engine(db_url))()


# create user (or iterate through list of users!)
user = UserInfo(username=username, password=hashed_password)
db.add(user)
db.commit()

A handy way to run Python scripts in a pod, from the host in kubernetes:

cat script.py | kubectl exec -i hub-... - python3
4 Likes

fyi, this worked great. Thanks a bunch for the advice! :smiley:

1 Like

Great answer @minrk - this should be added to the docs. Minor notes:

In the newest images, python3 -m jupyterhub.dbutil shell is needed.

Also, I got an error when adding the admin user ( <nativeauthenticator.orm.UserInfo at 0x7f168fd5baf0>) and looked at the JupyterHub test:

from nativeauthenticator.orm import UserInfo
auth.admin_users = {"johnsnow"}
auth.open_signup = True
auth.create_user("johnsnow", "password")
user_info = UserInfo.find(db, "johnsnow")
UserInfo.change_authorization(db, "johnsnow")
user_info = UserInfo.find(db, "johnsnow")
print(user_info.is_authorized)

True

… then it works. :+1:

Also, note the comment in quickstart.html#adding-new-users:

It is important to note that admin accounts must also be created through signup.
However, usernames listed in the config file as admins (see below) will automatically
have authorization to enter the system.

So if you have a config.yaml with NativeAuthenticator that specifies an admin user:

hub:
  config:
    JupyterHub:
      admin_access: false
      authenticator_class: nativeauthenticator.NativeAuthenticator
    Authenticator:
      admin_users:
        - johnsnow
    NativeAuthenticator:
      enable_signup: true
      minimum_password_length: 10
      check_common_password: true
      ask_email_on_signup: true
      allow_2fa: false

… you should be automatically admin, after sign up through the interface.

1 Like