Skip to content

nodevault/node-vault

Repository files navigation

node-vault

Build Status Download Status NPM Version License Dependency Status Open Collective backers and sponsors

A client for the HTTP API of HashiCorp's Vault written for Node.js.

Install

Prerequisites:

  • Node.js >= 18.0.0
npm install -S node-vault

Note: If you need to use an older version of Node.js (>= 6.x), use node-vault <= v0.10.0. Please be aware that node-vault <= v0.10.0 contains multiple known vulnerabilities ☠️

TypeScript definitions are included in the package.

Test

Run tests using docker-compose (includes vault and postgres) with:

docker-compose up --force-recreate test

Configuration

Client Options

const vault = require('node-vault')({
  apiVersion: 'v1',                        // API version (default: 'v1')
  endpoint: 'http://127.0.0.1:8200',      // Vault server URL (default: 'http://127.0.0.1:8200')
  token: 'MY_TOKEN',                       // Vault token for authentication
  pathPrefix: '',                          // Optional prefix for all request paths
  namespace: 'my-namespace',               // Vault Enterprise namespace
  noCustomHTTPVerbs: false,                // Use GET with ?list=1 instead of LIST HTTP method
  requestOptions: {},                      // Custom axios request options applied to all requests
});

Environment Variables

The client reads the following environment variables as defaults:

Variable Description
VAULT_ADDR Vault server URL (overridden by endpoint option)
VAULT_TOKEN Vault token (overridden by token option)
VAULT_NAMESPACE Vault Enterprise namespace (overridden by namespace option)
VAULT_PREFIX Request path prefix (overridden by pathPrefix option)
VAULT_SKIP_VERIFY When set, disables SSL certificate verification

Usage

Init and unseal

const vault = require('node-vault')({
  apiVersion: 'v1',
  endpoint: 'http://127.0.0.1:8200',
  token: 'MY_TOKEN', // optional; can be set after initialization
});

// init vault server
vault.init({ secret_shares: 1, secret_threshold: 1 })
  .then((result) => {
    const keys = result.keys;
    // set token for all following requests
    vault.token = result.root_token;
    // unseal vault server
    return vault.unseal({ secret_shares: 1, key: keys[0] });
  })
  .catch(console.error);

Write, read, update and delete secrets

vault.write('secret/hello', { value: 'world', lease: '1s' })
  .then(() => vault.read('secret/hello'))
  .then(() => vault.delete('secret/hello'))
  .catch(console.error);

The update method sends a PATCH request with application/merge-patch+json content type:

vault.update('secret/data/hello', { data: { value: 'new-world' } })
  .catch(console.error);

List secrets

vault.list('secret/metadata/')
  .then((result) => console.log(result.data.keys))
  .catch(console.error);

Kubernetes Auth Example

const fs = require('fs');

// Read service account token from default mount path
const jwt = fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', { encoding: 'utf8' });

// If the Vault Kubernetes auth endpoint is /auth/example-cluster/login and the role is example-role
vault.kubernetesLogin({
  role: 'example-role',
  jwt: jwt,
  mount_point: 'example-cluster',
}).catch(console.error);

AppRole Auth Example

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

vault.approleLogin({
  role_id: 'my-role-id',
  secret_id: 'my-secret-id',
})
  .then((result) => {
    // client token is automatically set on successful login
    console.log(result.auth.client_token);
  })
  .catch(console.error);

Error Handling

The client exposes two error types accessible from the module:

  • VaultError — Base error class for all vault-related errors.
  • ApiResponseError — Thrown on non-200/204 responses. Contains a response property with statusCode and body.
vault.read('secret/missing')
  .catch((err) => {
    console.error(err.message);       // Error message from Vault
    if (err.response) {
      console.error(err.response.statusCode); // e.g. 404
      console.error(err.response.body);       // Response body from Vault
    }
  });

Custom Commands

You can register custom API commands using generateFunction:

vault.generateFunction('myCustomEndpoint', {
  method: 'GET',
  path: '/my-custom/endpoint/{{id}}',
});

// Use the generated function
vault.myCustomEndpoint({ id: 'abc123' })
  .then(console.log)
  .catch(console.error);

Docs

Generate docco docs via:

npm run docs

Examples

Please have a look at the examples and the generated feature list to see all supported Vault API endpoints.

Instead of installing all the dependencies like vault itself and postgres, you can use docker and docker-compose to link and run multiple docker containers with all of their dependencies.

git clone git@github.com:nodevault/node-vault.git
cd node-vault
docker-compose up vault

Now you can run the examples from another terminal window.

First of all you should initialize and unseal the vault:

node example/init.js

You should see root_token: followed by a long key in the response. Please copy that long key and export it as an environment variable:

export VAULT_TOKEN=<insert long key here>

Now you are able to run all of the other examples:

node example/policies.js

Connecting to Vault Through a Bastion Host

To connect to a vault server in a private network through a bastion host, first open a SOCKS proxy connection:

ssh -D <socksPort> bastion.example.com

Then configure the client with a SOCKS proxy agent:

const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent(`socks://127.0.0.1:${socksPort}`);

const vault = require('node-vault')({
  apiVersion: 'v1',
  requestOptions: {
    httpsAgent: agent,
    httpAgent: agent,
  },
});

Custom SSL/TLS Configuration

If you encounter SSL errors after upgrading to Node 18+ (e.g., EPROTO errors related to unsafe legacy renegotiation disabled), you can pass SSL/TLS options via requestOptions or rpDefaults when initializing the client:

const vault = require('node-vault')({
  apiVersion: 'v1',
  endpoint: 'https://vault.example.com:8200',
  token: 'MY_TOKEN',
  requestOptions: {
    agentOptions: {
      securityOptions: 'SSL_OP_LEGACY_SERVER_CONNECT',
    },
  },
});

The requestOptions object supports TLS/SSL options (ca, cert, key, passphrase, agentOptions, strictSSL) as well as timeout, httpsAgent, and httpAgent. TLS options are mapped to an https.Agent and applied to every request. You can also pass native axios request options such as custom headers.

You can also pass request options per-call to any method:

vault.read('secret/hello', {
  agentOptions: {
    securityOptions: 'SSL_OP_LEGACY_SERVER_CONNECT',
  },
});

See example/pass_request_options.js for more examples.

Backers