1

Basically I use serverless framework serverless for a function that allows me to send/receive emails through mailgun.

For this I have a config.js file set up in my serverless folder. This config.js contains all my API keys, email address, login, etc for my “mailgun” function.

I want to use Google Cloud KMS to encrypt the resource config.js, because I am afraid my senstive data gets stolen and misused. The encrypted file is config.js.enc. google_key_management_service

But serverless deploy does not decrypt my config.js.enc. It throws me a resource/syntax error…</p>

Any solutions/ideas how I can make KMS work for my config.js file in my serverless framework?

I added AWS tags also, because they have a similar KMS as Google Cloud. But actually I think the real issue is with the serverless framework and to make encrypted files work in deploying serverless with sls deploy command, but I could be mistaken.

4

1 回答 1

3

无服务器框架似乎包含原生AWS SSM 集成

functions:
  myfunc:
    # other config
    environment:
      TWITTER_ACCESS_TOKEN: ${ssm:myFunc}

但是,正如您所指出的,GCP 上没有类似的功能,因此您需要自己推出其中的一些功能。您可能对无服务器中的秘密中概述的一些策略感兴趣:

你需要这些秘密吗?

问总是很重要——我真的需要这些秘密吗?您能否利用云提供商 IAM(甚至跨云 OIDC)而不是将秘密注入我的应用程序?在可能的情况下,尝试利用各种云提供的 IAM 解决方案。显然,仍然有相当多的情况需要秘密。

加密环境变量

在启动该功能之前,您将本地明文机密加密为密文(加密字符串)。这是 gcloud 的示例,但您也可以使用 API 或其他工具,例如HashiCorp Vault

$ gcloud kms encrypt \
    --ciphertext-file=- \
    --plaintext-file=/path/to/my/secret \
    --key=my-kms-key \
    --key-ring=my-kms-keyring \
    --location=us-east4 \ 
    | base64

这将输出一个 base64 编码的加密字符串,然后您将其存储在您的config.js:

CiQAePa3VBJLbunLSqIJT+RS4nYiKdIaW6U69Y...

启动时,将您的应用程序配置为:

  1. Base64解码字符串
  2. 使用 Cloud KMS 解密密文
  3. 只要需要秘密,就将明文存储在内存中

我不确定您使用的是什么语言,但这里有一个 nodejs 示例。您可以在 GitHub 上的sethvargo/secrets-in-serverless找到更多示例:

const cryptoKeyID = process.env.KMS_CRYPTO_KEY_ID;

const kms = require('@google-cloud/kms');
const client = new kms.v1.KeyManagementServiceClient();

let username;
client.decrypt({
  name: cryptoKeyID,
  ciphertext: process.env.DB_USER,
}).then(res => {
  username = res[0].plaintext.toString().trim();
}).catch(err => {
  console.error(err);
});

let password;
client.decrypt({
  name: cryptoKeyID,
  ciphertext: process.env.DB_PASS,
}).then(res => {
  password = res[0].plaintext.toString().trim();
}).catch(err => {
  console.error(err);
});

exports.F = (req, res) => {
  res.send(`${username}:${password}`)
}

谷歌云存储

由于您使用 GCP,另一种选择是直接使用 Google Cloud Storage (GCS) 来存储机密。这将从无服务器框架中移除您的耦合。

  1. 做一个桶:

    $ gsutil mb gs://${GOOGLE_CLOUD_PROJECT}-serverless-secrets
    
  2. 将存储桶设为私有:

    $ gsutil defacl set private gs://${GOOGLE_CLOUD_PROJECT}-serverless-secrets
    $ gsutil acl set -r private gs://${GOOGLE_CLOUD_PROJECT}-serverless-secrets
    
  3. 将一些秘密写入存储桶。尽管它们以明文形式提交,但它们在静态时被加密,并且通过 IAM 严格控制访问。

    $ gsutil -h 'Content-Type: application/json' cp - gs://${GOOGLE_CLOUD_PROJECT}-serverless-secrets/app1 <<< '{"username":"my-user", "password":"s3cr3t"}'
    

然后创建一个有权从存储桶中读取的服务帐户,并将该服务帐户分配给您的函数。

最后,在函数开始时从存储桶中读取(这次是 Python 示例):

import os
import json
from google.cloud import storage

blob = storage.Client() \
    .get_bucket(os.environ['STORAGE_BUCKET']) \
    .get_blob('app1') \
    .download_as_string()

parsed = json.loads(blob)

username = parsed['username']
password = parsed['password']

def F(request):
    return f'{username}:{password}'
于 2019-02-11T15:39:21.470 回答