Creating the superuser of a Django-based application is usually done by running the command
manage.py createsuperuser
, hence it requires shell access. This makes sense as shell access
implies the person should also have admin privileges. However, shell access can be clunky
(think of how to run something in a container, kubectl get pods -n chris && kubectl exec -it -n chris <pod_name> python manage.py createsuperuser
...).
We would prefer a declarative approach.
In production, ChRIS is deployed using Helm. Our Helm chart has a feature for automatically creating the superuser.
https://github.com/FNNDSC/charts/pull/2
Configuration
The superuser's username and password can be configured by values. That means either creating a
values.yaml
file containing
chris_admin:
username: admin
password: chris1234
or by passing the values via command-line arguments:
helm upgrade --install --set chris_admin.username=admin --set chris_admin.password=chris1234 chris fnndsc/chris
The default superuser username is khris
. If unspecified, the chart will either:
- If a password was set by a previous release, reuse the same password
- Randomly generate a password and save it as a Kubernetes Secret
After running helm install
or helm upgrade
, a copy-pastable command for reading the password is printed out:
NAME: khris
LAST DEPLOYED: Thu Oct 5 08:44:41 2023
NAMESPACE: chris
STATUS: deployed
REVISION: 1
NOTES:
The ChRIS backend is being deployed. Please wait for it to be ready.
You can run this command to block while the server is starting up:
kubectl wait --for=condition=ready pod -n chris -l app.kubernetes.io/instance=khris-heart --timeout=300s
After that, try logging in as the admin user. The username is "khris".
The password can be revealed by running the command
kubectl get secret -n chris khris-chris-superuser -o jsonpath='{.data.password}' | base64 --decode
Creating the User using an initContainer
initContainers
are defined to create the superuser during application startup.
initContainers:
- name: wait-db
image: docker.io/bitnami/postgresql:16.0.0-debian-11-r3
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- until pg_isready -U postgres -h {{ .Release.Name }}-postgresql -p 5432; do sleep 1; done
# envFrom: not shown
- name: migratedb
command: ["python", "manage.py", "migrate", "--noinput"]
# envFrom: not shown
- name: create-superuser
command:
- python
- manage.py
- shell
- -c
- |
import os
from django.contrib.auth.models import User
user_config = {
'username': os.environ['CHRIS_SUPERUSER_USERNAME'],
'password': os.environ['CHRIS_SUPERUSER_PASSWORD'],
'email': os.environ['CHRIS_SUPERUSER_EMAIL']
}
if (existing_user := User.objects.filter(username=user_config['username']).first()) is not None:
existing_user.set_password(user_config['password'])
existing_user.save()
print(f'Updated password for user "{existing_user.username}"')
else:
created_user = User.objects.create_superuser(**user_config)
print(f'Created superuser "{created_user.username}"')
env:
- name: CHRIS_SUPERUSER_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-chris-superuser
key: username
- name: CHRIS_SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-chris-superuser
key: password
- name: CHRIS_SUPERUSER_EMAIL
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-chris-superuser
key: email
Worthy Mentions
Other projects have other solutions to superuser creation. For example, Authentik lets the first person to visit the UI to create a superuser account.