An open API service indexing awesome lists of open source software.

https://github.com/nodevault/node-vault

Client for HashiCorp's Vault
https://github.com/nodevault/node-vault

api api-client client hashicorp node node-vault nodejs secrets vault

Last synced: about 1 month ago
JSON representation

Client for HashiCorp's Vault

Awesome Lists containing this project

README

          

# node-vault

[![Build Status](https://img.shields.io/github/actions/workflow/status/nodevault/node-vault/lint-and-test.yaml?branch=master&style=flat-square)](https://github.com/nodevault/node-vault/actions/workflows/lint-and-test.yaml)
[![Download Status](https://img.shields.io/npm/dm/node-vault.svg?style=flat-square)](https://www.npmjs.com/package/node-vault)
[![NPM Version](https://img.shields.io/npm/v/node-vault?style=flat-square)](https://www.npmjs.com/package/node-vault)
[![License](https://img.shields.io/npm/l/node-vault?style=flat-square)](https://github.com/nodevault/node-vault/blob/master/LICENSE)
[![Dependency Status](https://img.shields.io/librariesio/release/npm/node-vault.svg?style=flat-square)](https://libraries.io/npm/node-vault/)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/node-vault?style=flat-square)](https://opencollective.com/node-vault/contribute)

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

## Install

Prerequisites:
- Node.js >= `18.0.0`

```bash
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:
```bash
docker-compose up --force-recreate test
```

## Configuration

### Client Options

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

> **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:

| 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

```javascript
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);
```

### 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.

```javascript
const vault = require('node-vault')({
apiVersion: 'v1',
endpoint: 'http://127.0.0.1:8200',
});

// unseal vault server with a single key
vault.unseal({ key: 'my-unseal-key' })
.then(console.log)
.catch(console.error);
```

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

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

See [example/unseal.js](example/unseal.js) for a working example.

### Write, read, update and delete secrets

```javascript
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:

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

### List secrets

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

### Kubernetes Auth Example

```javascript
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

```javascript
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`.

```javascript
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`:

```javascript
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:
```bash
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.

```bash
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:
```bash
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:
```bash
export VAULT_TOKEN=
```

Now you are able to run all of the other [examples]:
```bash
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:
```bash
ssh -D bastion.example.com
```

Then configure the client with a SOCKS proxy agent:
```javascript
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:

```javascript
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](https://axios-http.com/) request options such as custom `headers`.

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

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

See [example/pass_request_options.js](example/pass_request_options.js) for more examples.

[![Backers](https://opencollective.com/node-vault/tiers/backers.svg?avatarHeight=80&width=600)](https://opencollective.com/node-vault/contribute)

[examples]: https://github.com/nodevault/node-vault/tree/master/example
[docker-compose.yml]: https://github.com/nodevault/node-vault/tree/master/docker-compose.yml
[Vault]: https://vaultproject.io/
[docker-compose]: https://docs.docker.com/compose/
[docker]: https://docs.docker.com/
[docco]: http://jashkenas.github.io/docco
[feature list]: https://github.com/nodevault/node-vault/tree/master/features.md