diff --git a/.changeset/add-keeper-plugin.md b/.changeset/add-keeper-plugin.md new file mode 100644 index 00000000..5e3ed03b --- /dev/null +++ b/.changeset/add-keeper-plugin.md @@ -0,0 +1,5 @@ +--- +"@varlock/keeper-plugin": minor +--- + +Add Keeper Security plugin for loading secrets from Keeper vaults via the Secrets Manager SDK. Supports fetching secrets by record UID, title, or Keeper notation syntax, with access to both standard and custom fields. Includes `keeperSmToken` data type for config token validation, `@initKeeper()` root decorator for initialization, and `keeper()` resolver function for secret retrieval. diff --git a/bun.lock b/bun.lock index 9a1c38b2..7ca9a7bc 100644 --- a/bun.lock +++ b/bun.lock @@ -289,6 +289,22 @@ "varlock": "workspace:^", }, }, + "packages/plugins/keeper": { + "name": "@varlock/keeper-plugin", + "version": "0.0.1", + "devDependencies": { + "@env-spec/utils": "workspace:^", + "@keeper-security/secrets-manager-core": "17.4.0", + "@types/node": "catalog:", + "outdent": "catalog:", + "tsup": "catalog:", + "varlock": "workspace:^", + "vitest": "catalog:", + }, + "peerDependencies": { + "varlock": "workspace:^", + }, + }, "packages/plugins/pass": { "name": "@varlock/pass-plugin", "version": "0.0.6", @@ -906,6 +922,8 @@ "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + "@keeper-security/secrets-manager-core": ["@keeper-security/secrets-manager-core@17.4.0", "", {}, "sha512-KjJKPlQiHK00ft2lhirLSyZHv1qLui4oD0AkMTFXXC4BpNlQFwR/CLTtJc4dLTJ3tKoA5aGs8A8l4sNqcNPUxQ=="], + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], diff --git a/packages/plugins/keeper/CHANGELOG.md b/packages/plugins/keeper/CHANGELOG.md new file mode 100644 index 00000000..657aa81e --- /dev/null +++ b/packages/plugins/keeper/CHANGELOG.md @@ -0,0 +1 @@ +# @varlock/keeper-plugin diff --git a/packages/plugins/keeper/README.md b/packages/plugins/keeper/README.md new file mode 100644 index 00000000..0578d1e4 --- /dev/null +++ b/packages/plugins/keeper/README.md @@ -0,0 +1,253 @@ +# @varlock/keeper-plugin + +[![npm version](https://img.shields.io/npm/v/@varlock/keeper-plugin.svg)](https://www.npmjs.com/package/@varlock/keeper-plugin) [![GitHub stars](https://img.shields.io/github/stars/dmno-dev/varlock.svg?style=social&label=Star)](https://github.com/dmno-dev/varlock) [![license](https://img.shields.io/npm/l/@varlock/keeper-plugin.svg)](https://github.com/dmno-dev/varlock/blob/main/LICENSE) + +This package is a [Varlock](https://varlock.dev) [plugin](https://varlock.dev/guides/plugins/) that enables loading secrets from [Keeper Security](https://keepersecurity.com/) vaults via the [Keeper Secrets Manager](https://docs.keeper.io/secrets-manager/) SDK. + +## Features + +- 🔐 Fetch individual secrets by record UID, title, or Keeper notation +- 🏷️ Access standard and custom fields (password, login, URL, notes, etc.) +- 🔑 Secure authentication via base64-encoded Secrets Manager configuration +- 📦 Multiple instance support for different vaults or applications +- ✅ Built-in validation for Keeper Secrets Manager config tokens + +## Installation + +```bash +# npm +npm install @varlock/keeper-plugin + +# pnpm +pnpm add @varlock/keeper-plugin + +# yarn +yarn add @varlock/keeper-plugin + +# bun +bun add @varlock/keeper-plugin +``` + +## Prerequisites + +You need a [Keeper Secrets Manager](https://docs.keeper.io/secrets-manager/) application set up in your Keeper vault: + +1. In the Keeper Admin Console, create a **Secrets Manager Application** +2. Share a folder with the application +3. Generate a **one-time access token** +4. Use the [KSM CLI](https://docs.keeper.io/secrets-manager/secrets-manager/secrets-manager-command-line-interface) to initialize a config: + +```bash +pip install keeper-secrets-manager-cli +ksm profile init +ksm profile export --format json | base64 +``` + +5. Store the base64-encoded config as the `KSM_CONFIG` environment variable + +## Setup + +### Basic setup + +```env-spec title=".env.schema" +# @plugin(@varlock/keeper-plugin) +# @initKeeper(token=$KSM_CONFIG) +# --- + +# @type=keeperSmToken @sensitive +KSM_CONFIG= +``` + +### Multiple instances + +```env-spec title=".env.schema" +# @plugin(@varlock/keeper-plugin) +# @initKeeper(token=$KSM_CONFIG_PROD, id=prod) +# @initKeeper(token=$KSM_CONFIG_DEV, id=dev) +# --- + +# @type=keeperSmToken @sensitive +KSM_CONFIG_PROD= + +# @type=keeperSmToken @sensitive +KSM_CONFIG_DEV= +``` + +## Loading secrets + +### By record UID (defaults to password field) + +```env-spec title=".env.schema" +# fetches the "password" field from the record +DB_PASSWORD=keeper("XXXXXXXXXXXXXXXXXXXX") +``` + +### By record UID with a specific field + +```env-spec title=".env.schema" +# fetch the "login" standard field +DB_USER=keeper("XXXXXXXXXXXXXXXXXXXX#login") + +# fetch a custom field by label +API_KEY=keeper("XXXXXXXXXXXXXXXXXXXX#API_KEY") + +# or use the named field parameter +DB_HOST=keeper("XXXXXXXXXXXXXXXXXXXX", field="host") +``` + +### Using Keeper notation + +The plugin supports [Keeper's notation syntax](https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/javascript-sdk#notation) for more advanced access patterns: + +```env-spec title=".env.schema" +# standard field by type +DB_PASS=keeper("XXXX/field/password") + +# standard field by label +DB_LOGIN=keeper("XXXX/field/login") + +# custom field by label +MY_SECRET=keeper("XXXX/custom_field/MySecretLabel") + +# by record title instead of UID +API_KEY=keeper("My API Keys/field/password") +``` + +### With named instances + +```env-spec title=".env.schema" +# first arg is instance id, second is the secret reference +PROD_SECRET=keeper(prod, "XXXX/field/password") +DEV_SECRET=keeper(dev, "YYYY#password") +``` + +## Reference + +### Root decorators + +#### `@initKeeper()` + +Initialize a Keeper Secrets Manager plugin instance. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `token` | string | yes | Base64-encoded Secrets Manager config (typically from `$KSM_CONFIG`) | +| `id` | string | no | Instance identifier for multiple configurations (defaults to `_default`) | + +### Data types + +#### `keeperSmToken` + +A sensitive string type for Keeper Secrets Manager configuration tokens. Validates that the value is a valid base64-encoded JSON string. + +### Resolver functions + +#### `keeper(reference)` / `keeper(instanceId, reference)` + +Fetch a single secret field from Keeper. + +**Arguments:** + +| Position | Type | Required | Description | +|----------|------|----------|-------------| +| 1 | string | yes (if no instanceId) | Secret reference (see formats below) | +| 1, 2 | string, string | for named instances | Instance ID, then secret reference | + +**Named parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `field` | string | no | Explicit field type/label to extract | + +**Reference formats:** + +| Format | Description | Example | +|--------|-------------|---------| +| `` | Record UID (defaults to password field) | `keeper("XXXX")` | +| `#` | Record UID with field selector | `keeper("XXXX#login")` | +| `/field/` | Keeper notation (standard field) | `keeper("XXXX/field/password")` | +| `/custom_field/