UNPKG

docker-swarm-secrets

Version:

A manager for Docker secrets that features customizable secret parsing and async I/O.

240 lines 14.5 kB
"use strict"; 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()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); var fs = __importStar(require("fs")); var path = __importStar(require("path")); var default_interpreters_1 = require("../interpreters/default-interpreters"); /** The default mount point of a docker secrets file system. */ var DEFAULT_DOCKER_SECRETS_MOUNT = '/run/secrets'; /** Defines a Docker Swarm Secrets reader object, which reads secrets from a configured secrets mount point (/run/secrets by default). */ var DSSReader = /** @class */ (function () { /** Builds a new DSSReader for a Docker secrets filesystem at a given mount point. */ function DSSReader(secretsDirectory) { if (secretsDirectory === void 0) { secretsDirectory = DEFAULT_DOCKER_SECRETS_MOUNT; } this.secretsDirectory = secretsDirectory; } /** * Reads a single secret by name asynchronously, optionally parsing it into type T using an `interpreter` function. * @param name The name of the secret to read * @param interpreter The interpreter function to run on the secret. * This function will be called on a secret after it is read, setting the calculated value of the secret to its return value. * This may be used to check data for validity, deserialize data, and/or any other work necessary to parse the raw secret data as type T. * If omitted, T is assumed to be Buffer and the secret data is returned as a raw Buffer. * @param callback Optional callback for handling the asynchronous return value, if preferred to async/await. */ DSSReader.prototype.readSecret = function (name, interpreter) { return __awaiter(this, void 0, void 0, function () { var data, secret; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.readFileIgnoreMissing(name)]; case 1: data = _a.sent(); secret = (interpreter !== null && interpreter !== void 0 ? interpreter : default_interpreters_1.DefaultInterpreters.asBuffer())({ name: name, data: data }); // Return the result return [2 /*return*/, { name: name, data: data, secret: secret }]; } }); }); }; /** * Reads all available secrets asynchronously, optionally parsing them using `interpreter` and `predicate` functions. * Secrets are returned as an object keyed by secret name. * @param interpreters The interpreter functions to run on secrets. * If a given `predicate` returns true for a secret, the associated `interpreter` will be called. * First matching interpreter wins. Secrets that do not match any interpreter will be ignored. * If no predicate is provided, the interpreter will match all secrets. This will prevent any subsequent interpreters from being checked. * Interpreter functions set the calculated value of the secret to their return value. * This may be used to check data for validity, deserialize data, and/or any other work necessary to parse the raw secret data. * If no interpreters are provided, all available secrets will be returned as raw Buffers. * @param callback Optional callback for handling the asynchronous return value, if preferred to async/await. */ DSSReader.prototype.readSecrets = function (interpreters) { return __awaiter(this, void 0, void 0, function () { var result, dir, _i, dir_1, name, data, secret, _a, interpreters_1, candidate; return __generator(this, function (_b) { switch (_b.label) { case 0: result = {}; // Handle varied interpreter args interpreters = interpreters !== null && interpreters !== void 0 ? interpreters : [{ interpreter: default_interpreters_1.DefaultInterpreters.asBuffer() }]; if (!Array.isArray(interpreters)) { interpreters = [interpreters]; } return [4 /*yield*/, fs.promises.readdir(this.secretsDirectory)]; case 1: dir = _b.sent(); _i = 0, dir_1 = dir; _b.label = 2; case 2: if (!(_i < dir_1.length)) return [3 /*break*/, 5]; name = dir_1[_i]; return [4 /*yield*/, this.readFileIgnoreMissing(name)]; case 3: data = _b.sent(); secret = undefined; for (_a = 0, interpreters_1 = interpreters; _a < interpreters_1.length; _a++) { candidate = interpreters_1[_a]; // Secrets matching a predicate (or a default interpreter) are parsed and returned. if (!candidate.predicate || candidate.predicate({ name: name, data: data })) { secret = candidate.interpreter({ name: name, data: data }); result[name] = ({ name: name, data: data, secret: secret }); break; } } _b.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: // Return the result return [2 /*return*/, result]; } }); }); }; /** * Reads a single secret by name synchronously, optionally parsing it into type T using an `interpreter` function. * @param name The name of the secret to read * @param interpreter The interpreter function to run on the secret. * This function will be called on a secret after it is read, setting the calculated value of the secret to its return value. * This may be used to check data for validity, deserialize data, and/or any other work necessary to parse the raw secret data as type T. * If omitted, T is assumed to be Buffer and the secret data is returned as a raw Buffer. * @param callback Optional callback for handling the asynchronous return value, if preferred to async/await. */ DSSReader.prototype.readSecretSync = function (name, interpreter) { // Read in the target secret file var data = this.readFileIgnoreMissingSync(name); // Run the interpreter -- Typecast is needed to satisfy compiler in the default case var secret = (interpreter !== null && interpreter !== void 0 ? interpreter : default_interpreters_1.DefaultInterpreters.asBuffer())({ name: name, data: data }); // Return the result return { name: name, data: data, secret: secret }; }; /** * Reads all available secrets synchronously, optionally parsing them using `interpreter` and `predicate` functions. * Secrets are returned as an object keyed by secret name. * @param interpreters The interpreter functions to run on secrets. * If a given `predicate` returns true for a secret, the associated `interpreter` will be called. * First matching interpreter wins. Secrets that do not match any interpreter will be ignored. * If no predicate is provided, the interpreter will match all secrets. This will prevent any subsequent interpreters from being checked. * Interpreter functions set the calculated value of the secret to their return value. * This may be used to check data for validity, deserialize data, and/or any other work necessary to parse the raw secret data. * If no interpreters are provided, all available secrets will be returned as raw Buffers. * @param callback Optional callback for handling the asynchronous return value, if preferred to async/await. */ DSSReader.prototype.readSecretsSync = function (interpreters) { // Create result array var result = {}; // Handle varied interpreter args interpreters = interpreters !== null && interpreters !== void 0 ? interpreters : [{ interpreter: default_interpreters_1.DefaultInterpreters.asBuffer() }]; if (!Array.isArray(interpreters)) { interpreters = [interpreters]; } // Read each file in the target directory var dir = fs.readdirSync(this.secretsDirectory); for (var _i = 0, dir_2 = dir; _i < dir_2.length; _i++) { var name = dir_2[_i]; var data = this.readFileIgnoreMissingSync(name); // Append the default unpredicated interpreter, then check for valid interpreters var secret = undefined; for (var _a = 0, interpreters_2 = interpreters; _a < interpreters_2.length; _a++) { var candidate = interpreters_2[_a]; // Secrets matching a predicate (or a default interpreter) are parsed and returned. if (!candidate.predicate || candidate.predicate({ name: name, data: data })) { secret = candidate.interpreter({ name: name, data: data }); result[name] = ({ name: name, data: data, secret: secret }); break; } } } // Return the result return result; }; /** * Reads a file in the secrets directory by name, returning undefined if it is missing instead of throwing an error. * @param name The file name to read */ DSSReader.prototype.readFileIgnoreMissing = function (name) { return __awaiter(this, void 0, void 0, function () { var err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, fs.promises.readFile(path.join(this.secretsDirectory, name))]; case 1: return [2 /*return*/, _a.sent()]; case 2: err_1 = _a.sent(); // Missing secrets are not an error condition, just an undefined data buffer for the interpreter. if (err_1.code === 'ENOENT') { return [2 /*return*/, undefined]; } throw err_1; case 3: return [2 /*return*/]; } }); }); }; /** * Reads a file in the secrets directory by name synchronously, returning undefined if it is missing instead of throwing an error. * @param name The file name to read */ DSSReader.prototype.readFileIgnoreMissingSync = function (name) { // Read in the target secret file try { return fs.readFileSync(path.join(this.secretsDirectory, name)); } catch (err) { // Missing secrets are not an error condition, just an undefined data buffer for the interpreter. if (err.code === 'ENOENT') { return undefined; } throw err; } }; return DSSReader; }()); exports.DSSReader = DSSReader; //# sourceMappingURL=dss-reader.js.map