Create django super user in a docker container without inputting password

39,760

Solution 1

I recommend adding a new management command that will automatically create a superuser if no Users exist.

See small example I created at https://github.com/dkarchmer/aws-eb-docker-django. In particular, see how I have a python manage.py initadmin which runs:

class Command(BaseCommand):

    def handle(self, *args, **options):
        if Account.objects.count() == 0:
            for user in settings.ADMINS:
                username = user[0].replace(' ', '')
                email = user[1]
                password = 'admin'
                print('Creating account for %s (%s)' % (username, email))
                admin = Account.objects.create_superuser(email=email, username=username, password=password)
                admin.is_active = True
                admin.is_admin = True
                admin.save()
        else:
            print('Admin accounts can only be initialized if no Accounts exist')

(See Authentication/management/commands).

You can see how the Dockerfile then just runs CMD to runserver.sh which basically runs

python manage.py migrate --noinput
python manage.py initadmin
python manage.py runserver 0.0.0.0:8080

Obviously, this assumes the Admins immediately go change their passwords after the server is up. That may or may not be good enough for you.

Solution 2

Get the container ID and run the command.

docker exec -it container_id python manage.py createsuperuser

Solution 3

Disclaimer:

Storing the passwords plaintext in the Dockerfile is insecure as the passwords can be extracted from the image at any time and the Dockerfiles are usually committed to version control. However, this answer is not about password security, rather about automating the createsuperuser command; if you are looking for a proper way to store the superuser password, take a look at this SO question: Docker and securing passwords .


I handle this by evaluating the python code line in Dockerfile.

ENV DJANGO_DB_NAME=default
ENV DJANGO_SU_NAME=admin
ENV [email protected]
ENV DJANGO_SU_PASSWORD=mypass

RUN python -c "import django; django.setup(); \
   from django.contrib.auth.management.commands.createsuperuser import get_user_model; \
   get_user_model()._default_manager.db_manager('$DJANGO_DB_NAME').create_superuser( \
   username='$DJANGO_SU_NAME', \
   email='$DJANGO_SU_EMAIL', \
   password='$DJANGO_SU_PASSWORD')"

Note that this is different from calling

User.objects.create_superuser('admin', '[email protected]', 'pass')

as django.contrib.auth.get_user_model will work fine with custom user model if you should have any (which is quite common), while with User.objects.create you only create a standard user entity, ignoring any custom user model.

Also, it's the same call that django's createsuperuser command does under the hood, so it should be pretty safe to do.

Solution 4

Use environment variables and non interactive mode. So you add something like this in your env file.

DJANGO_SUPERUSER_PASSWORD=**********
[email protected]
DJANGO_SUPERUSER_USERNAME=admin

Then, in your docker entrypoint file, add these commands:

python manage.py makemigrations
python manage.py migrate
python manage.py createcachetable

if [ "$DJANGO_SUPERUSER_USERNAME" ]
then
    python manage.py createsuperuser \
        --noinput \
        --username $DJANGO_SUPERUSER_USERNAME \
        --email $DJANGO_SUPERUSER_USERNAME
fi

$@

Note there is no need to put the password, as Django's createsuperuser script takes that from DJANGO_SUPERUSER_PASSWORD by default in noninteractive mode.

This will run the migrations and create the admin user as needed when the container is started using environment variables.

Solution 5

I would suggest running a Data Migration, so when you startup your Docker services (e.g. app & db) via docker-compose up, you can execute all migrations exactly once docker-compose exec web python code/manage.py migrate

So your migration would look like this (assuming you store credentials etc. in environment variables)

import os
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('<your_app>', '<previous_migration>'),
    ]

    def generate_superuser(apps, schema_editor):
        from django.contrib.auth.models import User

        DJANGO_DB_NAME = os.environ.get('DJANGO_DB_NAME', "default")
        DJANGO_SU_NAME = os.environ.get('DJANGO_SU_NAME')
        DJANGO_SU_EMAIL = os.environ.get('DJANGO_SU_EMAIL')
        DJANGO_SU_PASSWORD = os.environ.get('DJANGO_SU_PASSWORD')

        superuser = User.objects.create_superuser(
            username=DJANGO_SU_NAME,
            email=DJANGO_SU_EMAIL,
            password=DJANGO_SU_PASSWORD)

        superuser.save()

    operations = [
        migrations.RunPython(generate_superuser),
    ]

This allows you to use a built container to execute against a database, whether it's a local db in the same container or a separate service. And it's not done every time you rebuild your container, but only when the migration is necessary.

Share:
39,760
Liao Zhuodi
Author by

Liao Zhuodi

Updated on August 01, 2022

Comments

  • Liao Zhuodi
    Liao Zhuodi over 1 year

    I am tring to createsuperuser in a django docker container with fabric.

    To create the super user in django, I need run this in a django interactive mode:

    ./manage.py createsuperuser
    

    And because I want to make it run in a fabric script, so I find this command could avoid inputing password

    echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', '[email protected]', 'pass')" | ./manage.py shell
    

    Then I put this together with "docker exec" to run it in my django container

    docker exec container_django echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', '[email protected]', 'pass')" | ./manage.py shell
    

    The problem comes out with the linux pipe, the pipe(|) all the contents on its left(including the docker exec) to its right(./manage.py shell)

    And this is not only difficult part, considering to put all these junks into a fabric run, which means they need quotes on both end. It will make whole thing very urgly.

    fabric run:
    run("docker exec container_django {command to create django super user}")
    

    I am still struggling on how to make at least the junk work in a fabric run, but I don't know how to do it.