Чтение django_settings из менеджера секретов Google Cloud Platform не работает

При выполнении команды python manage.py makemigrations локально на моем ноутбуке, я получаю следующую ошибку на моей консоли:

(mywebsite) C:\Users\Sander\PycharmProjects\mywebsite>python manage.py makemigrations
Invalid line: echo DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-west6:mywebsite-db/mydb > .env
Invalid line: echo GS_BUCKET_NAME=mybucket >> .env
Invalid line: echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n

Обратите внимание, что инструкции echo [... etc. ...] > .env на самом деле являются содержанием секрета, который я настроил в менеджере секретов Google Cloud Platform, следуя инструкциям Google Cloud Platform по развертыванию моего сайта Django на Google Cloud Run.

Теперь я знаю, что эти инструкции echo [... etc. ...] > .env должны создать файл .env с переменными DATABASE_URL, GS_BUCKET_NAME и SECRET_KEY в нем, но это, конечно, не так, поскольку он сообщает об ошибке "Invalid line: ..." вместо этого.

Я нашел этот ответ на StackOverflow , в котором говорится, что эти инструкции bash (echo [... etc. ...] > .env) просто не могут быть выполнены python и что я могу просто выполнить их локально, запустив их из сценария оболочки вместо этого, так что выполнение этого create-env-file.sh работает:

DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-we
st6:mywebsite-db/mydb > .env
GS_BUCKET_NAME=mybucket >> .env
echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n

Однако, при этом создается локальный .env файл и, очевидно, я хотел бы убедиться, что поток с Secret Manager работает вместо него, прежде чем переносить сайт на производство, поскольку если я сохраню .env файл и загружу его на производство, нет смысла продолжать использовать Secret Manager, он просто больше не нужен.

Это также становится очень ясно при просмотре кода, который фактически выполняется в бэке:

import io
import os
from pathlib import Path

import environ
import google.auth
from google.cloud import secretmanager

if os.path.isfile(env_file):
    # Use a local secret file, if provided

    env.read_env(env_file)
elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
    # Pull secrets from Secret Manager
    project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")

    client = secretmanager.SecretManagerServiceClient()
    settings_name = os.environ.get("SETTINGS_NAME", "django_settings")
    name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
    payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")

    env.read_env(io.StringIO(payload))
else:
    raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")

Я отладил этот код, и проблема заключается в выполнении env.read_env(io.StringIO(payload)), поскольку полезная нагрузка содержит ожидаемое содержимое (строки echo [... etc. ...] > .env).

Я вижу преимущество использования менеджера секретов Google, но я начинаю думать, что либо что-то не так с содержанием секрета, который предоставил учебник Django Cloud Run, либо с разбором кода и сохранением его в переменных среды.

Я работаю локально на Windows, со следующими библиотеками (содержимое моего requirements.txt):

django

# For integrating with Google Cloud Platform:
django-storages[google]>=1.12
django-environ>=0.8.1
psycopg2-binary>=2.9.1
gunicorn>=20.1.0
google-cloud-secret-manager>=2.7.2

Это, очевидно, вызвано двумя причинами:

  • В settings.py секретное содержимое загружается в переменные окружения с помощью env.read_env(io.StringIO(payload)), как указано в вопросе. Эта функция read_env(), очевидно, делает следующее:

    # ...
    for line in content.splitlines():
        m1 = re.match(r'\A(?:export )?([A-Za-z_0-9]+)=(.*)\Z', line)
        if m1:
            # ...
        elif not line or line.startswith('#'):
            # ignore warnings for empty line-breaks or comments
            pass
        else:
            logger.warning('Invalid line: %s', line)
    # ...
    

    Поскольку строки секретного содержимого начинаются с "echo ", строки помечаются как недопустимые (вы можете проверить это на https://regex101.com) и команда python manage.py makemigrations поэтому завершается с этой ошибкой.

  • При редактировании payload перед входом в env.read_env(io.StringIO(payload)) так, чтобы он больше не начинался с echo (для решения проблемы, описанной в пуле выше) и, также из-за регексового соответствия, также удаляя > .env или >> .env в конце, 3-я строка также вызывает проблемы: SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n. Чтение /dev/urandom для генерации псевдослучайного SECRET_KEY является проблемой в Windows, /dev/urandom существует только в операционных системах на базе Linux.

Похоже, что секрет должен работать на производственных Linux-серверах Google Cloud Run, но не на локальном компьютере под управлением Windows, если его выполнить, как в инструкции по развертыванию сайта Django.

Вместо этого, для локальных запусков, do создайте .env файл, либо действительно запустив скрипт create-env-file.sh, упомянутый в вопросе (*), либо создав .env файл вручную в той же папке, что и settings.py:

DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-west6:mywebsite-db/mydb
GS_BUCKET_NAME=mybucket
SECRET_KEY=some1279Long30String5You7Create2Yourself8136

(*) /dev/urandom сработал для меня, потому что когда я выполняю create-env_file.sh на терминальном окне в PyCharm, на моем компьютере открывается новый терминал Git Bash, возможно потому, что на моем компьютере это зарегистрированная программа для выполнения файлов оболочки.

Вернуться на верх