您现在的位置是:首页 > 文章详情

记录:尝试 HashiCorp Vault + AWS IAM + AWS Lambda

日期:2022-01-30点击:284

Vault 是 HashiCorp 开源的密钥库程序。

https://www.vaultproject.io/downloads 下载得到的 CLI 程序包括既包括客户端也包括服务端实现。

本文以 Windows + PowerShell + Nodejs 为环境描述用法,其它环境基本相同。

Vault 基本用法

运行开发服务器:

vault server -dev 

开发服务器会直接使用 HTTP 而不启用 HTTPS,在 http://127.0.0.1:8200
开发服务器会默认启用 Vault UI,在 http://127.0.0.1:8200/ui

根据运行后的提示,客户端的用法是:

$env:VAULT_ADDR="http://127.0.0.1:8200" vault status vault auth list 

静态的密钥读写:

vault kv put secret/hello foo=1 bar=a vault kv get secret/hello 

这里的 secret 是一个默认的存储库(Secret Engine),hello 是具体的密钥,而 foo bar 是密钥中的多组键值对。

vault secrets list vault kv list secret/ 

密钥可以删除,删除后能读出来被删除的元信息。

vault kv delete secret/hello vault kv get secret/hello 

虽然 CLI 提供了 vault kv 命令,但其实 API 只有 read 接口,CLI 也提供它。这都有助于本地实验。

vault read secret/data/hello 

Vault SDK 用法

Vault 官方不提供 Node.js SDK。有一个相当完整的第三方库 node-vault

基本用法:

const NodeVault = require('node-vault'); const vault = NodeVault({ apiVersion: 'v1', endpoint: 'http://127.0.0.1:8200', token: 'xxx', }); if (require.main === module) { (async () => { const res = await vault.read('secret/data/hello'); console.log(res); })(); } 

场景与集成过程

如果在 AWS Lambda 中想使用某个密钥,第一个想法是从环境变量传进来。但这样会产生一个风险,因为可能很多 AWS 子账号都能看到这个信息。

同理,按上述的 SDK 基本用法传 VAULT_TOKEN,问题是一样的。

Vault 利用了 AWS STS 提供的 GetCallerIdentity 接口,设计了一个身份验证方式,并在验证成功后生成一个临时 token 用于访问。这称为 IAM 鉴权方式,是为了区别于另一种基于 EC2 的鉴权方式。

首先,在 AWS IAM 中创建一个新的 lambda role,并附加以下策略(Policies):

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "iam:GetUser", "iam:GetRole", "sts:GetCallerIdentity" ], "Effect": "Allow", "Resource": "*" } ] } 

你会得到一个 role arn。

vault auth enable aws vault write auth/aws/role/dev-iam ` auth_type=iam ` bound_iam_principal_arn=$ARN_ROLE ` token_policies=default ` token_ttl=600s 

这里的 dev-iam 是 Vault Role,default 是 Vault Policy,token_ttl 是临时 token 的有效期。

接下来是限定 AWS 凭据范围,减轻重放攻击的一个配置:

vault write auth/aws/config/client iam_server_id_header_value=dev 

然后,我们需要生成一个有效的 AWS STS GetCallerIdentity 请求,但不由 AWS SDK 发起,而是传给 Vault 使用,借此换取临时的 Vault token。

事实上 AWS SDK 不直接提供这个能力,虽然一切细节都是公开的。

const sts = new AWS.STS(); function getAwsRequest(iamServerId) { const request = sts.getCallerIdentity(); request.httpRequest.headers['X-Vault-AWS-IAM-Server-ID'] = iamServerId; // configured in vault request.emit('build', [request]); request.emit('afterBuild', [request]); request.emit('sign', [request]); // console.log(request.httpRequest); return request.httpRequest; } function toGolangStyleHeaders(headers) { const ret = {}; for (const [k,v] of Object.entries(headers)) { ret[k] = [v]; } return ret; } function toVaultRequest(request) { // console.log(request); const encoded_url = Buffer.from(request.endpoint.href, 'utf-8').toString('base64'); const encoded_body = Buffer.from(request.body, 'utf-8').toString('base64'); const golang_headers = toGolangStyleHeaders(request.headers); const encoded_headers = Buffer.from(JSON.stringify(golang_headers), 'utf-8').toString('base64'); return { 'role': 'dev-iam', // Vault Role 'iam_http_request_method': request.method, 'iam_request_url': encoded_url, 'iam_request_body': encoded_body, 'iam_request_headers': encoded_headers, } } async function awsLoginVault() { const iamServerId = 'dev'; const awsRequest = getAwsRequest(iamServerId); const vaultRequest = toVaultRequest(awsRequest); const response = await vault.awsIamLogin(vaultRequest); // console.log(response); vault.token = response.auth.client_token; // const status = await vault.status(); // console.log(status); } 

事实上这里得到的 vaultRequest 也能使用 CLI 试用:

vault write auth/aws/login \ role=dev-role-iam \ iam_http_request_method=POST \ iam_request_url=$encoded_url \ iam_request_body=$encoded_body \ iam_request_headers=$encoded_headers 

测试之:

async function getVaultSecret() { const response = await vault.read('secret/data/hello'); // console.log(response); return response.data.data; } 

失败了,很正常,还差一步。

上文选择了使用 default policy,但默认地,在这个 policy 中并未配置对 secret/* 的访问权。

vault policy list vault policy read default > default-policy.hcl // 根据下文修改文件 vault policy write default default-policy.hcl 

default-policy.hcl 中追加一段:

path "secret/*" { capabilities = ["read"] } 

再试一次,大功告成。

原文链接:https://my.oschina.net/tridays/blog/5423045
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章