app.yaml을 사용하여 GAE에 환경 변수를 안전하게 저장
app.yamlGAE에 배포 하기 위해 API 키 및 기타 민감한 정보 를 환경 변수 로 저장해야합니다 . 문제는 내가 app.yamlGitHub에 푸시 하면이 정보가 공개된다는 것입니다 (좋지 않음). 프로젝트에 적합하지 않기 때문에 데이터 저장소에 정보를 저장하고 싶지 않습니다. 오히려 .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')
데이터 저장소에 해당 키에 대한 값이 있으면 가져옵니다. 없는 경우 자리 표시 자 레코드가 생성되고 예외가 발생합니다. 예외는 Developers Console로 이동하여 자리 표시 자 레코드를 업데이트하도록 알려줍니다.
구성 값 설정에서 추측이 필요하다는 것을 알았습니다. 설정할 구성 값이 확실하지 않은 경우 코드를 실행하면 알려줍니다!
위 코드는 내부적으로 Memcache와 데이터 저장소를 사용하는 ndb 라이브러리를 사용하므로 빠릅니다.
최신 정보:
jelder 는 App Engine 콘솔에서 Datastore 값을 찾고 설정하는 방법을 물었습니다. 방법은 다음과 같습니다.
아직 선택하지 않은 경우 페이지 상단에서 프로젝트를 선택합니다.
에서 종류 드롭 다운 상자, 선택 설정 .
위의 코드를 실행하면 키가 표시됩니다. 모두 NOT SET 값을 갖습니다 . 각각을 클릭하고 값을 설정하십시오.
도움이 되었기를 바랍니다!
이 솔루션은 간단하지만 모든 팀에 적합하지 않을 수 있습니다.
먼저 환경 변수를 env_variables.yaml에 넣습니다 . 예 :
env_variables:
SECRET: 'my_secret'
그런 다음이 포함 env_variables.yaml의를app.yaml
includes:
- env_variables.yaml
마지막으로, 추가 env_variables.yaml로 .gitignore비밀 변수가 리포지터리 (repository) 내에 존재하지 않습니다 그래서.
이 경우 env_variables.yaml배치 관리자간에 공유해야합니다.
내 접근 방식은 App Engine 앱 자체 내 에서만 클라이언트 비밀을 저장하는 것입니다. 클라이언트 암호는 소스 제어 나 로컬 컴퓨터에 없습니다. 이는 모든 App Engine 공동 작업자가 클라이언트 보안 비밀에 대해 걱정할 필요없이 코드 변경 사항을 배포 할 수 있다는 이점이 있습니다.
클라이언트 암호를 Datastore에 직접 저장하고 Memcache를 사용하여 암호 액세스 지연 시간을 개선합니다. Datastore 항목은 한 번만 생성하면되며 향후 배포시 유지됩니다. 물론 App Engine 콘솔을 사용하여 언제든지 이러한 항목을 업데이트 할 수 있습니다.
일회성 엔티티 생성을 수행하는 두 가지 옵션이 있습니다.
- App Engine Remote API 대화 형 셸을 사용하여 항목을 만듭니다.
- 더미 값으로 엔티티를 초기화하는 관리자 전용 핸들러를 만듭니다. 이 관리 핸들러를 수동으로 호출 한 다음 App Engine 콘솔을 사용하여 프로덕션 클라이언트 비밀번호로 항목을 업데이트합니다.
이를 수행하는 가장 좋은 방법은 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()
GAE에 앱을 배포 할 때 appcfg.py의 -E 명령 줄 옵션을 사용하여 환경 변수를 설정할 수 있습니다 (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).
...
몇 가지 접근 방식을 할 수있는 것 같습니다. 유사한 문제가 있으며 다음을 수행합니다 (사용 사례에 맞게 조정 됨).
- 동적 app.yaml 값을 저장하는 파일을 만들고 빌드 환경의 보안 서버에 배치합니다. 정말 편집증이라면 값을 비대칭 적으로 암호화 할 수 있습니다. 버전 제어 / 동적 가져 오기가 필요한 경우 개인 저장소에 보관하거나 셸 스크립트를 사용하여 적절한 위치에서 복사 / 가져올 수도 있습니다.
- 배포 스크립트 중에 git에서 가져 오기
- git pull 후 yaml 라이브러리를 사용하여 순수 Python으로 읽고 쓰는 방식으로 app.yaml을 수정합니다.
이를 수행하는 가장 쉬운 방법은 Hudson , Bamboo 또는 Jenkins 와 같은 지속적 통합 서버를 사용하는 것 입니다. 위에서 언급 한 모든 항목을 수행하는 플러그인, 스크립트 단계 또는 워크 플로를 추가하기 만하면됩니다. 예를 들어 Bamboo 자체에 구성된 환경 변수를 전달할 수 있습니다.
요약하면 액세스 권한 만있는 환경에서 빌드 프로세스 중에 값을 푸시하면됩니다. 빌드를 아직 자동화하지 않았다면 그렇게해야합니다.
또 다른 옵션 옵션은 당신이 말한 것입니다. 데이터베이스에 넣으십시오. 그렇게하지 않는 이유가 너무 느리기 때문이라면 값을 두 번째 레이어 캐시로 memcache에 푸시하고 값을 첫 번째 레이어 캐시로 인스턴스에 고정하면됩니다. 값이 변경 될 수 있고 인스턴스를 재부팅하지 않고 업데이트해야하는 경우 해시를 유지하여 변경시기를 확인하거나 수행 한 작업이 값을 변경할 때 어떻게 든 트리거 할 수 있습니다. 그게 다야.
대부분의 답변은 구식입니다. Google 클라우드 데이터 저장소를 사용하는 것은 실제로 현재 약간 다릅니다. 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'이고 종류가 'settings'이며 'value'가 TWITTER_APP_KEY 항목의 속성이라고 가정합니다.
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 파일)에 넣습니다.
암호 해독을 시작하는 데 도움이되는 비단뱀 코드입니다.
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
javascript / nodejs에서이 문제를 어떻게 해결했는지 기록하고 싶었습니다. 로컬 개발을 위해 .env 파일에서 process.env로 환경 변수를로드하는 'dotenv'npm 패키지를 사용했습니다. GAE를 사용하기 시작했을 때 환경 변수를 'app.yaml'파일에 설정해야한다는 것을 알게되었습니다. 글쎄, 나는 로컬 개발에 'dotenv'를 사용하고 GAE에 'app.yaml'을 사용하고 싶지 않았기 때문에 (그리고 두 파일 사이에 내 환경 변수를 복제), 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
Cloud Datastore에 appengine 환경 변수를 저장할 수있는 gae_env 라는 pypi 패키지 가 있습니다. 내부적으로는 Memcache도 사용하므로
용법:
import gae_env
API_KEY = gae_env.get('API_KEY')
데이터 저장소에 해당 키에 대한 값이 있으면 반환됩니다. 없는 경우 자리 표시 자 레코드 __NOT_SET__가 생성되고 a ValueNotSetError가 발생합니다. 예외는 Developers Console 로 이동 하여 자리 표시 자 레코드를 업데이트하도록 알려줍니다 .
Martin의 답변과 유사하게 Datastore에서 키 값을 업데이트하는 방법은 다음과 같습니다.
개발자 콘솔에서 데이터 저장소 섹션 으로 이동
아직 선택하지 않은 경우 페이지 상단에서 프로젝트를 선택합니다.
에서 종류 드롭 다운 상자를 선택합니다
GaeEnvSettings.예외가 발생한 키는 값을 갖습니다
__NOT_SET__.
사용 / 구성에 대한 자세한 정보 는 패키지의 GitHub 페이지 로 이동 하십시오.
Google Datastore 사용을 기반으로 한 @Jason F의 답변 은 비슷하지만 코드는 라이브러리 문서 의 샘플 사용을 기반으로 약간 구식 입니다. 나를 위해 일한 스 니펫은 다음과 같습니다.
from google.cloud import datastore
client = datastore.Client('<your project id>')
key = client.key('<kind e.g settings>', '<entity name>') # note: entity name not property
# get by key for this entity
result = client.get(key)
print(result) # prints all the properties ( a dict). index a specific value like result['MY_SECRET_KEY'])
이 Medium 게시물에서 부분적으로 영감을 얻었 습니다.
'Program Tip' 카테고리의 다른 글
| Dart는 열거를 지원합니까? (0) | 2020.10.21 |
|---|---|
| WPF MVVM 직선 XAML 창보기가 아닌 ContentControl + DataTemplate보기를 사용하는 이유는 무엇입니까? (0) | 2020.10.21 |
| PyCharm 내에서 (Ana) conda 사용 (0) | 2020.10.21 |
| RuntimeError : 모듈이 API 버전 A에 대해 컴파일되었지만이 버전의 numpy는 9입니다. (0) | 2020.10.21 |
| equals 메소드와 관련된 Java 코드 (0) | 2020.10.21 |


