在Docker容器中创建Django超级用户,无需输入密码

51

我正在使用fabric在Django的Docker容器中尝试创建超级用户。

为了在Django中创建超级用户,我需要在Django交互模式下运行以下命令:

./manage.py createsuperuser

因为我想在织物脚本中运行它,所以我找到 这个 命令可以避免输入密码。

echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'pass')" | ./manage.py shell

然后我结合使用 "docker exec" 将其在我的 Django 容器中运行。

docker exec container_django echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'pass')" | ./manage.py shell

问题出在Linux管道符号 "|" 上,它将其左侧的所有内容(包括docker exec)传输到其右侧的"./manage.py shell"中。

而这不仅是困难的部分,还需考虑将所有这些杂项放入fabric run中,这意味着它们需要在两端使用引号,这将使整个过程非常丑陋。

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

我仍在努力想方设法让垃圾至少能在面料运行时工作,但我不知道该怎么做。


尝试更简单的方法:https://dev59.com/fG0NtIcB2Jgan1znGjV2#74273514 - abdulalim
12个回答

67

获取容器ID并运行命令。

docker exec -it container_id python manage.py createsuperuser

2
这并没有解决问题。 - Jorge
1
如何将用户名、电子邮件和密码作为参数附加? - Andrew

38

我建议添加一个新的管理命令,如果没有用户存在,它将自动创建一个超级用户。

请参见我在https://github.com/dkarchmer/aws-eb-docker-django创建的小例子。特别是,请看我如何运行python manage.py initadmin

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')

(请参阅Authentication/management/commands)。

您可以查看Dockerfile的操作步骤,然后运行CMD以运行runserver.sh,该脚本基本上是运行的命令。

python manage.py migrate --noinput
python manage.py initadmin
python manage.py runserver 0.0.0.0:8080
很明显,这假设管理员在服务器恢复后立即更改密码。这可能对您来说足够好或不够好。

请注意,您还可以使用 docker run --rm python manage.py initadmin 手动运行 initadmin,这在 fabfile 中非常容易实现。 - dkarchmer
为了让它稍微更安全,也许你可以通过环境变量来输入默认的管理员密码,就像在settings.py中处理数据库凭证一样。 - Shawn
更加安全?这只是在第一次发布时才会出现的问题。即使有人在你之前知道并登录并更改了密码,你只需将网站关闭并再次操作即可。想象一下,如果有人知道你要发布第一个网站并在登录上打败你的可能性。这就是“不是问题”的定义。 - dkarchmer
1
我认为以下命令是不必要的:admin.is_active = Trueadmin.is_admin = Trueadmin.save()。您正在使用create_superuser() - Taciano Morais Silva

30

使用环境变量和非交互模式。因此,您可以在您的env文件中添加类似于下面的内容。

DJANGO_SUPERUSER_PASSWORD=**********
DJANGO_SUPERUSER_EMAIL=example@example.com
DJANGO_SUPERUSER_USERNAME=admin

然后,在您的Docker入口文件中添加以下命令:

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

$@

请注意,在非交互模式下,Django的createsuperuser脚本默认从DJANGO_SUPERUSER_PASSWORD中获取密码,因此无需输入密码。

当使用环境变量启动容器时,这将运行迁移并根据需要创建管理员用户。


我的Django createsuperuser脚本没有从DJANGO_SUPERUSER_PASSWORD中获取密码。为什么?我该如何测试? - Taciano Morais Silva
请记得使用export命令或在docker环境中将其导出到环境变量DJANGO_SUPERUSER_PASSWORD。我使用source .env加载了它,并将值设置为变量,但是docker-compose.yml找不到它。至于USERNAME和EMAIL,source命令可以正常工作。 - Taciano Morais Silva
@TacianoMoraisSilva 这只适用于 Django >= 3.0。 - LennyLip
2
--email $DJANGO_SUPERUSER_USERNAME 中似乎应该写成 --email $DJANGO_SUPERUSER_EMAIL - alan.elkin

16

免责声明:

在Dockerfile中以明文形式存储密码是不安全的,因为密码可以随时从镜像中提取,而且Dockerfile通常提交到版本控制中。然而,本答案不涉及密码安全性,而是关于自动化createsuperuser命令的方法;如果您正在寻找正确的存储超级用户密码的方法,请参阅此SO问题:Docker和密码安全


我通过在Dockerfile中评估Python代码行来处理这个问题。

ENV DJANGO_DB_NAME=default
ENV DJANGO_SU_NAME=admin
ENV DJANGO_SU_EMAIL=admin@my.company
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')"

请注意,这与调用不同。

User.objects.create_superuser('admin', 'admin@example.com', 'pass')

如果您使用自定义用户模型,那么django.contrib.auth.get_user_model将可以很好地工作,而使用User.objects.create仅会创建标准用户实体,并忽略任何自定义用户模型。

此外,Django的createsuperuser命令在后台也会调用相同的函数,因此这种方法应该是非常安全的。


2
通常情况下,Dockerfile会被提交到版本控制中,这也会在那里暴露凭据,因此我认为这种特定的方式不是一个好主意。环境变量应该单独处理(例如命令行或.env文件)。 - Hendrik F
环境变量本身是不安全的,无论是通过命令行还是dotenv文件传递它们。但是,您说得对,至少要在答案中放置免责声明,以警告不要盲目地复制粘贴。 - hoefling

15

我在使用compose时使用这个命令

docker-compose run <web> python manage.py createsuperuser
< p>其中< code><web>是docker服务的名称(在docker-compose.yml中)https://docs.docker.com/compose/reference/run/

当使用Dockerfile运行时

docker exec -it <container_id> python manage.py createsuperuser

docker exec -it <容器 ID/容器名称> python manage.py createsuperuser - abdulalim

14
我建议运行一次数据迁移,这样当您通过 docker-compose up 启动 Docker 服务(例如 app 和 db)时,您可以执行所有迁移。您的迁移应该像这样(假设您将凭据等存储在环境变量中): Data Migration docker-compose exec web python code/manage.py migrate
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),
    ]

这允许您使用内置容器来对数据库执行操作,无论是同一容器中的本地数据库还是单独的服务。并且仅在需要迁移时才进行,而不是每次重建容器都要执行。


2
我们应该把这段代码放在项目的哪个位置? - Pablo Ruiz Ruiz
在你的其余迁移中,很可能有一个名为"migrations"的文件夹。 - Antoine Claval

7

我使用 decouple 库从 .env 文件中加载环境变量,然后进行了用户名存在性检测。

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.')

我这样做:

  source .env
  python manage.py initadmin

我的.env文件中包含:

DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@admin.com
DJANGO_SUPERUSER_PASSWORD=mypass

3

在我的项目中,没有任何一个答案起作用。 这个方法有效:

最初的回答:

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')"

现在用户对象需要用户名、电子邮件和密码,因此可以使用以下代码创建超级用户:User.objects.create_superuser('your_user', 'your_email', 'your_password') - Pratik K.

2

最好的方法可能是编写一个Python脚本来为您创建Django超级用户,而不是尝试通过manage.py shell运行所有这些命令。您可以将命令放入一个名为yourfile.py的文件中:

#!/usr/bin/env python

from django.contrib.auth.models import User
User.objects.create_superuser('admin', 'admin@example.com', 'pass')

然后,在执行 chmod +x yourfile.py 命令之后:

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

根据您的设置,您可能需要确保DJANGO_SETTINGS_MODULE环境变量对于该run()命令被适当地设置。


我选择将Hashicorp Vault加入到解决方案中,只是为了让密码对攻击者保持秘密。与其明文传递密码(也许还有用户名),不如查询Vault以获取秘密密钥。 - Ikon

2
我采用了 @hoefling's answer 并做了一些修改。
我需要在构建步骤之后创建超级用户,因此我将其放在监督脚本中。这意味着它每次运行容器时都会被执行。因此,我添加了一个简单的 if / else 控制来检查是否已经创建了超级用户。这可以减少执行时间。同时我们还需要设置 DJANGO_SETTINGS_MODULE 环境变量。
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...'"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接