Create django super user in a docker container without inputting password
Asked Answered
F

12

53

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.

Fingertip answered 4/5, 2015 at 9:52 Comment(1)
Try the easier way: https://mcmap.net/q/121085/-django-deployment-with-docker-create-superuserAnnulment
U
39

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.

Ulphiah answered 20/6, 2015 at 1:34 Comment(4)
Note that you can also just manually run the initadmin with docker run --rm python manage.py initadmin which is easy to do from your fabfileUlphiah
To make it slightly more secure perhaps you can feed the default admin password by environment variable, the same as what you will do in database credentials in settings.pyHedveh
More secure from what? This is only an issue on the first release. Even if somebody was to know and login before you and change the password, you just bring the site back down and do it again. Just think about the possibility of somebody knowing you are releasing your first site and beating you on the login. This is the definition of not an issue.Ulphiah
I believe the following commands are not needed: admin.is_active = True, admin.is_admin = True e admin.save(). You are using create_superuser().Liquor
E
67

Get the container ID and run the command.

docker exec -it container_id python manage.py createsuperuser
Epencephalon answered 20/8, 2018 at 7:45 Comment(2)
This doesn't solve the problemVengeance
how to append username email and passwd as argument???Wesla
U
39

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.

Ulphiah answered 20/6, 2015 at 1:34 Comment(4)
Note that you can also just manually run the initadmin with docker run --rm python manage.py initadmin which is easy to do from your fabfileUlphiah
To make it slightly more secure perhaps you can feed the default admin password by environment variable, the same as what you will do in database credentials in settings.pyHedveh
More secure from what? This is only an issue on the first release. Even if somebody was to know and login before you and change the password, you just bring the site back down and do it again. Just think about the possibility of somebody knowing you are releasing your first site and beating you on the login. This is the definition of not an issue.Ulphiah
I believe the following commands are not needed: admin.is_active = True, admin.is_admin = True e admin.save(). You are using create_superuser().Liquor
L
37

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_EMAIL
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.

Lebron answered 24/9, 2020 at 15:52 Comment(4)
My Django's createsuperuser script do not takes the password from DJANGO_SUPERUSER_PASSWORD. Why? How do I test?Liquor
Remember to export to environment variable DJANGO_SUPERUSER_PASSWORD with export command or in docker environment. I loaded it with source .env and it set the value to the variable but docker-compose.yml couldn't find it. As for USERNAME and EMAIL the source worked.Liquor
@TacianoMoraisSilva this is works only for Django >= 3.0Louis
It seems that in --email $DJANGO_SUPERUSER_USERNAME it should say --email $DJANGO_SUPERUSER_EMAILChef
V
16

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.

Veiled answered 8/8, 2017 at 20:28 Comment(2)
So normally the Dockerfile is committed to version control which would also expose the credentials there, so I don't think this particular way is a good idea. The environment variables should be handled separately (e.g. cmd line or .env file)Orlop
The environment variables are insecure per se and so is passing them either via command line or a dotenv file; however, you are right that at least a disclaimer must be put in the answer to warn against a blind copypasting.Veiled
A
15

I use this command when using compose

docker-compose run <web> python manage.py createsuperuser

where <web> is the name of the docker service(in docker-compose.yml) https://docs.docker.com/compose/reference/run/

When run with Dockerfile

docker exec -it <container_id> python manage.py createsuperuser
Avesta answered 4/2, 2020 at 10:25 Comment(1)
docker exec -it <container_id/container_name> python manage.py createsuperuserAnnulment
O
14

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.

Orlop answered 12/11, 2018 at 12:11 Comment(2)
Where should we place this piece of code in the project?Weaponless
along the rest of your migrations, most likely the "migrations" folderDramatization
L
7

I used decople lib to load the environment variables from the .env file and I did the test if the username exists.

from decouple import config
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand

class Command(BaseCommand):

    def handle(self, *args, **options):
        username = config('DJANGO_SUPERUSER_USERNAME')
        email = config('DJANGO_SUPERUSER_EMAIL')
        password = config('DJANGO_SUPERUSER_PASSWORD')

        if not User.objects.filter(username=username).exists():
            print('Creating account for %s (%s)' % (username, email))
            admin = User.objects.create_superuser(
                email=email, username=username, password=password)
        else:
            print('Admin account has already been initialized.')

So I do it like this:

  source .env
  python manage.py initadmin

My .env file has:

DJANGO_SUPERUSER_USERNAME=admin
[email protected]
DJANGO_SUPERUSER_PASSWORD=mypass
Liquor answered 14/8, 2021 at 2:28 Comment(0)
F
3

None of the answers had worked in my project. This worked:

docker exec web ./manage.py shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('your_user', 'your_password')"
Forgave answered 28/1, 2019 at 13:16 Comment(1)
The user object now takes username, email, and then password, so, User.objects.create_superuser('your_user', 'your_email', 'your_password')Arlo
P
2

Might be easiest to just put together a Python script to create the Django superuser for you, instead of trying to feed all those commands through manage.py shell. Can you put your commands in a .py file, let's say yourfile.py:

#!/usr/bin/env python

from django.contrib.auth.models import User
User.objects.create_superuser('admin', '[email protected]', 'pass')

And then, after doing chmod +x yourfile.py:

fabric run:
run("docker exec container_django yourfile.py")

Depending on your setup you may need to make sure that the DJANGO_SETTINGS_MODULE environment variable is set appropriately for that run() command.

Parabolic answered 4/5, 2015 at 12:53 Comment(1)
I choose this solution with adding Hashicorp Vault into the mix just to make the secrets remain a secret to attackers. Instead of passing the password (and maybe the username too) plainly, you'd query Vault for a secret key.Fornix
S
2

I took @hoefling's answer, and changed it a little.

I needed to create super user AFTER the build step. So i put it inside a supervisor script. That means it will be executed every time i run the container. So i added a simple if / else control to check if superuser is already created. That reduces the execution time. And we need to set DJANGO_SETTINGS_MODULE environment variable as well.

python -c "import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'project_name.settings'
import django
django.setup()
from django.contrib.auth.management.commands.createsuperuser import get_user_model
if get_user_model().objects.filter(username='$DJANGO_SUPERUSER_USERNAME'): 
    print 'Super user already exists. SKIPPING...'
else:
    print 'Creating super user...'
    get_user_model()._default_manager.db_manager('$DJANGO_DB_NAME').create_superuser(username='$DJANGO_SUPERUSER_USERNAME', email='$DJANGO_SUPERUSER_EMAIL', password='$DJANGO_SUPERUSER_PASSWORD')
    print 'Super user created...'"
Skitter answered 28/11, 2017 at 11:10 Comment(0)
V
1

create file Makefile

.ONESHELL:
reset-superuser: SHELL := python
reset-superuser:
    import os
    import django
    from django.contrib.auth import get_user_model

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.develop")
    django.setup()

    User = get_user_model()
    user = User.objects.filter(is_superuser=True).first()

    if user:
        user.set_password("admin")
        user.save()
    else:
        user = User.objects.create_superuser(
            username="josuedjh",
            first_name="Josue djh",
            password="admin",
        )

    print(f"Superuser {user.username!r} with password `admin`")

run comando:

make reset-superuser
Villainy answered 23/11, 2020 at 13:44 Comment(0)
G
0

The clean and elegant way is not to put any additional custom code or commands into the codebase if you are running the Django in the container. Instead use a bash script to run any configuration required (e.g. creating superuser). This way you can better mange and automate your configurations and keeping the codebase clean and reusable accross environments. Put in your docker-compose.yml

command: >
  bash -c '.. 
  && source create_superuser_prod.sh 
  && ..'

Put in the create_superuser_prod.sh

echo "from django.contrib.auth.models import User; User.objects.create_superuser($PROD_USER,$PROD_USER_EMAIL,$PROD_USER_PASS)" | python manage.py shell 
Guv answered 28/8, 2021 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.