はじめに
Djangoではプロジェクトの設定情報を settings.py
で管理します。
このファイルにはデータベースの接続情報などを設定しますが、これら機密情報の管理には以下の課題があります。
- セキュリティ上の問題となるため、リポジトリ上で公開すべきではない
- 実行環境ごとに変わる情報であるためバージョン管理外としたい
そこで本エントリでは、機密情報をバージョン管理外のファイルで管理する手順をまとめます。
なお、説明を簡単にするため django-admin startproject
が生成するプロジェクトディレクトリ直下の settings.py
を対象に話を進めますが、当該ファイルを別のパスで管理している場合には適宜説明を読み替えてください。
プロジェクトの作成
my_project
という名前のプロジェクトで説明します。
$ django-admin startproject my_project $ cd my_project
プロジェクトディレクトリ my_project
以下に作成される settings.py
の機密情報を管理する方法を考えていきます。
$ ls manage.py my_project $ ls my_project __init__.py settings.py urls.py wsgi.py
settings.pyの設定内容
ここでは settings.py
に以下のような設定があるものとします。
データベースは標準でSQLite3ですが、ここではPostgreSQLに変更しています。
SECRET_KEY = 'noeh%g##*pneyx@stz_1b*cl6liudiw(dvsa#9xh44h0x^@gjb' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'my_db', 'USER': 'my_user', 'PASSWORD': 'maeh7Aik', 'HOST': 'localhost', 'PORT': '', 'ATOMIC_REQUESTS': True, } }
ここで問題となるのは以下の4項目です。各項目の用途はリンク先を参照してください。
機密情報をJSONファイルで管理
以下の手順で機密情報の管理方法を改めます。
- 機密情報を管理するJSONファイル
secrets.json
の生成スクリプトを用意 settings.py
でsecrets.json
の設定値を参照するように変更- 実行環境ごとに
1.
のスクリプトでsecrets.json
を作成し、機密情報を入力
1. 機密情報を管理するJSONファイル secrets.json
の生成スクリプトを用意
my_project
以下にスクリプト gen_secrets.py
を作成します。
このスクリプトは機密情報を管理するJSONの雛形をプロジェクトのルートに作成します。
SECRET_KEY
については django.core.management.utils.get_random_secret_key
で自動生成していますが、データベース関連の項目は空文字を出力します。
プロジェクトで取り扱う機密情報に応じて secrets = {...}
の辞書定義は修正してください。
import json import os from django.core.management.utils import get_random_secret_key BASE_DIR = os.path.dirname( os.path.dirname( os.path.abspath(__file__) ) ) secrets = { 'SECRET_KEY': get_random_secret_key(), 'DB_NAME': '', 'DB_USER': '', 'DB_PASSWORD': '', } with open(os.path.join(BASE_DIR, 'secrets.json'), 'w') as outfile: json.dump(secrets, outfile)
secrets.json
はバージョン管理する必要のないファイルなので、忘れずに .gitignore
に指定しておきましょう。
secrets.json
2. settings.py
で secrets.json
の設定値を参照するように変更
以下の通り settings.py
を修正します。
SECRET_KEY
等の設定値が get_secret()
の戻り値となっていることを確認してください。
get_secret()
は引数の文字列をキーに secrets.json
から値を取得しています。
これで機密情報の管理を settings.py
から secrets.json
に追い出すことができました。
import json import os ... if os.path.exists(os.path.join(BASE_DIR, 'secrets.json')): with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file: secrets = json.load(secrets_file) else: print('secrets.json could not be found.') quit() def get_secret(setting, secrets=secrets, is_optional=False): # 設定が見つからない場合にNoneを返したい場合にはis_optionalにTrueを設定 try: secret = secrets[setting] if secret: return secret else: if is_optional: return None else: print(f'Please set {setting} in secrets.json') quit() except KeyError: print(f' Please set {setting} in secrets.json') quit() SECRET_KEY = get_secret('SECRET_KEY') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': get_secret('DB_NAME'), 'USER': get_secret('DB_USER'), 'PASSWORD': get_secret('DB_PASSWORD'), 'HOST': 'localhost', 'PORT': '', 'ATOMIC_REQUESTS': True, } }
3. 実行環境ごとに 1.
のスクリプトで secrets.json
を作成し、機密情報を入力
secrets.jsonの作成
以下のコマンドで secrets.json
(雛形)をプロジェクトのルートに作成します。
SECRET_KEY
の値は実行の都度変化します。
$ python my_project/gen_secrets.py $ cat secrets.json {"SECRET_KEY": "#_odl(6s%+h_1=l95ow%5d=*=f!uzvt%*++y_7ne1%5_f(#l2(", "DB_NAME": "", "DB_USER": "", "DB_PASSWORD": ""}
secrets.jsonの編集
元々 settings.py
で管理していた値で DB_NAME
、DB_USER
、 DB_PASSWORD
を更新しました。
$ cat secrets.json {"SECRET_KEY": "#_odl(6s%+h_1=l95ow%5d=*=f!uzvt%*++y_7ne1%5_f(#l2(", "DB_NAME": "my_db", "DB_USER": "my_user", "DB_PASSWORD": "maeh7Aik"}
動作確認
データベースの用意
secrets.json
の設定値でデータベース・ユーザを作成します。
$ initdb -D postgres_data $ pg_ctl -D postgres_data -l logfile start $ createuser -s -d -P my_user Enter password for new role: maeh7Aik Enter it again: maeh7Aik $ createdb my_db -O my_user
マイグレーション
マイグレーションを実行します。これが成功すれば正しく設定できています。
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK
サーバ起動
サーバも問題なく起動できました。
$ python manage.py runserver Performing system checks... System check identified no issues (0 silenced). July 14, 2020 - 14:32:06 Django version 2.1.7, using settings 'my_project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
さいごに
本エントリでは、Djangoアプリの機密情報を管理する方法をまとめました。
今回紹介した内容は一つの例に過ぎないので、ご自身のプロジェクトに適した方式を検討することをおすすめします。
django-environ等を採用するのも良いと思います。