在GAE中使用app.yaml安全存储环境变量

124 投票
15 回答
60305 浏览
提问于 2025-04-18 00:01

我需要在 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 个回答

17

最好的做法是把密钥存放在一个叫做 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()
21

我的做法是将客户端的秘密信息存储在App Engine应用程序内部。这些秘密信息既不在源代码管理中,也不存储在任何本地电脑上。这样做的好处是,任何参与App Engine的合作者都可以部署代码更改,而不必担心这些秘密信息。

我直接将客户端的秘密信息存储在Datastore中,并使用Memcache来提高访问这些秘密信息的速度。Datastore中的实体只需要创建一次,以后每次部署时都会保留。当然,App Engine控制台可以随时用来更新这些实体。

创建一次性实体有两种选择:

  • 使用App Engine的远程API交互式命令行来创建实体。
  • 创建一个仅限管理员使用的处理程序,用虚拟值初始化这些实体。手动调用这个管理员处理程序,然后使用App Engine控制台将实体更新为实际的客户端秘密信息。
44

你发帖的时候这个服务还不存在,不过对于其他可能看到这里的人,谷歌现在提供了一个叫做Secret Manager的服务。

这个服务其实就是一个简单的REST服务(当然也有SDK可以用),可以把你的秘密安全地存储在谷歌云平台上。相比于数据存储(Data Store),这个方法更好,因为查看存储的秘密需要的步骤更少,而且权限管理更细致——如果你需要的话,可以为项目的不同部分设置不同的秘密保护。

它还支持版本管理,这样你在更改密码时就会简单很多。此外,它还有强大的查询和管理功能,让你在运行时可以发现和创建秘密。

Python SDK

使用示例:

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
98

这个解决方案很简单,但可能不适合所有团队。

首先,把环境变量放在一个叫env_variables.yaml的文件里,比如:

env_variables:
  SECRET: 'my_secret'

接着,把这个env_variables.yaml文件包含到app.yaml里。

includes:
  - env_variables.yaml

最后,把env_variables.yaml添加到.gitignore文件中,这样秘密变量就不会出现在代码库里。

在这种情况下,env_variables.yaml需要在部署管理人员之间共享。

64

如果你要处理敏感数据,就不要把它存储在源代码里,因为这些代码会被上传到版本控制系统。这样的话,可能会有不该看到这些数据的人(无论是公司内部还是外部)找到它。而且,你的开发环境和生产环境的配置值可能是不同的。如果这些值都写在代码里,你就得在开发和生产环境中运行不同的代码,这样会很麻烦,也是不好的做法。

在我的项目中,我会把配置数据放在数据存储中,使用这个类:

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控制台中找到数据存储的值并设置它们。方法如下:

  1. 访问 https://console.cloud.google.com/datastore/

  2. 如果你的项目没有被选中,请在页面顶部选择你的项目。

  3. Kind 下拉框中,选择 Settings

  4. 如果你运行了上面的代码,你的键会显示出来。它们的值都会是 NOT SET。点击每一个,设置它的值。

希望这能帮到你!

你的设置,由Settings类创建

点击进行编辑

输入真实值并保存

撰写回答