Skip to content

Repository files navigation

node-vault

Build StatusDownload StatusNPM VersionLicenseDependency StatusOpen 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

constvault=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 authenticationpathPrefix: '',// Optional prefix for all request pathsnamespace: 'my-namespace',// Vault Enterprise namespacenoCustomHTTPVerbs: false,// Use GET with ?list=1 instead of LIST HTTP methodrequestOptions: {},// Custom axios request options applied to all requests});

Note: Trailing slashes in the endpoint URL (e.g. http://127.0.0.1:8200/) are automatically stripped to prevent malformed request URIs.

Environment Variables

The client reads the following environment variables as defaults:

VariableDescription
VAULT_ADDRVault server URL (overridden by endpoint option)
VAULT_TOKENVault token (overridden by token option)
VAULT_NAMESPACEVault Enterprise namespace (overridden by namespace option)
VAULT_PREFIXRequest path prefix (overridden by pathPrefix option)
VAULT_SKIP_VERIFYWhen set, disables SSL certificate verification

Usage

Init and unseal

constvault=require('node-vault')({apiVersion: 'v1',endpoint: 'http://127.0.0.1:8200',token: 'MY_TOKEN',// optional; can be set after initialization});// init vault servervault.init({secret_shares: 1,secret_threshold: 1}).then((result)=>{constkeys=result.keys;// set token for all following requestsvault.token=result.root_token;// unseal vault serverreturnvault.unseal({secret_shares: 1,key: keys[0]});}).catch(console.error);

Unseal a vault that is already initialized

If the vault server has been restarted or sealed, you can unseal it using the unseal keys from the original initialization. If the vault was initialized with secret_threshold > 1, you must call unseal multiple times with different keys until the threshold is met.

constvault=require('node-vault')({apiVersion: 'v1',endpoint: 'http://127.0.0.1:8200',});// unseal vault server with a single keyvault.unseal({key: 'my-unseal-key'}).then(console.log).catch(console.error);

When the vault requires multiple unseal keys (threshold > 1):

vault.unseal({key: 'first-unseal-key'}).then((result)=>{// result.sealed will be true until enough keys are providedconsole.log('Sealed:',result.sealed);console.log('Progress:',result.progress+'/'+result.t);returnvault.unseal({key: 'second-unseal-key'});}).then((result)=>{// once the threshold is met, sealed will be falseconsole.log('Sealed:',result.sealed);}).catch(console.error);

See example/unseal.js for a working example.

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

constfs=require('fs');// Read service account token from default mount pathconstjwt=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-rolevault.kubernetesLogin({role: 'example-role',jwt: jwt,mount_point: 'example-cluster',}).catch(console.error);

AppRole Auth Example

constvault=require('node-vault')();vault.approleLogin({role_id: 'my-role-id',secret_id: 'my-secret-id',}).then((result)=>{// client token is automatically set on successful loginconsole.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 Vaultif(err.response){console.error(err.response.statusCode);// e.g. 404console.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 functionvault.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');constagent=newSocksProxyAgent(`socks://127.0.0.1:${socksPort}`);constvault=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:

constvault=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

Releases

Packages

Used by

Contributors

Languages