credvault 0.14.1 → 0.15.0
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/README.md +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -0
- package/dist/gate/plan.d.ts +48 -0
- package/dist/gate/plan.js +151 -0
- package/dist/gate/plan.js.map +1 -0
- package/dist/gate/run.d.ts +42 -0
- package/dist/gate/run.js +170 -0
- package/dist/gate/run.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/gate/addon.py +85 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ npm install credvault
|
|
|
38
38
|
| `projectKeys(keys)`, `applyProjection(store, keys)`, `projectionDiff(store, keys)` | The env view of the keys, one way. Per vendor: `<BASE>_API_KEY` from the `default` key, `<BASE>_API_KEY_1..n` from the rest in ring order, `NUM_<BASE>` = n. `applyProjection` writes only names that differ and removes stale ones. `projectionDiff` lists what would change as ids, never values: empty means env equals the vault. `parseVendorKeyName` and `isProjectedName` tell a projected name from any other. |
|
|
39
39
|
| `keyUses(uses)` | From the audit: per key id, the count of allowed uses and the time of the last. A key use is a `SecretUse` with `keyId`, `owner` and `reason` set. |
|
|
40
40
|
| `keysFromEnv(entries, { owner, madeAt })` | The migration piece. Each `<BASE>_API_KEY` is account `env-0` with purpose `default`, each `<BASE>_API_KEY_<n>` is `env-<n>`. A name above `NUM_<BASE>`, or a second name with the same value, is skipped and listed with its reason. Pure. |
|
|
41
|
+
| `credvault gate --config <file>`, `gate(config)` | The key gate. Each role's processes hold sentinels (`wsk_…`, `sentinel(gateKey, role, name)`, stable) in `<dir>/<role>.env`, never keys. mitmproxy (pinned, upstream mode to the role's egress proxy) ends TLS only on keyed vendor hosts (`VENDOR_HOSTS`, or `hosts` in the config) and swaps a sentinel in a header, the query or the body for the real key. A sentinel on another role or host: 403 and an alert. Every swap and refusal is a `fileAudit` line with the `keyId`. Clients trust `<dir>/ca.pem` (`NODE_EXTRA_CA_CERTS`) or `<dir>/ca-bundle.pem` (`SSL_CERT_FILE`). Design: `designs/2026-11-08-gate.md`. |
|
|
41
42
|
|
|
42
43
|
## Example
|
|
43
44
|
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** `credvault gate --config <file>`: runs the key gate until stopped. */
|
|
3
|
+
import { gate, loadGateConfig } from "./gate/run.js";
|
|
4
|
+
const USAGE = "usage: credvault gate --config <file>";
|
|
5
|
+
async function main(argv) {
|
|
6
|
+
const [cmd, ...rest] = argv;
|
|
7
|
+
if (cmd !== "gate") {
|
|
8
|
+
console.error(USAGE);
|
|
9
|
+
return 2;
|
|
10
|
+
}
|
|
11
|
+
const i = rest.indexOf("--config");
|
|
12
|
+
const path = i >= 0 ? rest[i + 1] : undefined;
|
|
13
|
+
if (!path) {
|
|
14
|
+
console.error(USAGE);
|
|
15
|
+
return 2;
|
|
16
|
+
}
|
|
17
|
+
const g = await gate(await loadGateConfig(path));
|
|
18
|
+
const roles = [...g.plan.files.keys()].join(", ");
|
|
19
|
+
console.error(`credvault gate: up; roles ${roles}; ${g.plan.keys.size} keys on ${g.plan.hosts.length} hosts`);
|
|
20
|
+
for (const sig of ["SIGTERM", "SIGINT"])
|
|
21
|
+
process.on(sig, () => g.stop());
|
|
22
|
+
return g.done;
|
|
23
|
+
}
|
|
24
|
+
main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
25
|
+
console.error(`credvault gate: ${err.message}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,yEAAyE;AACzE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,KAAK,GAAG,uCAAuC,CAAC;AAEtD,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CACX,6BAA6B,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAC/F,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAClF,OAAO,CAAC,CAAC,IAAI,CAAC;AAChB,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;IACN,OAAO,CAAC,KAAK,CAAC,mBAAoB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type EnvEntry } from "../env-store.js";
|
|
3
|
+
/** Where each vendor's keys may go. A key whose vendor is not here needs a `hosts` entry in the config. */
|
|
4
|
+
export declare const VENDOR_HOSTS: Readonly<Record<string, readonly string[]>>;
|
|
5
|
+
/** `wsk_` + 28 base32 characters: what a process holds in place of a key. */
|
|
6
|
+
export declare const SENTINEL: RegExp;
|
|
7
|
+
export declare const gateConfigSchema: z.ZodObject<{
|
|
8
|
+
dir: z.ZodString;
|
|
9
|
+
mitmdump: z.ZodString;
|
|
10
|
+
audit: z.ZodString;
|
|
11
|
+
keychain: z.ZodDefault<z.ZodObject<{
|
|
12
|
+
service: z.ZodString;
|
|
13
|
+
account: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, z.core.$strip>>;
|
|
15
|
+
sources: z.ZodArray<z.ZodString>;
|
|
16
|
+
hosts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
17
|
+
roles: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
18
|
+
port: z.ZodNumber;
|
|
19
|
+
upstream: z.ZodString;
|
|
20
|
+
keys: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
21
|
+
plain: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
22
|
+
}, z.core.$strip>>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type GateConfig = z.infer<typeof gateConfigSchema>;
|
|
25
|
+
export interface GateKey {
|
|
26
|
+
role: string;
|
|
27
|
+
name: string;
|
|
28
|
+
value: string;
|
|
29
|
+
hosts: string[];
|
|
30
|
+
/** The vendor, or the first host for a key with none. */
|
|
31
|
+
site: string;
|
|
32
|
+
keyId: string;
|
|
33
|
+
}
|
|
34
|
+
export interface GatePlan {
|
|
35
|
+
/** Per role: the env file's entries, sentinels in place of keys. */
|
|
36
|
+
files: Map<string, EnvEntry[]>;
|
|
37
|
+
/** Sentinel → the real key. Only the gate and its addon hold this. */
|
|
38
|
+
keys: Map<string, GateKey>;
|
|
39
|
+
/** Every host a sentinel may go to: the proxy ends TLS on these only. */
|
|
40
|
+
hosts: string[];
|
|
41
|
+
}
|
|
42
|
+
/** Stable per gate key, role and name: a restart or a rotated value keeps the file as it is. */
|
|
43
|
+
export declare function sentinel(gateKey: Buffer, role: string, name: string): string;
|
|
44
|
+
/** Layers the sources: an earlier file wins a name. */
|
|
45
|
+
export declare function layer(sources: EnvEntry[][]): Map<string, string>;
|
|
46
|
+
export declare function planGate(config: GateConfig, values: Map<string, string>, gateKey: Buffer): GatePlan;
|
|
47
|
+
/** mitmproxy's `allow_hosts`: TLS is ended on these hosts only; the rest pass as opaque tunnels. */
|
|
48
|
+
export declare function allowHostsRegex(hosts: string[]): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key gate's plan: which names each role holds, as stand-ins (sentinels)
|
|
3
|
+
* or as plain values, and the sentinel → real key map the proxy addon swaps
|
|
4
|
+
* from. Pure: the gate key and the source entries come in, nothing is read here.
|
|
5
|
+
*/
|
|
6
|
+
import { createHmac } from "node:crypto";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { ENV_KEY } from "../env-store.js";
|
|
9
|
+
import { keyId, parseVendorKeyName } from "../keys.js";
|
|
10
|
+
/** Where each vendor's keys may go. A key whose vendor is not here needs a `hosts` entry in the config. */
|
|
11
|
+
export const VENDOR_HOSTS = {
|
|
12
|
+
anthropic: ["api.anthropic.com"],
|
|
13
|
+
cerebras: ["api.cerebras.ai"],
|
|
14
|
+
cohere: ["api.cohere.com", "api.cohere.ai"],
|
|
15
|
+
exa: ["api.exa.ai"],
|
|
16
|
+
gemini: ["generativelanguage.googleapis.com"],
|
|
17
|
+
groq: ["api.groq.com"],
|
|
18
|
+
mistral: ["api.mistral.ai"],
|
|
19
|
+
openai: ["api.openai.com"],
|
|
20
|
+
openalex: ["api.openalex.org"],
|
|
21
|
+
openrouter: ["openrouter.ai"],
|
|
22
|
+
perplexity: ["api.perplexity.ai"],
|
|
23
|
+
typesafe: ["api.typesafe.ai"],
|
|
24
|
+
youtube: ["www.googleapis.com", "youtube.googleapis.com"],
|
|
25
|
+
};
|
|
26
|
+
/** `wsk_` + 28 base32 characters: what a process holds in place of a key. */
|
|
27
|
+
export const SENTINEL = /wsk_[a-z2-7]{28}/g;
|
|
28
|
+
const HOST = /^(\*\.)?[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
|
|
29
|
+
/** A name, or a name with `*` in it (`EXA_API_KEY*`). */
|
|
30
|
+
const NAME_GLOB = /^[A-Z*][A-Z0-9_*]*$/;
|
|
31
|
+
const roleSchema = z.object({
|
|
32
|
+
/** The port this role's processes send through (`HTTPS_PROXY`). */
|
|
33
|
+
port: z.number().int().min(1024).max(65535),
|
|
34
|
+
/** The role's own egress proxy (smokescreen): every request leaves through it. */
|
|
35
|
+
upstream: z.string().url(),
|
|
36
|
+
/** Names the role holds as sentinels. */
|
|
37
|
+
keys: z.array(z.string().regex(NAME_GLOB)).default([]),
|
|
38
|
+
/** Names the role holds as they are (`NUM_EXA`, a URL). Never a key. */
|
|
39
|
+
plain: z.array(z.string().regex(NAME_GLOB)).default([]),
|
|
40
|
+
});
|
|
41
|
+
export const gateConfigSchema = z.object({
|
|
42
|
+
/** Where the role env files, the CA and its bundle go. 0700. */
|
|
43
|
+
dir: z.string().min(1),
|
|
44
|
+
/** The `mitmdump` binary (a pinned venv). */
|
|
45
|
+
mitmdump: z.string().min(1),
|
|
46
|
+
/** The hash-chained audit file (`fileAudit`). */
|
|
47
|
+
audit: z.string().min(1),
|
|
48
|
+
/** The Keychain item that holds the gate key: sentinels are an HMAC of it. */
|
|
49
|
+
keychain: z.object({ service: z.string().min(1), account: z.string().optional() }).default({
|
|
50
|
+
service: "credvault-gate",
|
|
51
|
+
}),
|
|
52
|
+
/** Env files with the real values. An earlier file wins a name. */
|
|
53
|
+
sources: z.array(z.string().min(1)).min(1),
|
|
54
|
+
/** Hosts per name or glob, for a key with no vendor (`RESTATE_AUTH_TOKEN`), or to replace the vendor's list. */
|
|
55
|
+
hosts: z.record(z.string().regex(NAME_GLOB), z.array(z.string().regex(HOST)).min(1)).default({}),
|
|
56
|
+
roles: z.record(z.string().regex(/^[a-z][a-z0-9-]{0,31}$/), roleSchema),
|
|
57
|
+
});
|
|
58
|
+
const glob = (pattern) => new RegExp(`^${pattern.replace(/\*/g, "[A-Z0-9_]*")}$`);
|
|
59
|
+
const matchesAny = (name, patterns) => patterns.some((p) => glob(p).test(name));
|
|
60
|
+
const BASE32 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
61
|
+
function base32(buf) {
|
|
62
|
+
let bits = 0;
|
|
63
|
+
let acc = 0;
|
|
64
|
+
let out = "";
|
|
65
|
+
for (const byte of buf) {
|
|
66
|
+
acc = (acc << 8) | byte;
|
|
67
|
+
bits += 8;
|
|
68
|
+
while (bits >= 5) {
|
|
69
|
+
out += BASE32[(acc >>> (bits - 5)) & 31];
|
|
70
|
+
bits -= 5;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/** Stable per gate key, role and name: a restart or a rotated value keeps the file as it is. */
|
|
76
|
+
export function sentinel(gateKey, role, name) {
|
|
77
|
+
const mac = createHmac("sha256", gateKey).update(`${role}/${name}`).digest();
|
|
78
|
+
return `wsk_${base32(mac).slice(0, 28)}`;
|
|
79
|
+
}
|
|
80
|
+
function hostsFor(name, overrides) {
|
|
81
|
+
const exact = overrides[name];
|
|
82
|
+
if (exact)
|
|
83
|
+
return exact;
|
|
84
|
+
const byGlob = Object.entries(overrides).find(([p]) => p.includes("*") && glob(p).test(name));
|
|
85
|
+
if (byGlob)
|
|
86
|
+
return byGlob[1];
|
|
87
|
+
const vendor = parseVendorKeyName(name)?.vendor;
|
|
88
|
+
const listed = vendor ? VENDOR_HOSTS[vendor] : undefined;
|
|
89
|
+
return listed ? [...listed] : null;
|
|
90
|
+
}
|
|
91
|
+
/** Layers the sources: an earlier file wins a name. */
|
|
92
|
+
export function layer(sources) {
|
|
93
|
+
const out = new Map();
|
|
94
|
+
for (const entries of sources)
|
|
95
|
+
for (const { name, value } of entries)
|
|
96
|
+
if (!out.has(name))
|
|
97
|
+
out.set(name, value);
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
export function planGate(config, values, gateKey) {
|
|
101
|
+
const files = new Map();
|
|
102
|
+
const keys = new Map();
|
|
103
|
+
const hosts = new Set();
|
|
104
|
+
const ports = new Set();
|
|
105
|
+
for (const [role, r] of Object.entries(config.roles)) {
|
|
106
|
+
if (ports.has(r.port))
|
|
107
|
+
throw new Error(`role ${role}: port ${r.port} is taken by another role`);
|
|
108
|
+
ports.add(r.port);
|
|
109
|
+
const entries = [];
|
|
110
|
+
for (const name of [...values.keys()].sort()) {
|
|
111
|
+
const value = values.get(name);
|
|
112
|
+
if (!ENV_KEY.test(name))
|
|
113
|
+
continue;
|
|
114
|
+
if (matchesAny(name, r.keys)) {
|
|
115
|
+
const to = hostsFor(name, config.hosts);
|
|
116
|
+
if (!to)
|
|
117
|
+
throw new Error(`role ${role}: ${name} has no hosts; add it to "hosts"`);
|
|
118
|
+
const s = sentinel(gateKey, role, name);
|
|
119
|
+
const vendor = parseVendorKeyName(name)?.vendor;
|
|
120
|
+
keys.set(s, {
|
|
121
|
+
role,
|
|
122
|
+
name,
|
|
123
|
+
value,
|
|
124
|
+
hosts: to,
|
|
125
|
+
site: vendor ?? to[0],
|
|
126
|
+
keyId: keyId(value),
|
|
127
|
+
});
|
|
128
|
+
for (const h of to)
|
|
129
|
+
hosts.add(h);
|
|
130
|
+
entries.push({ name, value: s });
|
|
131
|
+
}
|
|
132
|
+
else if (matchesAny(name, r.plain)) {
|
|
133
|
+
if (hostsFor(name, config.hosts) && parseVendorKeyName(name))
|
|
134
|
+
throw new Error(`role ${role}: ${name} is a key; list it in "keys", not "plain"`);
|
|
135
|
+
entries.push({ name, value });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
files.set(role, entries);
|
|
139
|
+
}
|
|
140
|
+
return { files, keys, hosts: [...hosts].sort() };
|
|
141
|
+
}
|
|
142
|
+
/** mitmproxy's `allow_hosts`: TLS is ended on these hosts only; the rest pass as opaque tunnels. */
|
|
143
|
+
export function allowHostsRegex(hosts) {
|
|
144
|
+
const alt = hosts
|
|
145
|
+
.map((h) => h.startsWith("*.")
|
|
146
|
+
? `(?:[a-z0-9-]+\\.)+${h.slice(2).replace(/\./g, "\\.")}`
|
|
147
|
+
: h.replace(/\./g, "\\."))
|
|
148
|
+
.join("|");
|
|
149
|
+
return `^(?:${alt})(?::\\d+)?$`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/gate/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key gate process: plans from the sources, writes each role's env file
|
|
3
|
+
* (sentinels), starts mitmdump with one listener per keyed role, hands the map
|
|
4
|
+
* to the addon over fd 3 and audits each swap or refusal it reports on fd 4.
|
|
5
|
+
* Real values live in this process and the addon only.
|
|
6
|
+
*/
|
|
7
|
+
import { type ChildProcess } from "node:child_process";
|
|
8
|
+
import { type SecretAudit } from "../audit.js";
|
|
9
|
+
import { type GateConfig, type GatePlan } from "./plan.js";
|
|
10
|
+
export interface GateEvent {
|
|
11
|
+
t: "use" | "deny";
|
|
12
|
+
role: string | null;
|
|
13
|
+
host: string;
|
|
14
|
+
path: string;
|
|
15
|
+
/** The key's name; none for an unknown sentinel. */
|
|
16
|
+
name?: string | null;
|
|
17
|
+
/** The role the key belongs to (differs from `role` on a refusal). */
|
|
18
|
+
key_role?: string | null;
|
|
19
|
+
why?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function loadGateConfig(path: string): Promise<GateConfig>;
|
|
22
|
+
export declare function readSources(paths: string[]): Promise<Map<string, string>>;
|
|
23
|
+
/** `<dir>/<role>.env` per role, 0600. */
|
|
24
|
+
export declare function writeRoleFiles(dir: string, plan: GatePlan): Promise<string[]>;
|
|
25
|
+
/** The audit line for one addon event. No value, no query. */
|
|
26
|
+
export declare function auditLine(e: GateEvent, plan: GatePlan): Parameters<SecretAudit["record"]>[0];
|
|
27
|
+
export interface RunningGate {
|
|
28
|
+
child: ChildProcess;
|
|
29
|
+
plan: GatePlan;
|
|
30
|
+
/** Resolves with the child's exit code. */
|
|
31
|
+
done: Promise<number>;
|
|
32
|
+
stop(): void;
|
|
33
|
+
}
|
|
34
|
+
export interface GateOptions {
|
|
35
|
+
/** Defaults to the config's Keychain item. */
|
|
36
|
+
gateKey?: Buffer;
|
|
37
|
+
audit?: SecretAudit;
|
|
38
|
+
/** A refused sentinel: a canary. Default: one stderr line. */
|
|
39
|
+
onDeny?: (e: GateEvent) => void;
|
|
40
|
+
}
|
|
41
|
+
/** Writes the role files and the CA bundle, starts mitmdump; returns once it runs. */
|
|
42
|
+
export declare function gate(config: GateConfig, o?: GateOptions): Promise<RunningGate>;
|
package/dist/gate/run.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key gate process: plans from the sources, writes each role's env file
|
|
3
|
+
* (sentinels), starts mitmdump with one listener per keyed role, hands the map
|
|
4
|
+
* to the addon over fd 3 and audits each swap or refusal it reports on fd 4.
|
|
5
|
+
* Real values live in this process and the addon only.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
10
|
+
import { connect } from "node:net";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { createInterface } from "node:readline";
|
|
13
|
+
import { fileAudit } from "../audit.js";
|
|
14
|
+
import { keychainKey } from "../cipher.js";
|
|
15
|
+
import { parseDotenv, toDotenv } from "../env-store.js";
|
|
16
|
+
import { allowHostsRegex, gateConfigSchema, layer, planGate, } from "./plan.js";
|
|
17
|
+
/** Shipped beside dist: `<package>/gate/addon.py`. */
|
|
18
|
+
const ADDON = new URL("../../gate/addon.py", import.meta.url).pathname;
|
|
19
|
+
const CA_CERT = "mitmproxy-ca-cert.pem";
|
|
20
|
+
/** macOS's roots; Python and curl read `SSL_CERT_FILE` as the whole store, so the bundle adds the gate CA to them. */
|
|
21
|
+
const SYSTEM_ROOTS = "/etc/ssl/cert.pem";
|
|
22
|
+
async function writePrivate(path, text) {
|
|
23
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
24
|
+
await writeFile(tmp, text, { mode: 0o600 });
|
|
25
|
+
await rename(tmp, path);
|
|
26
|
+
}
|
|
27
|
+
export async function loadGateConfig(path) {
|
|
28
|
+
return gateConfigSchema.parse(JSON.parse(await readFile(path, "utf8")));
|
|
29
|
+
}
|
|
30
|
+
export async function readSources(paths) {
|
|
31
|
+
const read = (p) => (existsSync(p) ? readFileSync(p, "utf8") : null);
|
|
32
|
+
return layer(await Promise.all(paths.map(async (p) => {
|
|
33
|
+
if (!existsSync(p))
|
|
34
|
+
throw new Error(`gate source ${p} is missing`);
|
|
35
|
+
return parseDotenv(await readFile(p, "utf8"), read);
|
|
36
|
+
})));
|
|
37
|
+
}
|
|
38
|
+
/** `<dir>/<role>.env` per role, 0600. */
|
|
39
|
+
export async function writeRoleFiles(dir, plan) {
|
|
40
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
41
|
+
await chmod(dir, 0o700);
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const [role, entries] of plan.files) {
|
|
44
|
+
const path = join(dir, `${role}.env`);
|
|
45
|
+
await writePrivate(path, toDotenv(entries));
|
|
46
|
+
out.push(path);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** The audit line for one addon event. No value, no query. */
|
|
51
|
+
export function auditLine(e, plan) {
|
|
52
|
+
const name = e.name ?? "unknown";
|
|
53
|
+
const k = [...plan.keys.values()].find((x) => x.name === name && x.role === e.key_role);
|
|
54
|
+
return {
|
|
55
|
+
at: new Date().toISOString(),
|
|
56
|
+
credential: name,
|
|
57
|
+
field: "secret",
|
|
58
|
+
site: k?.site ?? e.host,
|
|
59
|
+
url: `https://${e.host}${e.path}`,
|
|
60
|
+
by: `gate:${e.role ?? "?"}`,
|
|
61
|
+
allowed: e.t === "use",
|
|
62
|
+
owner: "wren",
|
|
63
|
+
...(k ? { keyId: k.keyId } : {}),
|
|
64
|
+
...(e.why ? { reason: e.why } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Writes the role files and the CA bundle, starts mitmdump; returns once it runs. */
|
|
68
|
+
export async function gate(config, o = {}) {
|
|
69
|
+
const { service, account } = config.keychain;
|
|
70
|
+
const key = o.gateKey ?? keychainKey(account ? { service, account } : { service });
|
|
71
|
+
const plan = planGate(config, await readSources(config.sources), key);
|
|
72
|
+
await writeRoleFiles(config.dir, plan);
|
|
73
|
+
const confdir = join(config.dir, "ca");
|
|
74
|
+
await mkdir(confdir, { recursive: true, mode: 0o700 });
|
|
75
|
+
const keyed = Object.entries(config.roles).filter(([role]) => [...plan.keys.values()].some((k) => k.role === role));
|
|
76
|
+
if (!keyed.length)
|
|
77
|
+
throw new Error("no role holds a key: nothing to gate");
|
|
78
|
+
const args = [
|
|
79
|
+
"-q",
|
|
80
|
+
...keyed.flatMap(([, r]) => ["--mode", `upstream:${r.upstream}@127.0.0.1:${r.port}`]),
|
|
81
|
+
"--set",
|
|
82
|
+
`confdir=${confdir}`,
|
|
83
|
+
"--set",
|
|
84
|
+
`allow_hosts=${allowHostsRegex(plan.hosts)}`,
|
|
85
|
+
"--set",
|
|
86
|
+
"termlog_verbosity=warn",
|
|
87
|
+
"--set",
|
|
88
|
+
"flow_detail=0",
|
|
89
|
+
"-s",
|
|
90
|
+
ADDON,
|
|
91
|
+
];
|
|
92
|
+
const child = spawn(config.mitmdump, args, {
|
|
93
|
+
stdio: ["ignore", "inherit", "inherit", "pipe", "pipe"],
|
|
94
|
+
});
|
|
95
|
+
const map = {
|
|
96
|
+
ports: Object.fromEntries(keyed.map(([role, r]) => [String(r.port), role])),
|
|
97
|
+
keys: Object.fromEntries([...plan.keys].map(([s, k]) => [
|
|
98
|
+
s,
|
|
99
|
+
{ role: k.role, name: k.name, value: k.value, hosts: k.hosts },
|
|
100
|
+
])),
|
|
101
|
+
};
|
|
102
|
+
child.stdio[3].end(JSON.stringify(map));
|
|
103
|
+
const audit = o.audit ?? fileAudit(config.audit);
|
|
104
|
+
const onDeny = o.onDeny ??
|
|
105
|
+
((e) => console.error(`credvault gate: ALERT ${e.name ?? "unknown sentinel"} refused on ${e.host}${e.path} (role ${e.role}): ${e.why}`));
|
|
106
|
+
createInterface({ input: child.stdio[4] }).on("line", (line) => {
|
|
107
|
+
let e;
|
|
108
|
+
try {
|
|
109
|
+
e = JSON.parse(line);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (e.t === "deny")
|
|
115
|
+
onDeny(e);
|
|
116
|
+
void audit
|
|
117
|
+
.record(auditLine(e, plan))
|
|
118
|
+
.catch((err) => console.error(`credvault gate: audit failed: ${err.message}`));
|
|
119
|
+
});
|
|
120
|
+
const done = new Promise((resolve) => child.on("exit", (code) => resolve(code ?? 1)));
|
|
121
|
+
await waitFor(() => existsSync(join(confdir, CA_CERT)), done, "the gate CA");
|
|
122
|
+
await writeBundle(config.dir, confdir);
|
|
123
|
+
for (const [role, r] of keyed)
|
|
124
|
+
await waitPort(r.port, done, `role ${role}`);
|
|
125
|
+
return { child, plan, done, stop: () => child.kill("SIGTERM") };
|
|
126
|
+
}
|
|
127
|
+
/** `<dir>/ca.pem` (the gate CA, for `NODE_EXTRA_CA_CERTS`) and `<dir>/ca-bundle.pem` (system roots + it). */
|
|
128
|
+
async function writeBundle(dir, confdir) {
|
|
129
|
+
const ca = await readFile(join(confdir, CA_CERT), "utf8");
|
|
130
|
+
const roots = existsSync(SYSTEM_ROOTS) ? await readFile(SYSTEM_ROOTS, "utf8") : "";
|
|
131
|
+
await writeFile(join(dir, "ca.pem"), ca, { mode: 0o644 });
|
|
132
|
+
await writeFile(join(dir, "ca-bundle.pem"), `${roots.trimEnd()}\n${ca}`, { mode: 0o644 });
|
|
133
|
+
}
|
|
134
|
+
async function waitFor(ok, died, what) {
|
|
135
|
+
let dead = false;
|
|
136
|
+
void died.then(() => {
|
|
137
|
+
dead = true;
|
|
138
|
+
});
|
|
139
|
+
for (let i = 0; i < 300; i++) {
|
|
140
|
+
if (ok())
|
|
141
|
+
return;
|
|
142
|
+
if (dead)
|
|
143
|
+
throw new Error(`mitmdump exited before ${what} was ready`);
|
|
144
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`${what} not ready after 30 s`);
|
|
147
|
+
}
|
|
148
|
+
async function waitPort(port, died, what) {
|
|
149
|
+
let dead = false;
|
|
150
|
+
void died.then(() => {
|
|
151
|
+
dead = true;
|
|
152
|
+
});
|
|
153
|
+
const probe = () => new Promise((resolve) => {
|
|
154
|
+
const s = connect(port, "127.0.0.1");
|
|
155
|
+
s.once("connect", () => {
|
|
156
|
+
s.destroy();
|
|
157
|
+
resolve(true);
|
|
158
|
+
});
|
|
159
|
+
s.once("error", () => resolve(false));
|
|
160
|
+
});
|
|
161
|
+
for (let i = 0; i < 300; i++) {
|
|
162
|
+
if (await probe())
|
|
163
|
+
return;
|
|
164
|
+
if (dead)
|
|
165
|
+
throw new Error(`mitmdump exited before ${what} listened`);
|
|
166
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
167
|
+
}
|
|
168
|
+
throw new Error(`${what}: port ${port} not listening after 30 s`);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/gate/run.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export { type Chained, type ChainedFile, chainedFile, rowHash, type Verification
|
|
|
4
4
|
export { aesGcmCipher, type Cipher, isSealed, type KeychainItem, keychainKey, plainCipher, trustKeychainKey, } from "./cipher.js";
|
|
5
5
|
export { CREDENTIAL_ENV_FIELDS, type Credential, type CredentialEnvStore, type CredentialInput, type CredentialStore, credentialEnv, credentialEnvName, credentialSchema, type EnvNaming, envCredentials, fileCredentials, layeredCredentials, memoryCredentials, type PushOptions, pullCredentials, pushCredentials, ROLE_NAME, type SeenVersions, type SharedCredentialStore, type SyncOptions, syncedCredentials, versionsFile, } from "./credentials.js";
|
|
6
6
|
export { ENV_KEY, type EnvEntry, type EnvListing, type EnvStore, envFileStore, expiring, memoryEnvStore, memoryUnshared, type PutOptions, parseDotenv, REMOVED, type SyncedEnvStore, ssmEnvStore, syncedEnvStore, toDotenv, toExports, type Unshared, unsharedFile, upsertDotenv, WouldReplace, } from "./env-store.js";
|
|
7
|
+
export { allowHostsRegex, type GateConfig, type GateKey, type GatePlan, gateConfigSchema, layer, planGate, SENTINEL, sentinel, VENDOR_HOSTS, } from "./gate/plan.js";
|
|
8
|
+
export { auditLine, type GateEvent, type GateOptions, gate, loadGateConfig, type RunningGate, readSources, writeRoleFiles, } from "./gate/run.js";
|
|
7
9
|
export { type CredentialHistory, type CredentialVersion, changedFields, memoryCredentialHistory, ssmCredentialHistory, } from "./history.js";
|
|
8
10
|
export { type ApiKey, type ApiKeyInput, apiKeySchema, applyProjection, envKeys, inRing, isProjectedName, KEY_ACCOUNT, KEY_SITE, type KeyInfo, type KeyStore, type KeysForOptions, keyAddress, keyId, keyInfo, keyRowName, keysFor, keysFromEnv, keyUses, liveAt, memoryKeys, movePurposes, type ProjectionChange, type ProjectionOptions, parseKeyAddress, parseVendorKeyName, projectionDiff, projectKeys, vendorBase, } from "./keys.js";
|
|
9
11
|
export { type AwsCredentials, checkOwner, OWNER_NAME, type OwnerSession, ownerCredentials, ownerPath, ownerSsmClient, } from "./owner.js";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ export { chainedFile, rowHash, verifyChain, } from "./chain.js";
|
|
|
4
4
|
export { aesGcmCipher, isSealed, keychainKey, plainCipher, trustKeychainKey, } from "./cipher.js";
|
|
5
5
|
export { CREDENTIAL_ENV_FIELDS, credentialEnv, credentialEnvName, credentialSchema, envCredentials, fileCredentials, layeredCredentials, memoryCredentials, pullCredentials, pushCredentials, ROLE_NAME, syncedCredentials, versionsFile, } from "./credentials.js";
|
|
6
6
|
export { ENV_KEY, envFileStore, expiring, memoryEnvStore, memoryUnshared, parseDotenv, REMOVED, ssmEnvStore, syncedEnvStore, toDotenv, toExports, unsharedFile, upsertDotenv, WouldReplace, } from "./env-store.js";
|
|
7
|
+
export { allowHostsRegex, gateConfigSchema, layer, planGate, SENTINEL, sentinel, VENDOR_HOSTS, } from "./gate/plan.js";
|
|
8
|
+
export { auditLine, gate, loadGateConfig, readSources, writeRoleFiles, } from "./gate/run.js";
|
|
7
9
|
export { changedFields, memoryCredentialHistory, ssmCredentialHistory, } from "./history.js";
|
|
8
10
|
export { apiKeySchema, applyProjection, envKeys, inRing, isProjectedName, KEY_ACCOUNT, KEY_SITE, keyAddress, keyId, keyInfo, keyRowName, keysFor, keysFromEnv, keyUses, liveAt, memoryKeys, movePurposes, parseKeyAddress, parseVendorKeyName, projectionDiff, projectKeys, vendorBase, } from "./keys.js";
|
|
9
11
|
export { checkOwner, OWNER_NAME, ownerCredentials, ownerPath, ownerSsmClient, } from "./owner.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AACtF,OAAO,EAEL,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,WAAW,EACX,OAAO,EAEP,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EAEZ,QAAQ,EAER,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EAKrB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAEhB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAEjB,eAAe,EACf,eAAe,EACf,SAAS,EAIT,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,OAAO,EAIP,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EAEd,WAAW,EACX,OAAO,EAEP,WAAW,EACX,cAAc,EACd,QAAQ,EACR,SAAS,EAET,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAGL,aAAa,EACb,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,YAAY,EACZ,eAAe,EACf,OAAO,EACP,MAAM,EACN,eAAe,EACf,WAAW,EACX,QAAQ,EAIR,UAAU,EACV,KAAK,EACL,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,OAAO,EACP,MAAM,EACN,UAAU,EACV,YAAY,EAGZ,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAEL,UAAU,EACV,UAAU,EAEV,gBAAgB,EAChB,SAAS,EACT,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,aAAa,EAGb,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,YAAY,EAIZ,IAAI,EACJ,eAAe,GAChB,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AACtF,OAAO,EAEL,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,WAAW,EACX,OAAO,EAEP,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EAEZ,QAAQ,EAER,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EAKrB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAEhB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAEjB,eAAe,EACf,eAAe,EACf,SAAS,EAIT,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,OAAO,EAIP,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EAEd,WAAW,EACX,OAAO,EAEP,WAAW,EACX,cAAc,EACd,QAAQ,EACR,SAAS,EAET,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,eAAe,EAIf,gBAAgB,EAChB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,SAAS,EAGT,IAAI,EACJ,cAAc,EAEd,WAAW,EACX,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAGL,aAAa,EACb,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,YAAY,EACZ,eAAe,EACf,OAAO,EACP,MAAM,EACN,eAAe,EACf,WAAW,EACX,QAAQ,EAIR,UAAU,EACV,KAAK,EACL,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,OAAO,EACP,MAAM,EACN,UAAU,EACV,YAAY,EAGZ,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAEL,UAAU,EACV,UAAU,EAEV,gBAAgB,EAChB,SAAS,EACT,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,aAAa,EAGb,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,YAAY,EAIZ,IAAI,EACJ,eAAe,GAChB,MAAM,WAAW,CAAC"}
|
package/gate/addon.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# credvault key gate: mitmproxy addon. Swaps a sentinel for its real key on the key's hosts only.
|
|
2
|
+
# fd 3 in: the map (JSON, read once). fd 4 out: one JSON line per sentinel seen. Values never go out.
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
from mitmproxy import http
|
|
8
|
+
|
|
9
|
+
SENTINEL = re.compile(r"wsk_[a-z2-7]{28}")
|
|
10
|
+
BODY_MAX = 1 << 20
|
|
11
|
+
|
|
12
|
+
with os.fdopen(3, "r") as f:
|
|
13
|
+
_MAP = json.load(f)
|
|
14
|
+
PORTS = {int(p): r for p, r in _MAP["ports"].items()}
|
|
15
|
+
KEYS = _MAP["keys"] # sentinel -> {role, name, value, hosts}
|
|
16
|
+
_events = os.fdopen(4, "w", buffering=1)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _host_ok(host: str, hosts: list) -> bool:
|
|
20
|
+
for h in hosts:
|
|
21
|
+
if h == host or (h.startswith("*.") and host.endswith(h[1:])):
|
|
22
|
+
return True
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _emit(**e) -> None:
|
|
27
|
+
try:
|
|
28
|
+
_events.write(json.dumps(e) + "\n")
|
|
29
|
+
except (BrokenPipeError, ValueError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _deny(flow: http.HTTPFlow, why: str) -> None:
|
|
34
|
+
flow.response = http.Response.make(403, f"credvault gate: {why}\n".encode(), {"content-type": "text/plain"})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def request(flow: http.HTTPFlow) -> None:
|
|
38
|
+
req = flow.request
|
|
39
|
+
role = PORTS.get(flow.client_conn.sockname[1]) if flow.client_conn.sockname else None
|
|
40
|
+
host = req.pretty_host.lower()
|
|
41
|
+
path = req.path.split("?", 1)[0]
|
|
42
|
+
|
|
43
|
+
found = set()
|
|
44
|
+
for _, v in req.headers.items(multi=True):
|
|
45
|
+
found.update(SENTINEL.findall(v))
|
|
46
|
+
for _, v in req.query.items(multi=True):
|
|
47
|
+
found.update(SENTINEL.findall(v))
|
|
48
|
+
body = None
|
|
49
|
+
if req.raw_content and len(req.raw_content) <= BODY_MAX:
|
|
50
|
+
try:
|
|
51
|
+
body = req.get_text(strict=False)
|
|
52
|
+
except ValueError:
|
|
53
|
+
body = None
|
|
54
|
+
if body:
|
|
55
|
+
found.update(SENTINEL.findall(body))
|
|
56
|
+
if not found:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
for s in found:
|
|
60
|
+
k = KEYS.get(s)
|
|
61
|
+
why = None
|
|
62
|
+
if k is None:
|
|
63
|
+
why = "unknown sentinel"
|
|
64
|
+
elif k["role"] != role:
|
|
65
|
+
why = "key not for this role"
|
|
66
|
+
elif not _host_ok(host, k["hosts"]):
|
|
67
|
+
why = "key not for this host"
|
|
68
|
+
if why:
|
|
69
|
+
_emit(t="deny", name=(k or {}).get("name"), key_role=(k or {}).get("role"), role=role, host=host, path=path, why=why)
|
|
70
|
+
_deny(flow, why)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
for s in found:
|
|
74
|
+
k = KEYS[s]
|
|
75
|
+
for name, v in list(req.headers.items(multi=True)):
|
|
76
|
+
if s in v:
|
|
77
|
+
req.headers.set_all(name, [x.replace(s, k["value"]) for x in req.headers.get_all(name)])
|
|
78
|
+
for name in list(req.query.keys()):
|
|
79
|
+
vals = req.query.get_all(name)
|
|
80
|
+
if any(s in x for x in vals):
|
|
81
|
+
req.query.set_all(name, [x.replace(s, k["value"]) for x in vals])
|
|
82
|
+
if body and s in body:
|
|
83
|
+
body = body.replace(s, k["value"])
|
|
84
|
+
req.set_text(body)
|
|
85
|
+
_emit(t="use", name=k["name"], key_role=k["role"], role=role, host=host, path=path)
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "credvault",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "PolyForm-Strict-1.0.0",
|
|
5
5
|
"packageManager": "pnpm@9.15.0",
|
|
6
6
|
"description": "Keep the credentials an automation signs in with: sealed at rest, shared through SSM, every use audited, expiries watched.",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"credvault": "./dist/cli.js"
|
|
12
|
+
},
|
|
10
13
|
"exports": {
|
|
11
14
|
".": {
|
|
12
15
|
"types": "./dist/index.d.ts",
|
|
@@ -15,6 +18,7 @@
|
|
|
15
18
|
},
|
|
16
19
|
"files": [
|
|
17
20
|
"dist",
|
|
21
|
+
"gate",
|
|
18
22
|
"README.md",
|
|
19
23
|
"LICENSE.md"
|
|
20
24
|
],
|