如何使用 IAM 身份验证从 NodeJS 中的 AWS Lambda 调用 Hashicorp Vault

问题描述 投票:0回答:1

有谁知道是否有一个好的 NodeJS 库可以使用 IAM 身份验证从 AWS Lambda 连接到 hashicorpVault。

类似于 Python 的 HVAC 的东西会很好。

我尝试过使用 node-vault-client 但没有任何好的 IAM 身份验证示例,并且自 2019 年以来它似乎没有更新,所以我不确定它是否正在积极维护。

javascript node.js amazon-web-services aws-lambda hashicorp-vault
1个回答
3
投票

我设法使用 node-vault-client 让它工作,但我必须对库进行更改,因为它不允许您传递名称空间标头。我提出了一个 PR,添加了一个新字段来解决该问题。

这是我的代码示例:

const VaultClient = require('node-vault-client');

const vaultClient = VaultClient.boot('main', {
  api: { url: 'https://my-vault-url.com' },
  auth: {
    type: 'iam',
    config: {
      role: 'my-role',
      iam_server_id_header_value: 'my-vault-url.com',
      namespace: 'my-namespace', // new option added in my pull request
      credentials: new AWS.Credentials({
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        sessionToken: process.env.AWS_SESSION_TOKEN,
      }),
    },
  },
});


 vaultClient
    .read('secrets/data/path/to/secret')
    .then((secrets: any) => {
      console.log(`MY SECRET IS ${secrets.__data.data['MY_SECRET_KEY']}`);
    })
    .catch((e: Error) => {
      console.error('Error connecting to vault .....');
      console.error(e);
    });

************ 更新 **************

此后,我决定编写代码来自己从保管库检索秘密,并删除对节点保管库客户端的依赖。

首先,我需要调用 AWS Secrets Manager 来获取 Vault CA 证书

// aws-secrets-manager.client.ts

import AWS from 'aws-sdk';
import HttpException from '../errors/http.exception';

export default class AWSSecretsManagerClient {
  private static readonly client: AWS.SecretsManager = new AWS.SecretsManager({
    region: process.env.AWS_REGION,
  });

  // eslint-disable-next-line @typescript-eslint/no-empty-function
  private constructor() {}

  static async getSecret(secretName: string): Promise<string> {
    console.log(`Reading secret from aws secrets manager`);

    try {
      const response = await AWSSecretsManagerClient.client.getSecretValue({ SecretId: secretName }).promise();

      console.log(`Successfully read secret from aws secrets manager`);

      return response.SecretString;
    } catch (e) {
      console.error(`Failed to read secret from aws secrets manager`);
      console.error(e);
      throw new HttpException('Internal Server Error', 500);
    }
  }
}

然后我使用 Vault 进行身份验证以获取身份验证令牌:

// vault-auth.client.ts

import * as https from 'https';

import axios, { AxiosResponse } from 'axios';
import AWS from 'aws-sdk';
import { Request } from 'aws4';
import * as aws4 from 'aws4';

import HttpException from '../errors/http.exception';
import VaultAuthResponse from '../models/vault/vault-auth-response.model';
import VaultAuthRequest from '../models/vault/vault-auth-request.model';

export default class VaultAuthClient {
  private readonly GET_CALLER_IDENTITY: string = 'Action=GetCallerIdentity&Version=2011-06-15';

  async authenticate(caCert: string): Promise<string> {
    console.log(`Attempting to authenticate with vault`);

    const axiosConfig = {
      timeout: 5000,
      httpsAgent: new https.Agent({
        ca: caCert,
      }),
      headers: {
        'X-Vault-Namespace': process.env.VAULT_NAMESPACE,
      },
    };

    const url: string = `${process.env.VAULT_URL}/v1/auth/aws/login`;
    const request = this.createVaultRequest();
  
    try {
      const vaultResponse: AxiosResponse<VaultAuthResponse> = await axios.post(url, request, axiosConfig);
  
      console.log(`Successfully authenticated with vault`);
      return vaultResponse.data.auth.client_token;
    } catch (e) {
      console.error(`Failed to authenticate with vault`);
      console.error(e.stack);
      throw new HttpException('Internal Server Error', 500);
    }

   
  }

  private createSTSRequest(): Request {
    const credentials: AWS.Credentials = new AWS.Credentials({
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      sessionToken: process.env.AWS_SESSION_TOKEN,
    });

    const request = aws4.sign(
      {
        service: 'sts',
        method: 'POST',
        body: this.GET_CALLER_IDENTITY,
        headers: process.env.VAULT_HOST
          ? {
              'X-Vault-AWS-IAM-Server-ID': process.env.VAULT_HOST,
            }
          : {},
      },
      credentials,
    );

    return request;
  }

  private createVaultRequest() {
    const stsRequest: Request = this.createSTSRequest();

    const vaultAuthRequest: VaultAuthRequest = new VaultAuthRequest(
      stsRequest.method,
      encode(JSON.stringify(stsRequest.headers), 'base64'),
      encode(this.GET_CALLER_IDENTITY, 'base64'),
      encode(`https://${stsRequest.hostname}${stsRequest.path}`, 'base64'),
      vault.vaultRole,
    );

    return vaultAuthRequest;
  }
}

然后我打电话给金库来获取秘密:

import * as https from 'https';

import axios from 'axios';

import HttpException from '../errors/http.exception';

export default class VaultClient {
  private readonly VAULT_URL: string = `${process.env.VAULT_URL}/v1/secrets/data/${process.env.VAULT_ENV}`;

  async getVaultSecrets(token: string, caCert: string, path: string, key: string): Promise<string> {
    console.log(`Attempting to read secrets from vault`);

    const axiosConfig = {
      timeout: 5000,
      httpsAgent: new https.Agent({
        ca: caCert,
      }),
      headers: {
        'X-Vault-Namespace': process.env.VAULT_NAMESPACE,
        'X-Vault-Token': token,
      },
    };

    try {
      const url: string = `${this.VAULT_URL}/${path}`;
      const {
        data: {
          data: { data },
        },
      } = await axios.get(url, axiosConfig);
      const secret = data[`${key}`];

      console.log(`Successfully read secret from vault`);

      return secret;
    } catch (e) {
      console.error(`Failed to read secret from vault`);
      console.error(e.stack);

      throw new HttpException('Internal Server Error', 500);
    }
  }
}

// Get the Vault CA Cert
const caCert: string = await AWSSecretsManagerClient.getSecret(process.env.CA_CERT_SECRET_NAME);

// Get the Vault Auth Token
const token: string = await new VaultAuthClient().authenticate(caCert);

// Get the Secret
const secret: string = await new VaultClient().getVaultSecrets(token, caCert, 'path-to-secret', 'secret-name');

© www.soinside.com 2019 - 2024. All rights reserved.