/gitops-secrets-nodejs

GitOps encrypted secrets workflow for Node.js

Primary LanguageJavaScriptApache License 2.0Apache-2.0

GitOps Secrets

A SecretOps workflow for bundling encrypted secrets into your deployments.

GitOps SecretsDiagram

Usage

  1. Install the gitops-secretes package:
npm install gitops-secrets
  1. Bundle encrypted secrets into your build
// ./bin/encrypt-secrets.js
const secrets = require("gitops-secrets");

async function main() {
  const payload = await secrets.providers.doppler.fetch();
  secrets.build(payload);
}

main();
// package.json
{
  "scripts: {
    ...
    "encrypt-secrets": "node ./bin/encrypt-secrets.js"
  }
}
  1. Decrypt secrets at runtime
const { loadSecrets } = require("gitops-secrets");
const secrets = loadSecrets();

Background and Motivation

Exceeding AWS Lambda's 4KB environment variable limit is a common problem that also impacts platforms such as Vercel and the Serverless framework which deploy on top of AWS Lambda.

A SecretOps workflow that bundles encrypted secrets into a deployment eliminates such environment variable limits without insecure hacks such as storing unencrypted .env files in your builds.

As creators of the Doppler SecretOps Platform which provide secrets sync integrations for Vercel and Serverless, we built this to provide a secure solution for our customers and the open source community.

Our goal was to design a new way of accessing secrets in production that:

  • Allowed for a secrets payload of any size
  • Could be up and running in minutes
  • Scaled to work in any environment, including local development
  • Could support the most restrictive serverless platforms
  • Provided first-class support for ES modules
  • Prevented unencrypted secrets from ever touching the file system
  • Abstracted away the complexity of secrets fetching using community-contributed providers

Providers

A provider is designed to abstract away the complexities of fetching secrets from any secret manager or secrets store by exposing a single async fetch method.

A secrets provider returns a plain Key-Value Object to ensure that serializing to and from JSON during encryption and decryption produces the same object structure initially fetched from the provider.

The current list of providers are:

We'd love to see the list of providers grow! Please see our contributing guide to get started.

Encryption and Decryption

There are two file formats available for bundling encrypted secrets into your deployments:

  • JSON: Encrypted JSON file.
  • JS Module: Encrypted JSON embedded in JS module.

JSON

To encrypt secrets to a JSON file:

const secrets = require("gitops-secrets");

async function main() {
  const payload = await secrets.providers.doppler.fetch();

  // Internally managed storage
  secrets.encryptToFile(payload);

  // Custom path
  secrets.encryptToFile(payload, { path: ".secrets.enc.json" });
}

main();

To decrypt secrets from a JSON file:

const { decryptFromFile } = require("gitops-secrets");

// Internally managed storage
const secrets = decryptFromFile();

// Custom Path
const secrets = decryptFromFile(".secrets.enc.json");

// Optionally merge secrets into environment variables
secrets.populateEnv();

JS Module

The JS module format is ideal for restricted environments such as Vercel where application-wide access to reading static files is problematic.

Depending upon the deployment platform and framework, you can potentially omit the path parameter to have encrypted secrets access and storage managed internally for you.

But if using Vercel with Next.js for example, the path configures the module to be output in your codebase with the format of the module matching that of your application.

To encrypt secrets to a JS module:

const secrets = require("gitops-secrets");

async function main() {
  const payload = await secrets.providers.doppler.fetch();

  // Option 1: Internally managed storage
  secrets.build(payload);

  // Option 2: Custom path for restrictive environments
  secrets.build(payload, { path: "lib/secrets.js" });
}

main();

To decrypt secrets from a JS module using internally managed storage, use the package-level loadSecrets method:

const { loadSecrets } = require("gitops-secrets");

const secrets = loadSecrets();

// Optionally merge secrets into environment variables
secrets.populateEnv();

Or use the loadSecrets method from the generated module (ES modules also supported):

const { loadSecrets } = require("../lib/secrets");
const secrets = loadSecrets();

// Optionally merge secrets into environment variables
secrets.populateEnv();

Getting Started

We recommend checking out the Working around Vercel's 4KB Environment Variables Limit for Node.js with GitOps Secrets blog post which guides you through the entire process.

Or take a look at the Vercel GitOps Secrets Next.js sample repository to see a complete working example that you can test and deploy to Vercel.

Support

You can get support in the Doppler community forum, find us on Twitter, and for bugs or feature requests, create an issue on the DopplerHQ/gitops-secrets-nodejs GitHub repository.

We'd also love to see the number of providers grow, and you can check out our contributing guide to get started.