sliftutils 1.7.126 → 1.7.128
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/bin/testnotify.js +9 -0
- package/package.json +5 -3
- package/security/authorizedKeys/authorizedKeys.ts +49 -9
- package/security/authorizedKeys/daemon/authLog.ts +74 -5
- package/security/authorizedKeys/daemon/changes.ts +26 -0
- package/security/authorizedKeys/daemon/daemon.ts +58 -30
- package/security/authorizedKeys/daemon/git.ts +3 -3
- package/security/authorizedKeys/daemon/revocation.ts +60 -31
- package/security/authorizedKeys/daemon/rootKeys.ts +88 -25
- package/security/authorizedKeys/daemon/sessions.ts +139 -0
- package/security/authorizedKeys/daemon/state.ts +16 -2
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +49 -11
- package/security/authorizedKeys/dist/secureSSH.ts.cache +18 -31
- package/security/authorizedKeys/dist/unrevoke.ts.cache +36 -14
- package/security/authorizedKeys/revokeSource.ts +12 -2
- package/security/authorizedKeys/secureSSH.ts +116 -64
- package/security/authorizedKeys/sources.ts +47 -5
- package/security/authorizedKeys/unrevoke.ts +55 -20
- package/security/helpers/remoteSSH.ts +17 -6
- package/security/notifications/discord.ts +18 -1
- package/security/notifications/dist/discord.ts.cache +21 -4
- package/security/notifications/setupNotify.ts +17 -8
- package/security/notifications/testNotify.ts +48 -0
- package/security/signedFiles/dist/manifest.ts.cache +21 -4
- package/security/signedFiles/dist/signFiles.ts.cache +48 -13
- package/security/signedFiles/manifest.ts +19 -2
- package/security/signedFiles/signFiles.ts +51 -12
|
@@ -3,8 +3,10 @@ import os from "os";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { runPromise } from "socket-function/src/runPromise";
|
|
5
5
|
import { expandHome } from "../helpers/paths";
|
|
6
|
+
import { spawnPromise } from "../helpers/spawn";
|
|
6
7
|
import { buildManifest, formatManifest, MANIFEST_NAME, SIGNATURE_NAME, SIGN_NAMESPACE } from "./manifest";
|
|
7
8
|
import { revokedKeysInRepo } from "../authorizedKeys/unrevoke";
|
|
9
|
+
import { findKeyProblems, normalizeKeys } from "../authorizedKeys/authorizedKeys";
|
|
8
10
|
|
|
9
11
|
// A hardware backed key is the entire point. A key sitting on disk is compromised the moment the
|
|
10
12
|
// machine is, and then the signature proves nothing, so this is what we make when asked to make one.
|
|
@@ -58,6 +60,27 @@ async function publicKeyOf(keyPath: string) {
|
|
|
58
60
|
return `${keyType} ${keyBody}`;
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
/** A keys repo has rules the rest of this cannot enforce afterwards, so they are enforced before it
|
|
64
|
+
is signed. Signing anything that is not a keys repo is none of this function's business. */
|
|
65
|
+
async function refuseUnusableKeys(repoPath: string) {
|
|
66
|
+
let authorizedPath = path.join(repoPath, "authorized_keys");
|
|
67
|
+
if (!await pathExists(authorizedPath)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let problems = findKeyProblems(normalizeKeys(await fs.readFile(authorizedPath, "utf8")));
|
|
71
|
+
if (!problems.length) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Expected every key in ${authorizedPath} to be restricted and to belong to one person,`
|
|
76
|
+
+ ` found ${problems.length} problem(s):\n`
|
|
77
|
+
+ problems.map(problem => ` - ${problem}`).join("\n")
|
|
78
|
+
+ `\n\nEvery key needs a from= naming the addresses it may be used from, and no two keys may`
|
|
79
|
+
+ `\nname the same ones. Revoking a key that shares its addresses with another leaves that`
|
|
80
|
+
+ `\nother key working, which defeats the point of revoking it.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
/** Signing a keys repo that still holds a revoked key would publish it back to every machine that
|
|
62
85
|
took it out. Only applies to a repo that holds keys and has a revoke repo to check - signing
|
|
63
86
|
anything else is none of this function's business. */
|
|
@@ -65,8 +88,10 @@ async function refuseRevokedKeys(repoPath: string) {
|
|
|
65
88
|
if (!await pathExists(path.join(repoPath, "authorized_keys"))) {
|
|
66
89
|
return;
|
|
67
90
|
}
|
|
68
|
-
|
|
69
|
-
|
|
91
|
+
// Read for its value, so stdout has to be on its own. runPromise joins it with stderr.
|
|
92
|
+
let origin = await spawnPromise({ command: "git", args: ["remote", "get-url", "origin"], cwd: repoPath });
|
|
93
|
+
let originURL = origin.stdout.trim();
|
|
94
|
+
if (origin.status !== 0 || !originURL) {
|
|
70
95
|
return;
|
|
71
96
|
}
|
|
72
97
|
let revoked;
|
|
@@ -96,19 +121,16 @@ function parseArgs(argv: string[]) {
|
|
|
96
121
|
return { keyPath: positional[0], pushToGit };
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
let repoPath =
|
|
103
|
-
|
|
104
|
-
throw new Error(`Expected the current directory to be inside a git repo, it is not.\n${USAGE}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let signingKey = keyPath && expandHome(keyPath) || await ensureDefaultKey();
|
|
124
|
+
/** Writes the manifest and its signature into a repo. Separate from the command so anything that
|
|
125
|
+
changes a keys repo can leave it signed, rather than telling someone to go and do it. */
|
|
126
|
+
export async function signRepo(config: { repoPath: string; keyPath?: string }) {
|
|
127
|
+
let { repoPath } = config;
|
|
128
|
+
let signingKey = config.keyPath && expandHome(config.keyPath) || await ensureDefaultKey();
|
|
108
129
|
if (!await pathExists(signingKey)) {
|
|
109
130
|
throw new Error(`Expected a signing key at ${signingKey}, no such file exists`);
|
|
110
131
|
}
|
|
111
132
|
|
|
133
|
+
await refuseUnusableKeys(repoPath);
|
|
112
134
|
await refuseRevokedKeys(repoPath);
|
|
113
135
|
|
|
114
136
|
let manifest = await buildManifest(repoPath);
|
|
@@ -132,6 +154,22 @@ export async function main() {
|
|
|
132
154
|
await fs.copyFile(stagedSignature, path.join(repoPath, SIGNATURE_NAME));
|
|
133
155
|
await fs.rm(stagingDirectory, { recursive: true, force: true });
|
|
134
156
|
console.log(`Signed with ${await publicKeyOf(signingKey)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function main() {
|
|
160
|
+
let { keyPath, pushToGit } = parseArgs(process.argv.slice(2));
|
|
161
|
+
|
|
162
|
+
// Read for its value, so stdout has to be on its own. runPromise joins it with stderr, and a
|
|
163
|
+
// git warning glued to the front of this becomes a directory that does not exist.
|
|
164
|
+
let topLevel = await spawnPromise({ command: "git", args: ["rev-parse", "--show-toplevel"] });
|
|
165
|
+
let repoPath = topLevel.stdout.trim();
|
|
166
|
+
if (topLevel.status !== 0 || !repoPath) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Expected the current directory to be inside a git repo, it is not.\n`
|
|
169
|
+
+ `${(topLevel.stdout + topLevel.stderr).trim()}\n${USAGE}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
await signRepo({ repoPath, keyPath });
|
|
135
173
|
|
|
136
174
|
if (!pushToGit) {
|
|
137
175
|
console.log(`Commit and push ${MANIFEST_NAME} and ${SIGNATURE_NAME} for anything to see them.`);
|
|
@@ -140,7 +178,8 @@ export async function main() {
|
|
|
140
178
|
await runPromise(`git add -A`, { cwd: repoPath });
|
|
141
179
|
// Nothing staged is not worth stopping on. git commit calls that a failure, but it only means
|
|
142
180
|
// the signature matches the one already committed, so there is nothing to deploy.
|
|
143
|
-
let
|
|
181
|
+
let status = await spawnPromise({ command: "git", args: ["status", "--porcelain"], cwd: repoPath });
|
|
182
|
+
let staged = status.stdout;
|
|
144
183
|
if (!staged.trim()) {
|
|
145
184
|
console.log(`Nothing changed, ${MANIFEST_NAME} and ${SIGNATURE_NAME} are already committed.`);
|
|
146
185
|
return;
|