如何使用IAM身份验证的Nodejs中的AWS lambda调用Hashicorp库

发布于 2025-02-07 11:35:37 字数 396 浏览 1 评论 0原文

有人知道是否有一个很好的Nodejs库可以使用IAM身份验证从AWS Lambda连接到Hashicorp Vault。

类似于 hvac 因为python会很好。

我尝试使用 node-vault-client IAM身份验证,自2019年以来似乎没有进行更新,因此我不确定它是否积极维护。

Does anyone know if there is a good NodeJS library to connect to hashicorp vault from an AWS Lambda using IAM Authentication.

Something similar to HVAC for Python would be good.

I have tried using node-vault-client but there aren't any good examples of IAM Authentication and it doesn't seem to have had an update since 2019 so I am not sure if its actively being maintained.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

皇甫轩 2025-02-14 11:35:39

我设法使用 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进行身份验证以获取auth Token:

// 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;
  }
}

然后,我打电话给Vault获取秘密:

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');

I managed to get it working using node-vault-client but I had to make changes to the library because it doesnt allow you to pass a namespace header. I have raised a PR that adds a new field to resolve the issue.

Here is a sample of my code:

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);
    });

************ UPDATE **************

I have since decided to write the code to retrieve the secrets from vault myself and remove the dependency on node-vault-client.

First, I need to call AWS Secrets Manager to get the Vault CA Certificate

// 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);
    }
  }
}

Then I authenticate with Vault to get an auth token:

// 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;
  }
}

Then I call vault to get the Secret:

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');

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文