💬

Djangoで鍵を隠すためのプチテク

2022/10/02に公開

Django で開発中に鍵を隠しておきたいけど面倒だよね?

通常、Django などの CMS をいじるときに秘密鍵が Github などにアップされないように管理されなければなりません。こういう状況では、非同期されているファイルによる管理がよく利用されています。

私も今まではsecrets.jsonなどを用いて管理していましたが、PULL するときに毎回コピーするのも面倒だと感じていました。

開発中は自動生成できるようにするのが楽なんじゃない?ということで、ちょっとお試しで自動鍵生成、管理システムを Django に組み込んでみました。

まずは gitignore の設定

せっかく秘密鍵を別ファイルに作ってもそれをアップロードしたら意味ないので、まずは.gitignore でファイルのアップロードを制限します。

.gitignore
.secrets.json

settings.py の設定

Django では settings.py の設定で様々な初期設定が実行されます。この中に秘密鍵の生成プロセスを組み込むことで管理を容易にします。

モジュールのインポート

ファイルをいくつか操作するので、os, json, secrets(これは後述)をインポートします。

import os
import json
from . import secrets

鍵生成の要否を定義

今回の鍵生成は、単独で実行の要否を決められます。また、テスト環境から本番環境へ切り替えがされたときにも不用意に鍵が書き換えられないようにします。

SECRETS_GEN = True # 不要なときはFalse

鍵要否の判定

鍵が必要かどうかを判定します。DEBUG と SECRETS_GEN が True のときだけ鍵が生成されます。詳細は後述のsecretsライブラリを参照してください。

if DEBUG and SECRETS_GEN:
    SG = secrets.SecretsGen()
    SG.gen_key()

鍵の読み込み

自動生成された鍵を読み込むために json ファイルを読み込みます。pathlib のモジュールでsettings.pyの親フォルダを定義します。

生成された.secrets.jsonのパスを読み込み、辞書型としてSECRET_KEYに渡します。

SECRETS_PATH = Path(__file__).resolve().parent
open_json = open(os.path.join(SECRETS_PATH, ".secrets.json"), "r")
json_load = json.load(open_json)
SECRET_KEY = json_load["secretkey"]

settigs.py への追加コード全体像

全体概要はこちらです。これをDEBUGの下に入れておけば動きます。

settings.py
# 追加
import os
import json
from . import secrets

SECRETS_GEN = True

if DEBUG and SECRETS_GEN:  # DEBUGは初期状態ですでに定義されています。
    SG = secrets.SecretsGen()
    SG.gen_key()
SECRETS_PATH = Path(__file__).resolve().parent
open_json = open(os.path.join(SECRETS_PATH, ".secrets.json"), "r")
json_load = json.load(open_json)
SECRET_KEY = json_load["secretkey"]

secrets モジュールについて

鍵の自動生成のために、settings.py と同じ階層のフォルダにsecrets.pyを新規で作成します。

その中で、鍵の生成に必要なコードを記述します。

モジュールのインポート

今回のライブラリで特に必要なのはfrom django.core.management.utils import get_random_secret_keyになります。

これによって鍵が生成されます。また、今回は json ファイルを用いて鍵を管理するので、json も呼び出します。

from django.core.management.utils import get_random_secret_key
import json
import os
from pathlib import Path

秘密鍵の生成

秘密鍵の生成はライブラリによって実行されます。といっても、下記の 1 行で完結する素晴らしいものです。

secrets = get_random_secret_key()

鍵の保管場所を作成

秘密鍵は.secrets.json に保存されます。settings.pyと同じくファイルパス操作をうまく使いながら構築します。

file_path で親のフォルダパスを取得し、open_json で書き込み先の json ファイルの作成、上書きを実行します。

生成された秘密鍵は辞書型で格納されて json ファイルとして上書き保存されます。

file_path = Path(__file__).resolve().parent
open_json = open(os.path.join(file_path,".secrets.json"), "w")
json_writer = {"secretkey":secrets}
json.dump(json_writer, open_json, indent=2)
open_json.close()

class として取り扱う

上記の全体を class として取り扱い、ライブラリとして読み込めるようにします。

全体像はこちらです。

secret.py
from django.core.management.utils import get_random_secret_key
import json
import os
from pathlib import Path

class SecretsGen:
  def __init__(self) -> None:
    pass

  def gen_key(self):
    file_path = Path(__file__).resolve().parent
    secrets = get_random_secret_key()
    open_json = open(os.path.join(file_path,".secrets.json"), "w")
    json_writer = {"secretkey":secrets}
    json.dump(json_writer, open_json, indent=2)
    open_json.close()

  def __del__(self):
    pass


if __name__=="__main__":
  pass

使い勝手は?

ファイルパスは自動できるようにしたので、どんなプロジェクトでも settings.py と secrets.py に上記を記述すれば解決できるという点では楽だと感じています。

秘密鍵の取り扱いは面倒だと思いますが、Django プロジェクトを配布するようにするという考え方をする場合避けられない問題があります。

これを解決策のひとつとして考えてもらえれば幸いです。

Discussion