@larskarbo/gitops-secrets
Version:
SecretOps workflow for bundling encrypted secrets into your deployments to safely decrypt at runtime.
123 lines (122 loc) • 5.53 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const path = require("path");
const secrets_1 = require("./secrets");
const SECRETS_FOLDER = path.join(__dirname, "../.secrets");
const DEFAULT_JS_PATH = path.join(SECRETS_FOLDER, ".secrets.enc.js");
const DEFAULT_FILE_PATH = path.join(SECRETS_FOLDER, ".secrets.enc.json");
// eslint-disable-next-line security/detect-non-literal-fs-filename
if (!(0, fs_1.existsSync)(SECRETS_FOLDER)) {
// eslint-disable-next-line security/detect-non-literal-fs-filename
(0, fs_1.mkdirSync)(SECRETS_FOLDER);
}
/**
* Encapsulate encrypted secrets in a JS module for easy runtime access.
* Use {options.path} to output module locally for when package level storage or non-literal imports are disallowed.
* Use {options.cipherTextOnly} to limit the JS file to only exporting `CIPHER_TEXT`
* @param {<Record<string, any>} payload
* @param {{path: string, cipherTextOnly: boolean}} [options={path: null, cipherTextOnly: false}]
*/
function build(payload, options = { path: null, cipherTextOnly: false, typescript: false }) {
return __awaiter(this, void 0, void 0, function* () {
const cipherText = secrets_1.default.encrypt(payload);
const filePath = options.path ? path.resolve(options.path) : DEFAULT_JS_PATH;
const packageType = process.env.npm_package_type === "module" ? "esm" : "cjs";
const format = filePath === DEFAULT_JS_PATH ? "cjs" : packageType;
let lines = ["/* eslint-disable */", "// This file was auto-generated by gitops-secrets"];
if (options.typescript) {
lines = [
...lines,
`export const CIPHER_TEXT = "${cipherText}";`,
];
}
else if (format === "esm") {
if (!options.cipherTextOnly) {
lines = [
...lines,
`import secrets from "gitops-secrets/no-fs";`,
`const CIPHER_TEXT = "${cipherText}";`,
"const loadSecrets = () => secrets.loadSecretsFromCipher(CIPHER_TEXT);",
"export { CIPHER_TEXT, loadSecrets };",
];
}
else {
lines = [...lines, `const CIPHER_TEXT = "${cipherText}";`, "export { CIPHER_TEXT };"];
}
}
else if (format === "cjs") {
if (!options.cipherTextOnly) {
lines = [
...lines,
`const secrets = require("gitops-secrets/no-fs");`,
`const CIPHER_TEXT = "${cipherText}";`,
"const loadSecrets = () => secrets.loadSecretsFromCipher(CIPHER_TEXT);",
"module.Object = { CIPHER_TEXT, loadSecrets };",
];
}
else {
lines = [...lines, `const CIPHER_TEXT = "${cipherText}";`, "module.Object = { CIPHER_TEXT };"];
}
}
writeFile(filePath, lines.join("\n"));
});
}
/**
* Encrypt JSON-serializable payload to a static file
* @param {Record<string,any>} payload
* @param {{path: string}} [options={ path: null }]
*/
function encryptToFile(payload, options = { path: null }) {
const cipherText = secrets_1.default.encrypt(payload);
const filePath = options.path ? path.resolve(options.path) : DEFAULT_FILE_PATH;
console.log('filePath: ', filePath);
writeFile(filePath, cipherText);
}
/**
* Decrypt JSON payload to object with option to merge with process.env
* @param {string} filePath
* @returns
*/
function decryptFromFile(filePath) {
filePath = filePath ? path.resolve(filePath) : DEFAULT_FILE_PATH;
try {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const payload = secrets_1.default.decrypt((0, fs_1.readFileSync)(filePath, { encoding: "utf-8" }));
return Object.assign(Object.assign({}, payload), { populateEnv: () => secrets_1.default.populateEnv(payload) });
}
catch (error) {
throw new Error(`Unable to read secrets from ${filePath}: ${error}`);
}
}
function writeFile(filePath, fileContents) {
try {
// eslint-disable-next-line security/detect-non-literal-fs-filename
(0, fs_1.writeFileSync)(filePath, fileContents, { encoding: "utf-8" });
}
catch (error) {
throw new Error(`Unable to write secrets to ${filePath}: ${error}`);
}
}
function loadSecrets() {
// eslint-disable-next-line security/detect-non-literal-require
return require(DEFAULT_JS_PATH).loadSecrets();
}
const exportObject = {
build: build,
encryptToFile: encryptToFile,
decryptFromFile: decryptFromFile,
loadSecrets: loadSecrets,
DEFAULT_JS_PATH: DEFAULT_JS_PATH,
DEFAULT_FILE_PATH: DEFAULT_FILE_PATH,
};
exports.default = exportObject;