@homeflare/alchemy 0.41.0 → 0.42.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/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/credential-errors.d.ts +18 -4
- package/dist/proxmox/credential-errors.d.ts.map +1 -1
- package/dist/proxmox/credentials.d.ts +35 -1
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts.map +1 -1
- package/dist/proxmox/index.js +34 -18
- package/dist/proxmox/index.js.map +16 -15
- package/dist/proxmox/lease-cache.d.ts +67 -5
- package/dist/proxmox/lease-cache.d.ts.map +1 -1
- package/dist/proxmox/mint.d.ts +4 -2
- package/dist/proxmox/mint.d.ts.map +1 -1
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/role.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/unreadable-read.d.ts +8 -1
- package/dist/proxmox/unreadable-read.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/docs/credentials.md +14 -1
- package/package.json +1 -1
- package/src/proxmox/acl.ts +7 -1
- package/src/proxmox/api-token.ts +7 -1
- package/src/proxmox/credential-errors.ts +19 -5
- package/src/proxmox/credentials.test.ts +73 -1
- package/src/proxmox/credentials.ts +72 -1
- package/src/proxmox/group.ts +7 -1
- package/src/proxmox/lease-cache-mint.test.ts +70 -0
- package/src/proxmox/lease-cache.test.ts +83 -7
- package/src/proxmox/lease-cache.ts +74 -10
- package/src/proxmox/mint.ts +12 -5
- package/src/proxmox/node-network.ts +7 -1
- package/src/proxmox/role.ts +7 -1
- package/src/proxmox/storage.ts +7 -1
- package/src/proxmox/unreadable-read.ts +16 -4
- package/src/proxmox/user.ts +7 -1
- package/src/proxmox/zfs-pool.ts +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAUtE,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,EAAE,cAAc,CAAC;IACrB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,+FAA+F;IAC/F,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,QAAQ,EACR,aAAa,EACb,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAyB9D,eAAO,MAAM,kBAAkB,gIAkF5B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../src/proxmox/api-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../src/proxmox/api-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAM7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAStE,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAKlB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kDAE1B,CAAC;AAuBH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,qIAmFjC,CAAC"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { PveRole } from './credentials.ts';
|
|
2
1
|
declare const PveCredentialDenied_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
3
2
|
readonly _tag: "PveCredentialDenied";
|
|
4
3
|
} & Readonly<A>;
|
|
5
4
|
/**
|
|
6
|
-
* OpenBao answered 403 to `GET /v1/<mount>/creds/<
|
|
7
|
-
* for `
|
|
5
|
+
* OpenBao answered 403 to `GET /v1/<mount>/creds/<tier>`: this identity's AppRole has no grant
|
|
6
|
+
* for `tier` on `mount` — measured 2026-09-24 as "the agent lane cannot mint `provision`" (the
|
|
8
7
|
* lane's policy grants `read` but not `provision`). A TYPED tag, not the plain `Error` every
|
|
9
8
|
* other mint failure still is (404 no such role/mount, 503 sealed, a network failure), because
|
|
10
9
|
* this ONE case means "refused, not evidence of anything about the object" — a family's
|
|
@@ -15,10 +14,25 @@ declare const PveCredentialDenied_base: new <A extends Record<string, any> = {}>
|
|
|
15
14
|
* BROKEN, not this identity being narrowly scoped — folding those into "unreadable, report
|
|
16
15
|
* noop" would hide a real outage behind a quiet, misleadingly reassuring plan. Only 403 is
|
|
17
16
|
* "this identity, this role, denied" and safe to read as "cannot see, not evidence of absence".
|
|
17
|
+
*
|
|
18
|
+
* ⛔ NO `role` FIELD (removed, K-T1 second red-team pass). `mint.ts` knows the semantic `PveRole`
|
|
19
|
+
* it was asked for, but `lease-cache.ts`'s synthetic `mintForKey` reconstruction (after M3
|
|
20
|
+
* dropped `role` from `LeaseKey`) cannot supply the ORIGINAL caller's role on a cache-path
|
|
21
|
+
* mint — only the tier. A `role` field would then read as `'read'` on every pve cache miss
|
|
22
|
+
* regardless of which role was actually denied, which is exactly the stale/misleading-field
|
|
23
|
+
* trap M6 existed to close, one layer down. `tier` alone is what the message reads and what
|
|
24
|
+
* `readOrUnreadable`'s callers act on; nothing needs the semantic role name.
|
|
18
25
|
*/
|
|
19
26
|
export declare class PveCredentialDenied extends PveCredentialDenied_base<{
|
|
20
27
|
readonly mount: string;
|
|
21
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The OpenBao mint TIER actually requested (`credentials.ts`'s `mintTier`). ⛔ THE MESSAGE
|
|
30
|
+
* READS THIS, NOT A SEMANTIC ROLE NAME: printing `'read'` for a denied `talos-provision`
|
|
31
|
+
* mint would spell it as `creds/read`, exactly the path that was never called — silently
|
|
32
|
+
* wrong at precisely the moment an operator is reading this string to decide whether a
|
|
33
|
+
* grant, a policy or a target config is wrong (K-T1's own PR discussion).
|
|
34
|
+
*/
|
|
35
|
+
readonly tier: string;
|
|
22
36
|
readonly detail: string;
|
|
23
37
|
}> {
|
|
24
38
|
get message(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/credential-errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credential-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/credential-errors.ts"],"names":[],"mappings":";;;AAMA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,mBAAoB,SAAQ,yBAAwC;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAK7B;CACF"}
|
|
@@ -85,8 +85,41 @@ export type PveTarget = {
|
|
|
85
85
|
* and a wrong guess fails closed with a 401 rather than doing something odd.
|
|
86
86
|
*/
|
|
87
87
|
readonly scheme: 'pve';
|
|
88
|
+
/**
|
|
89
|
+
* Overrides which OpenBao mint TIER NAME (`<mount>/creds/<tier>`) a semantic role resolves to.
|
|
90
|
+
* Omitted or missing a role: unchanged, `role` is also the tier name, as every consumer before
|
|
91
|
+
* this field got. Present: `mintTier` below reads it first.
|
|
92
|
+
*
|
|
93
|
+
* ★ WHY A TARGET-LEVEL OVERRIDE, NOT A THIRD `PveRole`. A consumer whose OpenBao mount vends a
|
|
94
|
+
* single least-privilege tier for BOTH read and write (one tier that can only ever reach the
|
|
95
|
+
* scope that consumer needs, rather than the estate's general `read`/`provision` split) sets
|
|
96
|
+
* `roles: { read: 'my-tier', provision: 'my-tier' }` and every read/diff/reconcile/delete this
|
|
97
|
+
* package already issues resolves to that tier, with no new call site anywhere in the package.
|
|
98
|
+
* Widening `PveRole` itself to a per-consumer string would instead touch every literal
|
|
99
|
+
* `'read'`/`'provision'` call site across the package (`client.ts`, `lxc-read.ts`,
|
|
100
|
+
* `lxc-task.ts`, `network-apply-read.ts`, `network-apply-run.ts`, `qemu-read.ts`,
|
|
101
|
+
* `qemu-task.ts`, `storage-download-task.ts` — the eight this field's `mintTier` indirection
|
|
102
|
+
* above already covers) for something only the TARGET, never the CALL, needs to know.
|
|
103
|
+
*
|
|
104
|
+
* ⛔ SET BOTH ROLES OR NEITHER (I2, red team on K-T1). An unmentioned role is NOT scoped down —
|
|
105
|
+
* it still mints the estate-wide tier this file's own header describes (`hf-provision@pve`,
|
|
106
|
+
* 27 privileges including `Permissions.Modify`/`Sys.Modify` for `provision`). Setting only
|
|
107
|
+
* `roles: { read: 'my-tier' }` leaves every reconcile/delete on this target minting that wide
|
|
108
|
+
* credential while the plan output looks fully scoped down — nothing here warns. See
|
|
109
|
+
* `docs/credentials.md`'s override paragraph for the same warning at the consumer-facing level.
|
|
110
|
+
*/
|
|
111
|
+
readonly roles?: {
|
|
112
|
+
readonly read?: string;
|
|
113
|
+
readonly provision?: string;
|
|
114
|
+
};
|
|
88
115
|
};
|
|
89
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The same client against a Proxmox Backup Server. See the ⛔ on `scheme` above.
|
|
118
|
+
* ⛔ NO `roles` FIELD. The estate has no PBS consumer with a scoped tier yet, and a PBS mount
|
|
119
|
+
* vending only `read`/`provision` is the same shape `mint.ts` always assumed — `mintTier` below
|
|
120
|
+
* never overrides a PBS target for exactly that reason. Add the field here, the same way, when
|
|
121
|
+
* a PBS consumer needs it.
|
|
122
|
+
*/
|
|
90
123
|
export type PbsTarget = {
|
|
91
124
|
readonly mount: string;
|
|
92
125
|
readonly api: string;
|
|
@@ -94,6 +127,7 @@ export type PbsTarget = {
|
|
|
94
127
|
};
|
|
95
128
|
/** Either product. What `pve()` and `pveOperations` accept; what a RESOURCE accepts is narrower. */
|
|
96
129
|
export type ApiTarget = PbsTarget | PveTarget;
|
|
130
|
+
export declare const mintTier: (target: ApiTarget, role: PveRole) => string;
|
|
97
131
|
/**
|
|
98
132
|
* What a mint returns.
|
|
99
133
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/proxmox/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;;;;;;;;;;;;GAYG;AACH,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/proxmox/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;;;;;;;;;;;;GAYG;AACH,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;CACxB,CAAC;AAEF,oGAAoG;AACpG,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AA0B9C,eAAO,MAAM,QAAQ,WAAY,SAAS,QAAQ,OAAO,KAAG,MAY3D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,iGAAiG;AACjG,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../src/proxmox/group.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAc7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../src/proxmox/group.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAc7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKtE,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ,CAC5C,eAAe,EACf,UAAU,EACV,eAAe,EACf,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,oGAAoG;AACpG,eAAO,MAAM,YAAY,+CAEvB,CAAC;AAEH,eAAO,MAAM,oBAAoB,kIAiG9B,CAAC"}
|
package/dist/proxmox/index.js
CHANGED
|
@@ -1025,6 +1025,20 @@ import * as Duration from "effect/Duration";
|
|
|
1025
1025
|
import * as Effect3 from "effect/Effect";
|
|
1026
1026
|
import * as Exit from "effect/Exit";
|
|
1027
1027
|
|
|
1028
|
+
// src/proxmox/credentials.ts
|
|
1029
|
+
var VALID_TIER = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1030
|
+
var mintTier = (target, role) => {
|
|
1031
|
+
if (target.scheme !== "pve")
|
|
1032
|
+
return role;
|
|
1033
|
+
const tier = target.roles?.[role];
|
|
1034
|
+
if (tier === undefined)
|
|
1035
|
+
return role;
|
|
1036
|
+
if (!VALID_TIER.test(tier)) {
|
|
1037
|
+
throw new Error(`PveTarget.roles.${role} = ${JSON.stringify(tier)} is not a valid OpenBao tier name -- ` + 'it would be interpolated straight into "<mount>/creds/<tier>" and must be a plain name ' + '(letters, digits, ".", "_", "-", not starting with one of those and not "..").');
|
|
1038
|
+
}
|
|
1039
|
+
return tier;
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1028
1042
|
// src/proxmox/mint.ts
|
|
1029
1043
|
import * as Effect2 from "effect/Effect";
|
|
1030
1044
|
import * as Option from "effect/Option";
|
|
@@ -1038,14 +1052,15 @@ import * as Data from "effect/Data";
|
|
|
1038
1052
|
|
|
1039
1053
|
class PveCredentialDenied extends Data.TaggedError("PveCredentialDenied") {
|
|
1040
1054
|
get message() {
|
|
1041
|
-
return `OpenBao GET /v1/${this.mount}/creds/${this.
|
|
1055
|
+
return `OpenBao GET /v1/${this.mount}/creds/${this.tier} -> 403: ${this.detail}. This identity's ` + "AppRole has no grant for this role -- a refused read, not evidence the object is gone.";
|
|
1042
1056
|
}
|
|
1043
1057
|
}
|
|
1044
1058
|
|
|
1045
1059
|
// src/proxmox/mint.ts
|
|
1046
1060
|
var baoVariable = (env, name) => env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? "";
|
|
1047
1061
|
var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
1048
|
-
const
|
|
1062
|
+
const tier = mintTier(target, role);
|
|
1063
|
+
const path = `${target.mount}/creds/${tier}`;
|
|
1049
1064
|
const refuse = (status2, detail) => new Error(`OpenBao GET /v1/${path} -> ${status2 === 0 ? "no response" : status2}: ${detail}`);
|
|
1050
1065
|
const address = baoVariable(env, "AGENT_ADDR") || baoVariable(env, "ADDR") || "https://127.0.0.1:8200";
|
|
1051
1066
|
const socket = address.startsWith("unix://") ? address.slice("unix://".length) : undefined;
|
|
@@ -1078,14 +1093,14 @@ var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
|
1078
1093
|
const isOpenBaoBody = Array.isArray(errors);
|
|
1079
1094
|
const detail = isOpenBaoBody ? errors.map(String).join("; ") : "not an OpenBao error body";
|
|
1080
1095
|
if (status === 403 && isOpenBaoBody) {
|
|
1081
|
-
return yield* Effect2.fail(new PveCredentialDenied({ detail, mount: target.mount,
|
|
1096
|
+
return yield* Effect2.fail(new PveCredentialDenied({ detail, mount: target.mount, tier }));
|
|
1082
1097
|
}
|
|
1083
1098
|
return yield* Effect2.fail(refuse(status, detail || "(no errors given)"));
|
|
1084
1099
|
}
|
|
1085
1100
|
const tokenId = parsed?.data?.token_id;
|
|
1086
1101
|
const secret = parsed?.data?.secret;
|
|
1087
1102
|
if (typeof tokenId !== "string" || typeof secret !== "string") {
|
|
1088
|
-
return yield* Effect2.fail(new Error(`${target.mount}/creds/${
|
|
1103
|
+
return yield* Effect2.fail(new Error(`${target.mount}/creds/${tier} returned no token_id/secret. Either this approle lacks ` + `the grant, or the mount is not ${target.mount}. Say which mount and role you needed ` + `than falling back to a stored credential -- every stored PVE token is read-only.`));
|
|
1089
1104
|
}
|
|
1090
1105
|
const lease = parsed?.lease_duration;
|
|
1091
1106
|
const leaseSeconds = typeof lease === "number" ? lease : 0;
|
|
@@ -1097,13 +1112,14 @@ var authorization = (credential, target) => target.scheme === "pbs" ? `PBSAPITok
|
|
|
1097
1112
|
var REMINT_MARGIN_SECONDS = 60;
|
|
1098
1113
|
var keyOf = (target, role) => ({
|
|
1099
1114
|
mount: target.mount,
|
|
1100
|
-
|
|
1101
|
-
|
|
1115
|
+
scheme: target.scheme,
|
|
1116
|
+
tier: mintTier(target, role)
|
|
1102
1117
|
});
|
|
1103
1118
|
var timeToLive = (exit) => Exit.isSuccess(exit) && exit.value.leaseSeconds > REMINT_MARGIN_SECONDS ? Duration.seconds(exit.value.leaseSeconds - REMINT_MARGIN_SECONDS) : Duration.zero;
|
|
1104
1119
|
var makeLeases = (mintFor) => Cache.makeWith(mintFor, { capacity: 64, requireServicesAt: "lookup", timeToLive });
|
|
1105
|
-
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, scheme: "pve" };
|
|
1106
|
-
var
|
|
1120
|
+
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, roles: { read: key.tier }, scheme: "pve" };
|
|
1121
|
+
var mintForKey = (key, env = process.env) => mint(targetForKey(key), key.scheme === "pbs" ? key.tier : "read", env);
|
|
1122
|
+
var leases = Effect3.runSync(makeLeases(mintForKey));
|
|
1107
1123
|
var leased = (target, role) => Cache.get(leases, keyOf(target, role));
|
|
1108
1124
|
|
|
1109
1125
|
// src/proxmox/members.ts
|
|
@@ -1233,7 +1249,7 @@ var runPve = (target, role, isWrite, op) => Effect5.gen(function* () {
|
|
|
1233
1249
|
import * as Effect6 from "effect/Effect";
|
|
1234
1250
|
var UNREADABLE = Symbol("pve-unreadable");
|
|
1235
1251
|
var readOrUnreadable = (effect) => effect.pipe(Effect6.catchTag("PveCredentialDenied", () => Effect6.succeed(UNREADABLE)));
|
|
1236
|
-
var unreadableWarning = (type, identity2) => Effect6.logWarning(`${type} ${identity2}: the read was refused (no grant for this credential's role) --
|
|
1252
|
+
var unreadableWarning = (type, identity2, mount, tier) => Effect6.logWarning(`${type} ${identity2}: the read of ${mount}/creds/${tier} was refused (no grant for this ` + "credential's role) -- reporting noop rather than forcing an update with nothing compared. " + "Re-plan on a lane that can read this object before trusting this row.");
|
|
1237
1253
|
|
|
1238
1254
|
// src/proxmox/acl.ts
|
|
1239
1255
|
var ProxmoxAcl = Resource("Proxmox.Acl");
|
|
@@ -1255,7 +1271,7 @@ var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect7.succeed(Proxm
|
|
|
1255
1271
|
return;
|
|
1256
1272
|
const live = yield* readAttributes(news);
|
|
1257
1273
|
if (live === UNREADABLE) {
|
|
1258
|
-
yield* unreadableWarning("Proxmox.Acl", identity(news));
|
|
1274
|
+
yield* unreadableWarning("Proxmox.Acl", identity(news), news.target.mount, mintTier(news.target, "provision"));
|
|
1259
1275
|
return { action: "noop" };
|
|
1260
1276
|
}
|
|
1261
1277
|
return live !== undefined && matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
@@ -1425,7 +1441,7 @@ var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect8.su
|
|
|
1425
1441
|
return;
|
|
1426
1442
|
const live = yield* readToken(news);
|
|
1427
1443
|
if (live === UNREADABLE) {
|
|
1428
|
-
yield* unreadableWarning("Proxmox.ApiToken", `${news.userid}!${news.tokenid}
|
|
1444
|
+
yield* unreadableWarning("Proxmox.ApiToken", `${news.userid}!${news.tokenid}`, news.target.mount, mintTier(news.target, "provision"));
|
|
1429
1445
|
return { action: "noop" };
|
|
1430
1446
|
}
|
|
1431
1447
|
return live !== undefined && matches2(live, news) ? { action: "noop" } : { action: "update" };
|
|
@@ -2393,7 +2409,7 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect28.succee
|
|
|
2393
2409
|
return;
|
|
2394
2410
|
const live = yield* readGroupOrFail(news);
|
|
2395
2411
|
if (live === UNREADABLE) {
|
|
2396
|
-
yield* unreadableWarning("Proxmox.Group", news.groupid);
|
|
2412
|
+
yield* unreadableWarning("Proxmox.Group", news.groupid, news.target.mount, mintTier(news.target, "read"));
|
|
2397
2413
|
return { action: "noop" };
|
|
2398
2414
|
}
|
|
2399
2415
|
if (live === undefined) {
|
|
@@ -3866,7 +3882,7 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3866
3882
|
return;
|
|
3867
3883
|
const live = yield* readInterfaceOrFail(news);
|
|
3868
3884
|
if (live === UNREADABLE) {
|
|
3869
|
-
yield* unreadableWarning("Proxmox.NodeNetwork", `${news.node}/${news.iface}
|
|
3885
|
+
yield* unreadableWarning("Proxmox.NodeNetwork", `${news.node}/${news.iface}`, news.target.mount, mintTier(news.target, "read"));
|
|
3870
3886
|
return { action: "noop" };
|
|
3871
3887
|
}
|
|
3872
3888
|
if (live === undefined) {
|
|
@@ -5801,7 +5817,7 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect78.succeed(
|
|
|
5801
5817
|
return;
|
|
5802
5818
|
const live = yield* readRole(news);
|
|
5803
5819
|
if (live === UNREADABLE) {
|
|
5804
|
-
yield* unreadableWarning("Proxmox.Role", news.roleid);
|
|
5820
|
+
yield* unreadableWarning("Proxmox.Role", news.roleid, news.target.mount, mintTier(news.target, "read"));
|
|
5805
5821
|
return { action: "noop" };
|
|
5806
5822
|
}
|
|
5807
5823
|
if (live === undefined) {
|
|
@@ -5901,7 +5917,7 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect80.succeed(
|
|
|
5901
5917
|
return;
|
|
5902
5918
|
const live = yield* readUserOrFail(news);
|
|
5903
5919
|
if (live === UNREADABLE) {
|
|
5904
|
-
yield* unreadableWarning("Proxmox.User", news.userid);
|
|
5920
|
+
yield* unreadableWarning("Proxmox.User", news.userid, news.target.mount, mintTier(news.target, "read"));
|
|
5905
5921
|
return { action: "noop" };
|
|
5906
5922
|
}
|
|
5907
5923
|
if (live === undefined) {
|
|
@@ -6989,7 +7005,7 @@ var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect97.su
|
|
|
6989
7005
|
return;
|
|
6990
7006
|
const live = yield* readStorageOrFail(news);
|
|
6991
7007
|
if (live === UNREADABLE) {
|
|
6992
|
-
yield* unreadableWarning("Proxmox.Storage", news.storage);
|
|
7008
|
+
yield* unreadableWarning("Proxmox.Storage", news.storage, news.target.mount, mintTier(news.target, "provision"));
|
|
6993
7009
|
return { action: "noop" };
|
|
6994
7010
|
}
|
|
6995
7011
|
if (live === undefined) {
|
|
@@ -7112,7 +7128,7 @@ var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool, Effect100.s
|
|
|
7112
7128
|
return;
|
|
7113
7129
|
const live = yield* readPoolOrFail(news);
|
|
7114
7130
|
if (live === UNREADABLE) {
|
|
7115
|
-
yield* unreadableWarning("Proxmox.ZfsPool", `${news.node}/${news.name}
|
|
7131
|
+
yield* unreadableWarning("Proxmox.ZfsPool", `${news.node}/${news.name}`, news.target.mount, mintTier(news.target, "read"));
|
|
7116
7132
|
return { action: "noop" };
|
|
7117
7133
|
}
|
|
7118
7134
|
if (live === undefined) {
|
|
@@ -7207,5 +7223,5 @@ export {
|
|
|
7207
7223
|
violations
|
|
7208
7224
|
};
|
|
7209
7225
|
|
|
7210
|
-
//# debugId=
|
|
7226
|
+
//# debugId=71934628F9E3FB7664756E2164756E21
|
|
7211
7227
|
//# sourceMappingURL=index.js.map
|