docker-swarm-secrets
Version:
A manager for Docker secrets that features customizable secret parsing and async I/O.
240 lines • 14.5 kB
JavaScript
"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