目前我们在 AWS 上把密钥,API Key 等信息是存储在 AWS Systems Manager 的 Parameter Store 中,它只提供了用 KMS Key 加密存储字符串的功能,最大字符串大小是 4096 个字符,它是免费的。
最近发现 AWS 上有一个新的服务 AWS Secrets Manager(2018 年 4 月发布的),听起来用它来存储密钥信息更高大上些。它同样提供了用 KMS Key 加密存储字符串的功能,字符串最大也是 4096 个字符。从 AWS Web 控制台上看可配置用 Key/Value 的形式,其实本质也是存储为一个 JSON 字符串。
Secrets Manager 与 Parameter Store 更多的功能是能与 RDS 集成 -- 选择数据库收集数据库的配置信息(主机名,端口,实例等), 还有就是可配置定期更新密钥,这对一个安全的系统定期改密码很重要。对于定期更新密钥的未作深入研究,AWS Secrets Manager 本身知道如何轮换 RDS 数据库的密钥,其他的需要一个 Lambda 来支持。
Secrets Manger 相比于 Parameter Store 除了可定期轮换密码,再就是审计功能,其他的话没有太大的差别。对了 Secrets Manager 要钱的,一个密钥一个月 $0.40, 每 10,000 次 API 调用收 $0.05。如果用 Parameter Store, 结合 CloudWatch 和 Lambda 也能实现定期轮换密码。
为了方便 Parameter Store 过度到 Secrets Manager,我们还能用 Parameter Store(SSM) 的 API 来获得 Secrets Manager 中配置的值。支持 GetParameter
和 GetParameters
操作,需要加上特定的 Key 前缀来指明获取的是 Secrets Manager 中的密钥,如 Key: /aws/reference/secretsmanager/<secret_ID_in_Secrets_Manager>
。
Secret 的创建
创建 Secret 时可以选择某个 RDS 实例,填入用户名,密码,选择一个用于加密的 KMS key,那么 Secrets Manager 生成一个包含类似以下信息的字符串密钥
1 2 3 4 5 6 7 8 |
{ "username": "yanbin", "password": "password", "engine": "postgres", "host": "my_db.cco3cswdko89.us-east-1.rds.amazonaws.com", "port": 5432, "dbInstanceIdentifier": "my_db" } |
后台可展示为 Secret key/value 形式,便于查看与编辑。
或者创建时可以手工以 Secret key/value 的形式输入或直接编辑 Plaintext 内容。注意 Plaintext 中编辑时并不要求一定要符合 JSON 格式,可为任意的字符串,只是会造成切换回 Secret key/value 标签后无法显示。这个不要紧,反正后面的 API 获取到的也是一个字符串,必要时自行解析字符串为 JSON 对象。
输入其他的基本信息,Secret name, 描述信息,Tag 等。
获取 Secret
在 AWS 控制台打开每个 Secret 下方要获取密钥的示例代码,包含 Java, Javascript, C#, Python3, Ruby 和 Go 语言的。下面以 Python 3 为例,只保留了关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import boto3 import json session = boto3.Session(...) def get_secret(): secret_name = "qa/postgres/my_db" client = session.client('secretsmanager') get_secret_value_response = client.get_secret_value(SecretId=secret_name) return get_secret_value_response['SecretString'] if __name__ == '__main__': for i in range(20): secret_json = json.loads(get_secret()) print(secret_json['username']) |
上面代码得到前面数据库配置的用户名,假设 Secret key 是 qa/postgres/my_db
。如果配置的只是一个 Plaintext,那么取到的 get_secrete()
就是我们想要的。
读取 Secret 的 IAM 权限
上面读取 Secret 的用户需要有两个相应的权限才能正确执行
- 对 Secret key
qa/postgres/my_db
的secretsmanager:GetSecretValue
权限 - 对所用来加密的 KMS key 的
kms:Decrypt
权限
少了第一个权限会得到如下类似错误:
botocore.exceptions.ClientError: An error occurred (AccessDeniedException) when calling the GetSecretValue operation: User: arn:aws:iam::1234567890:user/program_user is not authorized to perform: secretsmanager:GetSecretValue on resource: arn:aws:secretsmanager:us-east-1:1234567890:secret:qa/postgres/my_db-DaFxju
如果没有第二个权限会得到如下类似的错误:
botocore.exceptions.ClientError: An error occurred (AccessDeniedException) when calling the GetSecretValue operation: Access to KMS is not allowed
通过 Parameter Store API 来读取 Secret
1 2 3 4 5 6 7 8 |
def get_secret(): secret_name = "qa/postgres/my_db" client = session.client('ssm') get_parameter_response = client.get_parameter( Name=f'/aws/reference/secretsmanager/{secret_name}', WithDecryption=True ) return get_parameter_response['Parameter']['Value'] |
这时候的权限要求就会有些奇怪了。需要的也是同上面完全一样的两个 secretsmanager:GetSecretValue
和 kms:Decrypt
。
虽然是在用 ssm
的 getParameter()
方法,实际上是不需要 ssm:GetParameter
权限的,所以底层上对 ssm.get_parameter()
的调用实际上委派给了 secretsmanager.get_secret_value()
操作。如果真要给个 ssm:GetParameter
权限,资源上也无法指定一个 secretesmanager 的资源啊。
链接:
本文链接 https://yanbin.blog/aws-secrets-manager-store-secrets/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。