在GAE中使用app.yaml安全存储环境变量
我需要在 app.yaml
文件中存储 API 密钥和其他敏感信息,作为在 GAE 上部署的环境变量。问题是,如果我把 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}
有什么好主意吗?
15 个回答
最好的做法是把密钥存放在一个叫做 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()
我的做法是将客户端的秘密信息仅存储在App Engine应用程序内部。这些秘密信息既不在源代码管理中,也不存储在任何本地电脑上。这样做的好处是,任何参与App Engine的合作者都可以部署代码更改,而不必担心这些秘密信息。
我直接将客户端的秘密信息存储在Datastore中,并使用Memcache来提高访问这些秘密信息的速度。Datastore中的实体只需要创建一次,以后每次部署时都会保留。当然,App Engine控制台可以随时用来更新这些实体。
创建一次性实体有两种选择:
- 使用App Engine的远程API交互式命令行来创建实体。
- 创建一个仅限管理员使用的处理程序,用虚拟值初始化这些实体。手动调用这个管理员处理程序,然后使用App Engine控制台将实体更新为实际的客户端秘密信息。
你发帖的时候这个服务还不存在,不过对于其他可能看到这里的人,谷歌现在提供了一个叫做Secret Manager的服务。
这个服务其实就是一个简单的REST服务(当然也有SDK可以用),可以把你的秘密安全地存储在谷歌云平台上。相比于数据存储(Data Store),这个方法更好,因为查看存储的秘密需要的步骤更少,而且权限管理更细致——如果你需要的话,可以为项目的不同部分设置不同的秘密保护。
它还支持版本管理,这样你在更改密码时就会简单很多。此外,它还有强大的查询和管理功能,让你在运行时可以发现和创建秘密。
使用示例:
from google.cloud import secretmanager_v1beta1 as secretmanager
secret_id = 'my_secret_key'
project_id = 'my_project'
version = 1 # use the management tools to determine version at runtime
client = secretmanager.SecretManagerServiceClient()
secret_path = client.secret_version_path(project_id, secret_id, version)
response = client.access_secret_version(secret_path)
password_string = response.payload.data.decode('UTF-8')
# use password_string -- set up database connection, call third party service, whatever
这个解决方案很简单,但可能不适合所有团队。
首先,把环境变量放在一个叫env_variables.yaml的文件里,比如:
env_variables:
SECRET: 'my_secret'
接着,把这个env_variables.yaml
文件包含到app.yaml
里。
includes:
- env_variables.yaml
最后,把env_variables.yaml
添加到.gitignore
文件中,这样秘密变量就不会出现在代码库里。
在这种情况下,env_variables.yaml
需要在部署管理人员之间共享。
如果你要处理敏感数据,就不要把它存储在源代码里,因为这些代码会被上传到版本控制系统。这样的话,可能会有不该看到这些数据的人(无论是公司内部还是外部)找到它。而且,你的开发环境和生产环境的配置值可能是不同的。如果这些值都写在代码里,你就得在开发和生产环境中运行不同的代码,这样会很麻烦,也是不好的做法。
在我的项目中,我会把配置数据放在数据存储中,使用这个类:
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')
如果数据存储中有这个键的值,你就能拿到它。如果没有,就会创建一个占位记录,并抛出一个异常。这个异常会提醒你去开发者控制台更新这个占位记录。
我发现这样可以避免在设置配置值时的猜测。如果你不确定该设置什么配置值,只需运行代码,它会告诉你!
上面的代码使用了ndb库,它在后台使用了memcache和数据存储,所以速度很快。
更新:
jelder问如何在App Engine控制台中找到数据存储的值并设置它们。方法如下:
如果你的项目没有被选中,请在页面顶部选择你的项目。
在 Kind 下拉框中,选择 Settings。
如果你运行了上面的代码,你的键会显示出来。它们的值都会是 NOT SET。点击每一个,设置它的值。
希望这能帮到你!