GAEにデプロイするための環境変数として、app.yaml
にAPIキーとその他の機密情報を保存する必要があります。これに関する問題は、app.yaml
をGitHubにプッシュすると、この情報が公開される(良くない)ことです。プロジェクトに適さないため、データストアに情報を保存したくありません。むしろ、アプリの各デプロイメントで.gitignore
にリストされているファイルから値を交換したいと思います。
ここに私のapp.yamlファイルがあります:
application: myapp
version: 3
runtime: python27
api_version: 1
threadsafe: true
libraries:
- name: webapp2
version: latest
- name: jinja2
version: latest
handlers:
- url: /static
static_dir: static
- url: /.*
script: main.application
login: required
secure: always
# auth_fail_action: unauthorized
env_variables:
CLIENT_ID: ${CLIENT_ID}
CLIENT_SECRET: ${CLIENT_SECRET}
ORG: ${ORG}
ACCESS_TOKEN: ${ACCESS_TOKEN}
SESSION_SECRET: ${SESSION_SECRET}
何か案は?
機密データの場合は、ソース管理にチェックインされるため、ソースコードに保存しないでください。間違った人(組織の内外)がそこにいる可能性があります。また、開発環境では、おそらく運用環境とは異なる構成値を使用します。これらの値がコードに保存されている場合、開発と本番で異なるコードを実行する必要があり、面倒で悪い習慣です。
私のプロジェクトでは、このクラスを使用してデータストアに構成データを配置します。
from google.appengine.ext import ndb
class Settings(ndb.Model):
name = ndb.StringProperty()
value = ndb.StringProperty()
@staticmethod
def get(name):
NOT_SET_VALUE = "NOT SET"
retval = Settings.query(Settings.name == name).get()
if not retval:
retval = Settings()
retval.name = name
retval.value = NOT_SET_VALUE
retval.put()
if retval.value == NOT_SET_VALUE:
raise Exception(('Setting %s not found in the database. A placeholder ' +
'record has been created. Go to the Developers Console for your app ' +
'in App Engine, look up the Settings record with name=%s and enter ' +
'its value in that record\'s value field.') % (name, name))
return retval.value
アプリケーションはこれを実行して値を取得します。
API_KEY = Settings.get('API_KEY')
データストアにそのキーの値がある場合、それを取得します。存在しない場合、プレースホルダーレコードが作成され、例外がスローされます。例外は、デベロッパーコンソールに移動してプレースホルダーレコードを更新することを思い出させます。
これにより、設定値の設定から推測を取り除くことができます。設定する設定値がわからない場合は、コードを実行するだけで通知されます!
上記のコードは、memcacheと内部のデータストアを使用するndbライブラリを使用しているため、高速です。
更新:
jelderは、App Engineコンソールでデータストア値を見つけて設定する方法を尋ねました。方法は次のとおりです。
まだ選択されていない場合は、ページの上部でプロジェクトを選択します。
Kindドロップダウンボックスで、Settingsを選択します。
上記のコードを実行すると、キーが表示されます。それらはすべて値を持ちますNOT SET。それぞれをクリックして、その値を設定します。
お役に立てれば!
このソリューションは単純ですが、すべての異なるチームに適しているとは限りません。
まず、環境変数をenv_variables.yamlに入れます。たとえば、
env_variables:
SECRET: 'my_secret'
次に、これを含めますenv_variables.yaml
の中に app.yaml
includes:
- env_variables.yaml
最後に、env_variables.yaml
から.gitignore
。これにより、シークレット変数がリポジトリに存在しなくなります。
この場合、env_variables.yaml
は、デプロイメントマネージャー間で共有する必要があります。
私のアプローチは、クライアントシークレットonlyをApp Engineアプリ自体に保存することです。クライアントシークレットは、ソース管理にもローカルコンピューターにもありません。これには、anyApp Engineコラボレーターがクライアントの秘密を気にすることなくコードの変更を展開できるという利点があります。
クライアントシークレットをデータストアに直接保存し、Memcacheを使用してシークレットにアクセスするレイテンシーを改善しました。データストアエンティティを作成する必要があるのは一度だけで、今後の展開でも保持されます。もちろん、App Engineコンソールを使用して、これらのエンティティをいつでも更新できます。
ワンタイムエンティティ作成を実行するには、2つのオプションがあります。
最善の方法は、client_secrets.jsonファイルにキーを保存し、それを.gitignoreファイルにリストして、gitへのアップロードから除外することです。環境ごとに異なるキーがある場合は、app_identity apiを使用してアプリIDを判別し、適切にロードできます。
ここにかなり包括的な例があります-> https://developers.google.com/api-client-library/python/guide/aaa_client_secrets 。
コードの例を次に示します。
# declare your app ids as globals ...
APPID_LIVE = 'awesomeapp'
APPID_DEV = 'awesomeapp-dev'
APPID_PILOT = 'awesomeapp-pilot'
# create a dictionary mapping the app_ids to the filepaths ...
client_secrets_map = {APPID_LIVE:'client_secrets_live.json',
APPID_DEV:'client_secrets_dev.json',
APPID_PILOT:'client_secrets_pilot.json'}
# get the filename based on the current app_id ...
client_secrets_filename = client_secrets_map.get(
app_identity.get_application_id(),
APPID_DEV # fall back to dev
)
# use the filename to construct the flow ...
flow = flow_from_clientsecrets(filename=client_secrets_filename,
scope=scope,
redirect_uri=redirect_uri)
# or, you could load up the json file manually if you need more control ...
f = open(client_secrets_filename, 'r')
client_secrets = json.loads(f.read())
f.close()
Appcfg.pyの-Eコマンドラインオプションを使用して、アプリをGAEにデプロイするときに環境変数を設定できます(appcfg.py更新)
$ appcfg.py
...
-E NAME:VALUE, --env_variable=NAME:VALUE
Set an environment variable, potentially overriding an
env_variable value from app.yaml file (flag may be
repeated to set multiple variables).
...
いくつかのアプローチを実行できるようです。同様の問題があり、以下を実行します(ユースケースに合わせて):
これを行う最も簡単な方法は、 Hudson 、 Bamboo 、または Jenkins などの継続的統合サーバーを使用することです。上記のすべての項目を実行するプラグイン、スクリプトステップ、またはワークフローを追加するだけです。たとえば、Bamboo自体で構成された環境変数を渡すことができます。
要約すると、あなただけがアクセスできる環境でビルドプロセス中に値をプッシュインするだけです。ビルドをまだ自動化していない場合は、そうする必要があります。
別のオプションオプションは、あなたが言ったことであり、データベースに入れます。これを行わない理由が物事が遅すぎることである場合、値を2次キャッシュとしてmemcacheにプッシュし、1次キャッシュとしてインスタンスに値を固定します。値が変更される可能性があり、インスタンスを再起動せずに更新する必要がある場合は、ハッシュを保持して、いつ変更するかを確認するか、何かを行うと値を変更するときにトリガーすることができます。それであるはずです。
Google kmsで変数を暗号化し、ソースコードに埋め込む必要があります。 ( https://cloud.google.com/kms/ )
echo -n the-Twitter-app-key | gcloud kms encrypt \
> --project my-project \
> --location us-central1 \
> --keyring THEKEYRING \
> --key THECRYPTOKEY \
> --plaintext-file - \
> --ciphertext-file - \
> | base64
スクランブル(暗号化およびbase64エンコード)された値を(yamlファイル内の)環境変数に入れます。
復号化を開始するためのいくつかのPythonコード。
kms_client = kms_v1.KeyManagementServiceClient()
name = kms_client.crypto_key_path_path("project", "global", "THEKEYRING", "THECRYPTOKEY")
Twitter_app_key = kms_client.decrypt(name, base64.b64decode(os.environ.get("Twitter_APP_KEY"))).plaintext
gae_env と呼ばれるpypiパッケージがあり、Cloud Datastoreにappengine環境変数を保存できます。内部では、Memcacheも使用するため、高速です
使用法:
import gae_env
API_KEY = gae_env.get('API_KEY')
データストアにそのキーの値がある場合、それが返されます。存在しない場合、プレースホルダーレコード__NOT_SET__
が作成され、ValueNotSetError
がスローされます。例外により、 Developers Console に移動してプレースホルダーレコードを更新するように通知されます。
Martinの答えと同様に、Datastoreのキーの値を更新する方法は次のとおりです。
開発者コンソールの Datastore Section に移動します
まだ選択されていない場合は、ページの上部でプロジェクトを選択します。
Kindドロップダウンボックスで、GaeEnvSettings
を選択します。
例外が発生したキーの値は__NOT_SET__
。
パッケージのGitHubページ にアクセスして、使用法/構成の詳細を確認してください。
ほとんどの答えは時代遅れです。 Google Cloud Datastoreの使用は、実際には今とは少し異なります。 https://cloud.google.com/python/getting-started/using-cloud-datastore
以下に例を示します。
from google.cloud import datastore
client = datastore.Client()
datastore_entity = client.get(client.key('settings', 'Twitter_APP_KEY'))
connection_string_prod = datastore_entity.get('value')
これは、エンティティ名が「Twitter_APP_KEY」、種類が「設定」、「値」がTwitter_APP_KEYエンティティのプロパティであることを前提としています。
Javascript/nodejsでこの問題をどのように解決したかに注目したかっただけです。ローカル開発では、環境変数を.envファイルからprocess.envにロードする 'dotenv' npmパッケージを使用しました。 GAEを使い始めたとき、環境変数を「app.yaml」ファイルで設定する必要があることを学びました。まあ、ローカル開発に 'dotenv'を使用し、GAEに 'app.yaml'を使用(および2つのファイル間で環境変数を複製)したくないので、app.yaml環境変数をプロセスにロードする小さなスクリプトを作成しました.env、ローカル開発用。これが誰かを助けることを願っています:
yaml_env.js:
(function () {
const yaml = require('js-yaml');
const fs = require('fs');
const isObject = require('lodash.isobject')
var doc = yaml.safeLoad(
fs.readFileSync('app.yaml', 'utf8'),
{ json: true }
);
// The .env file will take precedence over the settings the app.yaml file
// which allows me to override stuff in app.yaml (the database connection string (DATABASE_URL), for example)
// This is optional of course. If you don't use dotenv then remove this line:
require('dotenv/config');
if(isObject(doc) && isObject(doc.env_variables)) {
Object.keys(doc.env_variables).forEach(function (key) {
// Dont set environment with the yaml file value if it's already set
process.env[key] = process.env[key] || doc.env_variables[key]
})
}
})()
コードにできるだけ早くこのファイルを含めると、完了です。
require('../yaml_env')
マーティンの答えを拡張する
from google.appengine.ext import ndb
class Settings(ndb.Model):
"""
Get sensitive data setting from DataStore.
key:String -> value:String
key:String -> Exception
Thanks to: Martin Omander @ Stackoverflow
https://stackoverflow.com/a/35261091/1463812
"""
name = ndb.StringProperty()
value = ndb.StringProperty()
@staticmethod
def get(name):
retval = Settings.query(Settings.name == name).get()
if not retval:
raise Exception(('Setting %s not found in the database. A placeholder ' +
'record has been created. Go to the Developers Console for your app ' +
'in App Engine, look up the Settings record with name=%s and enter ' +
'its value in that record\'s value field.') % (name, name))
return retval.value
@staticmethod
def set(name, value):
exists = Settings.query(Settings.name == name).get()
if not exists:
s = Settings(name=name, value=value)
s.put()
else:
exists.value = value
exists.put()
return True