sliftutils 1.7.124 → 1.7.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -1
- package/bin/derivekey.js +10 -0
- package/bin/portsecuredaemon.js +13 -0
- package/bin/securessh.js +10 -0
- package/bin/setupnotify.js +9 -0
- package/bin/signfiles.js +10 -0
- package/bin/unrevoke.js +9 -0
- package/package.json +14 -3
- package/security/README.md +141 -0
- package/security/authorizedKeys/authorizedKeys.ts +66 -0
- package/security/authorizedKeys/daemon/authLog.ts +117 -0
- package/security/authorizedKeys/daemon/daemon.ts +308 -0
- package/security/authorizedKeys/daemon/git.ts +119 -0
- package/security/authorizedKeys/daemon/notify.ts +25 -0
- package/security/authorizedKeys/daemon/paths.ts +26 -0
- package/security/authorizedKeys/daemon/portsecure.service +19 -0
- package/security/authorizedKeys/daemon/revocation.ts +306 -0
- package/security/authorizedKeys/daemon/rootKeys.ts +135 -0
- package/security/authorizedKeys/daemon/sshdConfig.ts +85 -0
- package/security/authorizedKeys/daemon/state.ts +108 -0
- package/security/authorizedKeys/daemon/trust.ts +291 -0
- package/security/authorizedKeys/daemon/userKeys.ts +76 -0
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +73 -0
- package/security/authorizedKeys/dist/revokeSource.ts.cache +44 -0
- package/security/authorizedKeys/dist/secureSSH.ts.cache +552 -0
- package/security/authorizedKeys/dist/sources.ts.cache +24 -0
- package/security/authorizedKeys/dist/unrevoke.ts.cache +145 -0
- package/security/authorizedKeys/revokeSource.ts +40 -0
- package/security/authorizedKeys/secureSSH.ts +613 -0
- package/security/authorizedKeys/sources.ts +20 -0
- package/security/authorizedKeys/unrevoke.ts +149 -0
- package/security/helpers/dist/paths.ts.cache +28 -0
- package/security/helpers/dist/remoteSSH.ts.cache +90 -0
- package/security/helpers/dist/spawn.ts.cache +34 -0
- package/security/helpers/paths.ts +20 -0
- package/security/helpers/remoteSSH.ts +95 -0
- package/security/helpers/spawn.ts +36 -0
- package/security/keys/deriveKey.ts +72 -0
- package/security/keys/dist/deriveKey.ts.cache +72 -0
- package/security/keys/dist/sshKeyFile.ts.cache +153 -0
- package/security/keys/sshKeyFile.ts +156 -0
- package/security/notifications/discord.ts +190 -0
- package/security/notifications/dist/discord.ts.cache +180 -0
- package/security/notifications/remoteWebhook.ts +85 -0
- package/security/notifications/setupNotify.ts +29 -0
- package/security/signedFiles/dist/manifest.ts.cache +68 -0
- package/security/signedFiles/dist/signFiles.ts.cache +146 -0
- package/security/signedFiles/manifest.ts +69 -0
- package/security/signedFiles/signFiles.ts +151 -0
- package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +17 -20
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js contains a plain JS port of everything here, so it can
|
|
3
|
+
// resolve the same locations with no dependencies. Both sides must derive identical paths from a
|
|
4
|
+
// repo url - if you change one, make the matching change in the other.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
+
//exports.sourceRepoPath = exports.sourceKeyPath = exports.sourceName = exports.REPOS_DIR = exports.REPO_KEYS_DIR = void 0;
|
|
7
|
+
exports.REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
|
|
8
|
+
exports.REPOS_DIR = "/var/lib/portsecure/authorized-keys-repos";
|
|
9
|
+
/** A repo url reduced to something usable as a file name. Derived rather than configured, so the
|
|
10
|
+
daemon and the deploy script always agree on where a source's key and checkout live. */
|
|
11
|
+
function sourceName(repoURL) {
|
|
12
|
+
return repoURL.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
exports.sourceName = sourceName;
|
|
15
|
+
function sourceKeyPath(repoURL) {
|
|
16
|
+
return `${exports.REPO_KEYS_DIR}/${sourceName(repoURL)}`;
|
|
17
|
+
}
|
|
18
|
+
exports.sourceKeyPath = sourceKeyPath;
|
|
19
|
+
function sourceRepoPath(repoURL) {
|
|
20
|
+
return `${exports.REPOS_DIR}/${sourceName(repoURL)}`;
|
|
21
|
+
}
|
|
22
|
+
exports.sourceRepoPath = sourceRepoPath;
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNvdXJjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHlIQUF5SDtBQUN6SCxpR0FBaUc7QUFDakcsdUVBQXVFOzs7QUFFMUQsUUFBQSxhQUFhLEdBQUcsMkJBQTJCLENBQUM7QUFDNUMsUUFBQSxTQUFTLEdBQUcsMkNBQTJDLENBQUM7QUFFckU7MkZBQzJGO0FBQzNGLFNBQWdCLFVBQVUsQ0FBQyxPQUFlO0lBQ3RDLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3hGLENBQUM7QUFGRCxnQ0FFQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxPQUFlO0lBQ3pDLE9BQU8sR0FBRyxxQkFBYSxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ3JELENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxPQUFlO0lBQzFDLE9BQU8sR0FBRyxpQkFBUyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ2pELENBQUM7QUFGRCx3Q0FFQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFBPUlRFRCBDT0RFOiBzZWN1cml0eS9hdXRob3JpemVkS2V5cy9kYWVtb24vcG9ydHNlY3VyZURhZW1vbi5qcyBjb250YWlucyBhIHBsYWluIEpTIHBvcnQgb2YgZXZlcnl0aGluZyBoZXJlLCBzbyBpdCBjYW5cbi8vIHJlc29sdmUgdGhlIHNhbWUgbG9jYXRpb25zIHdpdGggbm8gZGVwZW5kZW5jaWVzLiBCb3RoIHNpZGVzIG11c3QgZGVyaXZlIGlkZW50aWNhbCBwYXRocyBmcm9tIGFcbi8vIHJlcG8gdXJsIC0gaWYgeW91IGNoYW5nZSBvbmUsIG1ha2UgdGhlIG1hdGNoaW5nIGNoYW5nZSBpbiB0aGUgb3RoZXIuXG5cbmV4cG9ydCBjb25zdCBSRVBPX0tFWVNfRElSID0gXCIvZXRjL3BvcnRzZWN1cmUvcmVwby1rZXlzXCI7XG5leHBvcnQgY29uc3QgUkVQT1NfRElSID0gXCIvdmFyL2xpYi9wb3J0c2VjdXJlL2F1dGhvcml6ZWQta2V5cy1yZXBvc1wiO1xuXG4vKiogQSByZXBvIHVybCByZWR1Y2VkIHRvIHNvbWV0aGluZyB1c2FibGUgYXMgYSBmaWxlIG5hbWUuIERlcml2ZWQgcmF0aGVyIHRoYW4gY29uZmlndXJlZCwgc28gdGhlXG4gICAgZGFlbW9uIGFuZCB0aGUgZGVwbG95IHNjcmlwdCBhbHdheXMgYWdyZWUgb24gd2hlcmUgYSBzb3VyY2UncyBrZXkgYW5kIGNoZWNrb3V0IGxpdmUuICovXG5leHBvcnQgZnVuY3Rpb24gc291cmNlTmFtZShyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcmVwb1VSTC5yZXBsYWNlKC9bXkEtWmEtejAtOV0rL2csIFwiLVwiKS5yZXBsYWNlKC9eLSt8LSskL2csIFwiXCIpLnRvTG93ZXJDYXNlKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzb3VyY2VLZXlQYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPX0tFWVNfRElSfS8ke3NvdXJjZU5hbWUocmVwb1VSTCl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNvdXJjZVJlcG9QYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPU19ESVJ9LyR7c291cmNlTmFtZShyZXBvVVJMKX1gO1xufVxuIl19
|
|
24
|
+
/* _JS_SOURCE_HASH = "d4a51163d16ac57a8fa5966ca3b09eaebcbef4ca2200c706a7e125220c7db7b8"; */
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
+
//exports.main = exports.revokedKeysInRepo = exports.readRemoteRevocations = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const runPromise_1 = require("socket-function/src/runPromise");
|
|
11
|
+
const authorizedKeys_1 = require("./authorizedKeys");
|
|
12
|
+
const revokeSource_1 = require("./revokeSource");
|
|
13
|
+
const authorizedKeys_2 = require("./authorizedKeys");
|
|
14
|
+
const paths_1 = require("../helpers/paths");
|
|
15
|
+
const spawn_1 = require("../helpers/spawn");
|
|
16
|
+
const UNREVOKES_DIR = "unrevoked";
|
|
17
|
+
const REVOCATIONS_DIR = "revocations";
|
|
18
|
+
const USAGE = `Usage: yarn unrevoke [keys-repo]
|
|
19
|
+
|
|
20
|
+
Run this in a keys repo, or name one. It reads that repo's revoke repo and writes one unrevoke file
|
|
21
|
+
naming every revocation in it, so the keys are accepted again once each machine's hour long wait
|
|
22
|
+
passes.
|
|
23
|
+
|
|
24
|
+
Keys that were revoked should normally be deleted from the repo instead. Unrevoking only matters
|
|
25
|
+
for a key you still want.`;
|
|
26
|
+
/** Every revocation the revoke repo lists. Cloned read only into a temp directory, with whatever
|
|
27
|
+
credentials this machine already has - the derived deploy key is for servers, and a person
|
|
28
|
+
running this has their own access to the repo. */
|
|
29
|
+
async function readRemoteRevocations(config) {
|
|
30
|
+
let { sourceURL } = config;
|
|
31
|
+
let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "unrevoke-"));
|
|
32
|
+
let repoPath = path_1.default.join(temporaryDirectory, "repo");
|
|
33
|
+
let clone = await (0, spawn_1.spawnPromise)({
|
|
34
|
+
command: "git",
|
|
35
|
+
args: ["clone", "--depth", "1", (0, revokeSource_1.revokeRepoURL)(sourceURL), repoPath],
|
|
36
|
+
});
|
|
37
|
+
if (clone.status !== 0) {
|
|
38
|
+
await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
|
|
39
|
+
throw new Error(`Expected to be able to read ${(0, revokeSource_1.revokeRepoURL)(sourceURL)}.\n`
|
|
40
|
+
+ `${(clone.stdout + clone.stderr).trim()}`);
|
|
41
|
+
}
|
|
42
|
+
let revocations = [];
|
|
43
|
+
let directory = path_1.default.join(repoPath, REVOCATIONS_DIR);
|
|
44
|
+
try {
|
|
45
|
+
for (let name of (await promises_1.default.readdir(directory)).sort()) {
|
|
46
|
+
if (name.endsWith(".json")) {
|
|
47
|
+
revocations.push(JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8")));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
// No revocations directory at all just means nothing has ever been revoked.
|
|
53
|
+
}
|
|
54
|
+
await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
|
|
55
|
+
return revocations;
|
|
56
|
+
}
|
|
57
|
+
exports.readRemoteRevocations = readRemoteRevocations;
|
|
58
|
+
/** Which keys in a repo are revoked. What signfiles and securessh refuse over. */
|
|
59
|
+
async function revokedKeysInRepo(config) {
|
|
60
|
+
let { repoPath, sourceURL } = config;
|
|
61
|
+
let revocations = await readRemoteRevocations({ sourceURL });
|
|
62
|
+
let revokedFingerprints = new Set(revocations.map(revocation => revocation.fingerprint));
|
|
63
|
+
let unrevoked = new Set();
|
|
64
|
+
try {
|
|
65
|
+
let directory = path_1.default.join(repoPath, UNREVOKES_DIR);
|
|
66
|
+
for (let name of await promises_1.default.readdir(directory)) {
|
|
67
|
+
if (!name.endsWith(".json")) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
let parsed = JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8"));
|
|
71
|
+
for (let revocationId of parsed.revocationIds || []) {
|
|
72
|
+
unrevoked.add(revocationId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// Nothing has been unrevoked.
|
|
78
|
+
}
|
|
79
|
+
let stillRevoked = revocations.filter(revocation => !unrevoked.has(revocation.revocationId));
|
|
80
|
+
let keys = (0, authorizedKeys_1.normalizeKeys)(await promises_1.default.readFile(path_1.default.join(repoPath, "authorized_keys"), "utf8").catch(() => ""));
|
|
81
|
+
return stillRevoked
|
|
82
|
+
.filter(revocation => keys.some(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint))
|
|
83
|
+
.map(revocation => ({ revocation, key: keys.find(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint) || "" }));
|
|
84
|
+
}
|
|
85
|
+
exports.revokedKeysInRepo = revokedKeysInRepo;
|
|
86
|
+
/** The repo named, or the one we are standing in. Checked the same way securessh checks it: a git
|
|
87
|
+
repo that actually holds keys and has an origin to clone from. */
|
|
88
|
+
async function resolveKeysRepo(named) {
|
|
89
|
+
let cwd = named && (0, paths_1.expandHome)(named) || undefined;
|
|
90
|
+
let repoPath = (await (0, runPromise_1.runPromise)("git rev-parse --show-toplevel", { quiet: true, cwd })).trim();
|
|
91
|
+
if (!repoPath) {
|
|
92
|
+
throw new Error(`Expected ${named || "the current directory"} to be inside a git repo, it is not.\n${USAGE}`);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await (0, authorizedKeys_2.readRepoKeys)(repoPath);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
throw new Error(`Expected ${repoPath} to be a keys repo, it holds no keys.\n${e}\n${USAGE}`);
|
|
99
|
+
}
|
|
100
|
+
let originURL = (await (0, runPromise_1.runPromise)("git remote get-url origin", { quiet: true, cwd: repoPath })).trim();
|
|
101
|
+
if (!originURL) {
|
|
102
|
+
throw new Error(`Expected ${repoPath} to have an origin remote, it has none.\n${USAGE}`);
|
|
103
|
+
}
|
|
104
|
+
return { repoPath, originURL };
|
|
105
|
+
}
|
|
106
|
+
async function main() {
|
|
107
|
+
var _a;
|
|
108
|
+
let argv = process.argv.slice(2);
|
|
109
|
+
if (argv.length > 1) {
|
|
110
|
+
throw new Error(`Expected at most a keys repo, was ${argv.length} argument(s)\n${USAGE}`);
|
|
111
|
+
}
|
|
112
|
+
let { repoPath, originURL } = await resolveKeysRepo(argv[0]);
|
|
113
|
+
let revocations = await readRemoteRevocations({ sourceURL: originURL });
|
|
114
|
+
if (!revocations.length) {
|
|
115
|
+
console.log(`${(0, revokeSource_1.revokeRepoURL)(originURL)} lists no revocations, so there is nothing to undo.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let directory = path_1.default.join(repoPath, UNREVOKES_DIR);
|
|
119
|
+
await promises_1.default.mkdir(directory, { recursive: true });
|
|
120
|
+
let stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
121
|
+
let unrevokeId = `${stamp}-unrevoke`;
|
|
122
|
+
await promises_1.default.writeFile(path_1.default.join(directory, `${unrevokeId}.json`), JSON.stringify({
|
|
123
|
+
unrevokeId,
|
|
124
|
+
createdAt: new Date().toISOString(),
|
|
125
|
+
// Named one by one rather than as a blanket "allow everything again", so this file only
|
|
126
|
+
// ever undoes the revocations that existed when it was written.
|
|
127
|
+
revocationIds: revocations.map(revocation => revocation.revocationId),
|
|
128
|
+
revocations: revocations.map(revocation => ({
|
|
129
|
+
revocationId: revocation.revocationId,
|
|
130
|
+
fingerprint: revocation.fingerprint,
|
|
131
|
+
revokedAt: revocation.revokedAt,
|
|
132
|
+
revokedBy: revocation.revokedBy,
|
|
133
|
+
attempt: revocation.attempt,
|
|
134
|
+
})),
|
|
135
|
+
}, undefined, 4) + "\n");
|
|
136
|
+
console.log(`Wrote ${UNREVOKES_DIR}/${unrevokeId}.json covering ${revocations.length} revocation(s):`);
|
|
137
|
+
for (let revocation of revocations) {
|
|
138
|
+
console.log(` ${revocation.fingerprint} revoked by ${revocation.revokedBy || "?"} from ${((_a = revocation.attempt) === null || _a === void 0 ? void 0 : _a.ip) || "?"}`);
|
|
139
|
+
}
|
|
140
|
+
console.log(`Each machine waits an hour after seeing it before the keys work again.`);
|
|
141
|
+
console.log(`Sign and publish it with:\n yarn signfiles git`);
|
|
142
|
+
}
|
|
143
|
+
exports.main = main;
|
|
144
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5yZXZva2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ1bnJldm9rZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwrREFBNEQ7QUFDNUQscURBQWlFO0FBQ2pFLGlEQUErQztBQUMvQyxxREFBZ0Q7QUFDaEQsNENBQThDO0FBQzlDLDRDQUFnRDtBQUVoRCxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUM7QUFDbEMsTUFBTSxlQUFlLEdBQUcsYUFBYSxDQUFDO0FBQ3RDLE1BQU0sS0FBSyxHQUFHOzs7Ozs7OzBCQU9ZLENBQUM7QUFXM0I7O3FEQUVxRDtBQUM5QyxLQUFLLFVBQVUscUJBQXFCLENBQUMsTUFBNkI7SUFDckUsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUMzQixJQUFJLGtCQUFrQixHQUFHLE1BQU0sa0JBQUUsQ0FBQyxPQUFPLENBQUMsY0FBSSxDQUFDLElBQUksQ0FBQyxZQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUMvRSxJQUFJLFFBQVEsR0FBRyxjQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3JELElBQUksS0FBSyxHQUFHLE1BQU0sSUFBQSxvQkFBWSxFQUFDO1FBQzNCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsSUFBSSxFQUFFLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsSUFBQSw0QkFBYSxFQUFDLFNBQVMsQ0FBQyxFQUFFLFFBQVEsQ0FBQztLQUN0RSxDQUFDLENBQUM7SUFDSCxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDckIsTUFBTSxrQkFBRSxDQUFDLEVBQUUsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFD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QUFDLE1BQU0saUJBQWlCLENBQUMsQ0FBQztJQUN2RyxLQUFLLElBQUksVUFBVSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxVQUFVLENBQUMsV0FBVyxlQUFlLFVBQVUsQ0FBQyxTQUFTLElBQUksR0FBRyxTQUFTLENBQUEsTUFBQSxVQUFVLENBQUMsT0FBTywwQ0FBRSxFQUFFLEtBQUksR0FBRyxFQUFFLENBQUMsQ0FBQztJQUMvSCxDQUFDO0lBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3RUFBd0UsQ0FBQyxDQUFDO0lBQ3RGLE9BQU8sQ0FBQyxHQUFHLENBQUMsaURBQWlELENBQUMsQ0FBQztBQUNuRSxDQUFDO0FBdENELG9CQXNDQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBydW5Qcm9taXNlIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvcnVuUHJvbWlzZVwiO1xuaW1wb3J0IHsga2V5RmluZ2VycHJpbnQsIG5vcm1hbGl6ZUtleXMgfSBmcm9tIFwiLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgcmV2b2tlUmVwb1VSTCB9IGZyb20gXCIuL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgcmVhZFJlcG9LZXlzIH0gZnJvbSBcIi4vYXV0aG9yaXplZEtleXNcIjtcbmltcG9ydCB7IGV4cGFuZEhvbWUgfSBmcm9tIFwiLi4vaGVscGVycy9wYXRoc1wiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcblxuY29uc3QgVU5SRVZPS0VTX0RJUiA9IFwidW5yZXZva2VkXCI7XG5jb25zdCBSRVZPQ0FUSU9OU19ESVIgPSBcInJldm9jYXRpb25zXCI7XG5jb25zdCBVU0FHRSA9IGBVc2FnZTogeWFybiB1bnJldm9rZSBba2V5cy1yZXBvXVxuXG5SdW4gdGhpcyBpbiBhIGtleXMgcmVwbywgb3IgbmFtZSBvbmUuIEl0IHJlYWRzIHRoYXQgcmVwbydzIHJldm9rZSByZXBvIGFuZCB3cml0ZXMgb25lIHVucmV2b2tlIGZpbGVcbm5hbWluZyBldmVyeSByZXZvY2F0aW9uIGluIGl0LCBzbyB0aGUga2V5cyBhcmUgYWNjZXB0ZWQgYWdhaW4gb25jZSBlYWNoIG1hY2hpbmUncyBob3VyIGxvbmcgd2FpdFxucGFzc2VzLlxuXG5LZXlzIHRoYXQgd2VyZSByZXZva2VkIHNob3VsZCBub3JtYWxseSBiZSBkZWxldGVkIGZyb20gdGhlIHJlcG8gaW5zdGVhZC4gVW5yZXZva2luZyBvbmx5IG1hdHRlcnNcbmZvciBhIGtleSB5b3Ugc3RpbGwgd2FudC5gO1xuXG5leHBvcnQgdHlwZSBSZXZvY2F0aW9uID0ge1xuICAgIHJldm9jYXRpb25JZDogc3RyaW5nO1xuICAgIGZpbmdlcnByaW50OiBzdHJpbmc7XG4gICAga2V5Pzogc3RyaW5nO1xuICAgIHJldm9rZWRBdD86IHN0cmluZztcbiAgICByZXZva2VkQnk/OiBzdHJpbmc7XG4gICAgYXR0ZW1wdD86IHsgaXA/OiBzdHJpbmc7IHVzZXI/OiBzdHJpbmc7IHJlcXVpcmVkPzogc3RyaW5nIH07XG59O1xuXG4vKiogRXZlcnkgcmV2b2NhdGlvbiB0aGUgcmV2b2tlIHJlcG8gbGlzdHMuIENsb25lZCByZWFkIG9ubHkgaW50byBhIHRlbXAgZGlyZWN0b3J5LCB3aXRoIHdoYXRldmVyXG4gICAgY3JlZGVudGlhbHMgdGhpcyBtYWNoaW5lIGFscmVhZHkgaGFzIC0gdGhlIGRlcml2ZWQgZGVwbG95IGtleSBpcyBmb3Igc2VydmVycywgYW5kIGEgcGVyc29uXG4gICAgcnVubmluZyB0aGlzIGhhcyB0aGVpciBvd24gYWNjZXNzIHRvIHRoZSByZXBvLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRSZW1vdGVSZXZvY2F0aW9ucyhjb25maWc6IHsgc291cmNlVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHNvdXJjZVVSTCB9ID0gY29uZmlnO1xuICAgIGxldCB0ZW1wb3JhcnlEaXJlY3RvcnkgPSBhd2FpdCBmcy5ta2R0ZW1wKHBhdGguam9pbihvcy50bXBkaXIoKSwgXCJ1bnJldm9rZS1cIikpO1xuICAgIGxldCByZXBvUGF0aCA9IHBhdGguam9pbih0ZW1wb3JhcnlEaXJlY3RvcnksIFwicmVwb1wiKTtcbiAgICBsZXQgY2xvbmUgPSBhd2FpdCBzcGF3blByb21pc2Uoe1xuICAgICAgICBjb21tYW5kOiBcImdpdFwiLFxuICAgICAgICBhcmdzOiBbXCJjbG9uZVwiLCBcIi0tZGVwdGhcIiwgXCIxXCIsIHJldm9rZVJlcG9VUkwoc291cmNlVVJMKSwgcmVwb1BhdGhdLFxuICAgIH0pO1xuICAgIGlmIChjbG9uZS5zdGF0dXMgIT09IDApIHtcbiAgICAgICAgYXdhaXQgZnMucm0odGVtcG9yYXJ5RGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCB0byBiZSBhYmxlIHRvIHJlYWQgJHtyZXZva2VSZXBvVVJMKHNvdXJjZVVSTCl9LlxcbmBcbiAgICAgICAgICAgICsgYCR7KGNsb25lLnN0ZG91dCArIGNsb25lLnN0ZGVycikudHJpbSgpfWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IHJldm9jYXRpb25zOiBSZXZvY2F0aW9uW10gPSBbXTtcbiAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBSRVZPQ0FUSU9OU19ESVIpO1xuICAgIHRyeSB7XG4gICAgICAgIGZvciAobGV0IG5hbWUgb2YgKGF3YWl0IGZzLnJlYWRkaXIoZGlyZWN0b3J5KSkuc29ydCgpKSB7XG4gICAgICAgICAgICBpZiAobmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgcmV2b2NhdGlvbnMucHVzaChKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihkaXJlY3RvcnksIG5hbWUpLCBcInV0ZjhcIikpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gTm8gcmV2b2NhdGlvbnMgZGlyZWN0b3J5IGF0IGFsbCBqdXN0IG1lYW5zIG5vdGhpbmcgaGFzIGV2ZXIgYmVlbiByZXZva2VkLlxuICAgIH1cbiAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICByZXR1cm4gcmV2b2NhdGlvbnM7XG59XG5cbi8qKiBXaGljaCBrZXlzIGluIGEgcmVwbyBhcmUgcmV2b2tlZC4gV2hhdCBzaWduZmlsZXMgYW5kIHNlY3VyZXNzaCByZWZ1c2Ugb3Zlci4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXZva2VkS2V5c0luUmVwbyhjb25maWc6IHsgcmVwb1BhdGg6IHN0cmluZzsgc291cmNlVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfSA9IGNvbmZpZztcbiAgICBsZXQgcmV2b2NhdGlvbnMgPSBhd2FpdCByZWFkUmVtb3RlUmV2b2NhdGlvbnMoeyBzb3VyY2VVUkwgfSk7XG4gICAgbGV0IHJldm9rZWRGaW5nZXJwcmludHMgPSBuZXcgU2V0KHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+IHJldm9jYXRpb24uZmluZ2VycHJpbnQpKTtcbiAgICBsZXQgdW5yZXZva2VkID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgdHJ5IHtcbiAgICAgICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgVU5SRVZPS0VTX0RJUik7XG4gICAgICAgIGZvciAobGV0IG5hbWUgb2YgYXdhaXQgZnMucmVhZGRpcihkaXJlY3RvcnkpKSB7XG4gICAgICAgICAgICBpZiAoIW5hbWUuZW5kc1dpdGgoXCIuanNvblwiKSkge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGV0IHBhcnNlZCA9IEpTT04ucGFyc2UoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKGRpcmVjdG9yeSwgbmFtZSksIFwidXRmOFwiKSk7XG4gICAgICAgICAgICBmb3IgKGxldCByZXZvY2F0aW9uSWQgb2YgcGFyc2VkLnJldm9jYXRpb25JZHMgfHwgW10pIHtcbiAgICAgICAgICAgICAgICB1bnJldm9rZWQuYWRkKHJldm9jYXRpb25JZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIE5vdGhpbmcgaGFzIGJlZW4gdW5yZXZva2VkLlxuICAgIH1cbiAgICBsZXQgc3RpbGxSZXZva2VkID0gcmV2b2NhdGlvbnMuZmlsdGVyKHJldm9jYXRpb24gPT4gIXVucmV2b2tlZC5oYXMocmV2b2NhdGlvbi5yZXZvY2F0aW9uSWQpKTtcbiAgICBsZXQga2V5cyA9IG5vcm1hbGl6ZUtleXMoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKSwgXCJ1dGY4XCIpLmNhdGNoKCgpID0+IFwiXCIpKTtcbiAgICByZXR1cm4gc3RpbGxSZXZva2VkXG4gICAgICAgIC5maWx0ZXIocmV2b2NhdGlvbiA9PiBrZXlzLnNvbWUoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpKVxuICAgICAgICAubWFwKHJldm9jYXRpb24gPT4gKHsgcmV2b2NhdGlvbiwga2V5OiBrZXlzLmZpbmQoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpIHx8IFwiXCIgfSkpO1xufVxuXG4vKiogVGhlIHJlcG8gbmFtZWQsIG9yIHRoZSBvbmUgd2UgYXJlIHN0YW5kaW5nIGluLiBDaGVja2VkIHRoZSBzYW1lIHdheSBzZWN1cmVzc2ggY2hlY2tzIGl0OiBhIGdpdFxuICAgIHJlcG8gdGhhdCBhY3R1YWxseSBob2xkcyBrZXlzIGFuZCBoYXMgYW4gb3JpZ2luIHRvIGNsb25lIGZyb20uICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlS2V5c1JlcG8obmFtZWQ6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICAgIGxldCBjd2QgPSBuYW1lZCAmJiBleHBhbmRIb21lKG5hbWVkKSB8fCB1bmRlZmluZWQ7XG4gICAgbGV0IHJlcG9QYXRoID0gKGF3YWl0IHJ1blByb21pc2UoXCJnaXQgcmV2LXBhcnNlIC0tc2hvdy10b3BsZXZlbFwiLCB7IHF1aWV0OiB0cnVlLCBjd2QgfSkpLnRyaW0oKTtcbiAgICBpZiAoIXJlcG9QYXRoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtuYW1lZCB8fCBcInRoZSBjdXJyZW50IGRpcmVjdG9yeVwifSB0byBiZSBpbnNpZGUgYSBnaXQgcmVwbywgaXQgaXMgbm90LlxcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHJlYWRSZXBvS2V5cyhyZXBvUGF0aCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGJlIGEga2V5cyByZXBvLCBpdCBob2xkcyBubyBrZXlzLlxcbiR7ZX1cXG4ke1VTQUdFfWApO1xuICAgIH1cbiAgICBsZXQgb3JpZ2luVVJMID0gKGF3YWl0IHJ1blByb21pc2UoXCJnaXQgcmVtb3RlIGdldC11cmwgb3JpZ2luXCIsIHsgcXVpZXQ6IHRydWUsIGN3ZDogcmVwb1BhdGggfSkpLnRyaW0oKTtcbiAgICBpZiAoIW9yaWdpblVSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGhhdmUgYW4gb3JpZ2luIHJlbW90ZSwgaXQgaGFzIG5vbmUuXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFpbigpIHtcbiAgICBsZXQgYXJndiA9IHByb2Nlc3MuYXJndi5zbGljZSgyKTtcbiAgICBpZiAoYXJndi5sZW5ndGggPiAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXQgbW9zdCBhIGtleXMgcmVwbywgd2FzICR7YXJndi5sZW5ndGh9IGFyZ3VtZW50KHMpXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgbGV0IHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9ID0gYXdhaXQgcmVzb2x2ZUtleXNSZXBvKGFyZ3ZbMF0pO1xuXG4gICAgbGV0IHJldm9jYXRpb25zID0gYXdhaXQgcmVhZFJlbW90ZVJldm9jYXRpb25zKHsgc291cmNlVVJMOiBvcmlnaW5VUkwgfSk7XG4gICAgaWYgKCFyZXZvY2F0aW9ucy5sZW5ndGgpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCR7cmV2b2tlUmVwb1VSTChvcmlnaW5VUkwpfSBsaXN0cyBubyByZXZvY2F0aW9ucywgc28gdGhlcmUgaXMgbm90aGluZyB0byB1bmRvLmApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgVU5SRVZPS0VTX0RJUik7XG4gICAgYXdhaXQgZnMubWtkaXIoZGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBsZXQgc3RhbXAgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZSgvWzouXS9nLCBcIi1cIik7XG4gICAgbGV0IHVucmV2b2tlSWQgPSBgJHtzdGFtcH0tdW5yZXZva2VgO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBgJHt1bnJldm9rZUlkfS5qc29uYCksIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgdW5yZXZva2VJZCxcbiAgICAgICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgIC8vIE5hbWVkIG9uZSBieSBvbmUgcmF0aGVyIHRoYW4gYXMgYSBibGFua2V0IFwiYWxsb3cgZXZlcnl0aGluZyBhZ2FpblwiLCBzbyB0aGlzIGZpbGUgb25seVxuICAgICAgICAvLyBldmVyIHVuZG9lcyB0aGUgcmV2b2NhdGlvbnMgdGhhdCBleGlzdGVkIHdoZW4gaXQgd2FzIHdyaXR0ZW4uXG4gICAgICAgIHJldm9jYXRpb25JZHM6IHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+IHJldm9jYXRpb24ucmV2b2NhdGlvbklkKSxcbiAgICAgICAgcmV2b2NhdGlvbnM6IHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+ICh7XG4gICAgICAgICAgICByZXZvY2F0aW9uSWQ6IHJldm9jYXRpb24ucmV2b2NhdGlvbklkLFxuICAgICAgICAgICAgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24uZmluZ2VycHJpbnQsXG4gICAgICAgICAgICByZXZva2VkQXQ6IHJldm9jYXRpb24ucmV2b2tlZEF0LFxuICAgICAgICAgICAgcmV2b2tlZEJ5OiByZXZvY2F0aW9uLnJldm9rZWRCeSxcbiAgICAgICAgICAgIGF0dGVtcHQ6IHJldm9jYXRpb24uYXR0ZW1wdCxcbiAgICAgICAgfSkpLFxuICAgIH0sIHVuZGVmaW5lZCwgNCkgKyBcIlxcblwiKTtcblxuICAgIGNvbnNvbGUubG9nKGBXcm90ZSAke1VOUkVWT0tFU19ESVJ9LyR7dW5yZXZva2VJZH0uanNvbiBjb3ZlcmluZyAke3Jldm9jYXRpb25zLmxlbmd0aH0gcmV2b2NhdGlvbihzKTpgKTtcbiAgICBmb3IgKGxldCByZXZvY2F0aW9uIG9mIHJldm9jYXRpb25zKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGAgICR7cmV2b2NhdGlvbi5maW5nZXJwcmludH0gcmV2b2tlZCBieSAke3Jldm9jYXRpb24ucmV2b2tlZEJ5IHx8IFwiP1wifSBmcm9tICR7cmV2b2NhdGlvbi5hdHRlbXB0Py5pcCB8fCBcIj9cIn1gKTtcbiAgICB9XG4gICAgY29uc29sZS5sb2coYEVhY2ggbWFjaGluZSB3YWl0cyBhbiBob3VyIGFmdGVyIHNlZWluZyBpdCBiZWZvcmUgdGhlIGtleXMgd29yayBhZ2Fpbi5gKTtcbiAgICBjb25zb2xlLmxvZyhgU2lnbiBhbmQgcHVibGlzaCBpdCB3aXRoOlxcbiAgeWFybiBzaWduZmlsZXMgZ2l0YCk7XG59XG4iXX0=
|
|
145
|
+
/* _JS_SOURCE_HASH = "1f43574c62196a1eb6f7594da6497ffb17128b3c8914274f810f020ee0cc4224"; */
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { deriveEd25519Key } from "../keys/deriveKey";
|
|
2
|
+
import { formatOpenSSHPrivateKey, parseOpenSSHPrivateKey } from "../keys/sshKeyFile";
|
|
3
|
+
import { REPO_KEYS_DIR, REPOS_DIR, sourceName } from "./sources";
|
|
4
|
+
|
|
5
|
+
// A source's revocations live in a repo beside it. Derived rather than configured, so adding a
|
|
6
|
+
// source brings its revoke repo with it and there is nothing extra to pass on the command line.
|
|
7
|
+
const REVOKED_SUFFIX = "_revoked";
|
|
8
|
+
// GitHub will not take the same public key as a deploy key on two repos, so the revoke repo is
|
|
9
|
+
// reached with a key derived from the source's, under this label.
|
|
10
|
+
export const REVOKE_KEY_LABEL = "revokegithubkey";
|
|
11
|
+
|
|
12
|
+
export function revokeRepoURL(sourceURL: string) {
|
|
13
|
+
let withoutGit = sourceURL.replace(/\.git$/, "");
|
|
14
|
+
return `${withoutGit}${REVOKED_SUFFIX}.git`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function revokeKeyPath(sourceURL: string) {
|
|
18
|
+
return `${REPO_KEYS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function revokeRepoPath(sourceURL: string) {
|
|
22
|
+
return `${REPOS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The revoke repo's key, worked out from the source repo's key. Anything holding the source key
|
|
26
|
+
can produce it, so it never has to be stored anywhere separately or handed around. */
|
|
27
|
+
export function deriveRevokeKey(sourcePrivateKey: string) {
|
|
28
|
+
let source = parseOpenSSHPrivateKey(sourcePrivateKey);
|
|
29
|
+
let derived = deriveEd25519Key({ seed: source.seed, label: REVOKE_KEY_LABEL });
|
|
30
|
+
return {
|
|
31
|
+
publicKey: `ssh-ed25519 ${Buffer.concat([
|
|
32
|
+
Buffer.from([0, 0, 0, 11]), Buffer.from("ssh-ed25519"),
|
|
33
|
+
Buffer.from([0, 0, 0, 32]), derived.publicKey,
|
|
34
|
+
]).toString("base64")}`,
|
|
35
|
+
privateKeyFile: formatOpenSSHPrivateKey({
|
|
36
|
+
seed: derived.seed,
|
|
37
|
+
comment: `${source.comment || "portsecure"} ${REVOKE_KEY_LABEL}`,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|