@namzu/sandbox 25.0.0 → 27.0.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +20 -7
  3. package/dist/backends/firecracker/protocol.d.ts +46 -0
  4. package/dist/backends/firecracker/protocol.d.ts.map +1 -1
  5. package/dist/backends/firecracker/protocol.js +31 -0
  6. package/dist/backends/firecracker/protocol.js.map +1 -1
  7. package/dist/backends/firecracker/transport.d.ts +15 -1
  8. package/dist/backends/firecracker/transport.d.ts.map +1 -1
  9. package/dist/backends/firecracker/transport.js +16 -0
  10. package/dist/backends/firecracker/transport.js.map +1 -1
  11. package/dist/backends/kubernetes/allocation.d.ts +115 -0
  12. package/dist/backends/kubernetes/allocation.d.ts.map +1 -0
  13. package/dist/backends/kubernetes/allocation.js +130 -0
  14. package/dist/backends/kubernetes/allocation.js.map +1 -0
  15. package/dist/backends/kubernetes/egress-policy.d.ts +111 -35
  16. package/dist/backends/kubernetes/egress-policy.d.ts.map +1 -1
  17. package/dist/backends/kubernetes/egress-policy.js +81 -41
  18. package/dist/backends/kubernetes/egress-policy.js.map +1 -1
  19. package/dist/backends/kubernetes/identity.d.ts +129 -0
  20. package/dist/backends/kubernetes/identity.d.ts.map +1 -1
  21. package/dist/backends/kubernetes/identity.js +150 -0
  22. package/dist/backends/kubernetes/identity.js.map +1 -1
  23. package/dist/backends/kubernetes/index.d.ts +23 -1
  24. package/dist/backends/kubernetes/index.d.ts.map +1 -1
  25. package/dist/backends/kubernetes/index.js +22 -3
  26. package/dist/backends/kubernetes/index.js.map +1 -1
  27. package/dist/backends/kubernetes/objects.d.ts +76 -0
  28. package/dist/backends/kubernetes/objects.d.ts.map +1 -1
  29. package/dist/backends/kubernetes/objects.js +61 -0
  30. package/dist/backends/kubernetes/objects.js.map +1 -1
  31. package/dist/backends/kubernetes/per-sandbox-fence.d.ts +194 -0
  32. package/dist/backends/kubernetes/per-sandbox-fence.d.ts.map +1 -0
  33. package/dist/backends/kubernetes/per-sandbox-fence.js +522 -0
  34. package/dist/backends/kubernetes/per-sandbox-fence.js.map +1 -0
  35. package/dist/backends/kubernetes/per-sandbox-policy.d.ts +45 -3
  36. package/dist/backends/kubernetes/per-sandbox-policy.d.ts.map +1 -1
  37. package/dist/backends/kubernetes/per-sandbox-policy.js +98 -8
  38. package/dist/backends/kubernetes/per-sandbox-policy.js.map +1 -1
  39. package/dist/backends/kubernetes/sandbox.d.ts +4 -9
  40. package/dist/backends/kubernetes/sandbox.d.ts.map +1 -1
  41. package/dist/backends/kubernetes/sandbox.js +4 -9
  42. package/dist/backends/kubernetes/sandbox.js.map +1 -1
  43. package/dist/backends/kubernetes/transport.d.ts +108 -1
  44. package/dist/backends/kubernetes/transport.d.ts.map +1 -1
  45. package/dist/backends/kubernetes/transport.js +150 -6
  46. package/dist/backends/kubernetes/transport.js.map +1 -1
  47. package/dist/backends/kubernetes/workspace-allocate.d.ts +206 -0
  48. package/dist/backends/kubernetes/workspace-allocate.d.ts.map +1 -0
  49. package/dist/backends/kubernetes/workspace-allocate.js +332 -0
  50. package/dist/backends/kubernetes/workspace-allocate.js.map +1 -0
  51. package/dist/backends/kubernetes/workspace-observe.d.ts +187 -0
  52. package/dist/backends/kubernetes/workspace-observe.d.ts.map +1 -0
  53. package/dist/backends/kubernetes/workspace-observe.js +343 -0
  54. package/dist/backends/kubernetes/workspace-observe.js.map +1 -0
  55. package/dist/backends/kubernetes/workspace-per-sandbox.d.ts +94 -0
  56. package/dist/backends/kubernetes/workspace-per-sandbox.d.ts.map +1 -0
  57. package/dist/backends/kubernetes/workspace-per-sandbox.js +119 -0
  58. package/dist/backends/kubernetes/workspace-per-sandbox.js.map +1 -0
  59. package/dist/backends/kubernetes/workspace.d.ts +139 -5
  60. package/dist/backends/kubernetes/workspace.d.ts.map +1 -1
  61. package/dist/backends/kubernetes/workspace.js +434 -36
  62. package/dist/backends/kubernetes/workspace.js.map +1 -1
  63. package/dist/index.d.ts +83 -7
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +100 -6
  66. package/dist/index.js.map +1 -1
  67. package/package.json +3 -3
  68. package/src/backends/firecracker/protocol.ts +47 -0
  69. package/src/backends/firecracker/transport.ts +31 -0
  70. package/src/backends/kubernetes/allocation.ts +199 -0
  71. package/src/backends/kubernetes/egress-policy.ts +165 -42
  72. package/src/backends/kubernetes/identity.ts +210 -0
  73. package/src/backends/kubernetes/index.ts +61 -2
  74. package/src/backends/kubernetes/objects.ts +98 -0
  75. package/src/backends/kubernetes/per-sandbox-fence.ts +800 -0
  76. package/src/backends/kubernetes/per-sandbox-policy.ts +152 -17
  77. package/src/backends/kubernetes/sandbox.ts +4 -9
  78. package/src/backends/kubernetes/transport.ts +203 -4
  79. package/src/backends/kubernetes/workspace-allocate.ts +607 -0
  80. package/src/backends/kubernetes/workspace-observe.ts +590 -0
  81. package/src/backends/kubernetes/workspace-per-sandbox.ts +179 -0
  82. package/src/backends/kubernetes/workspace.ts +655 -34
  83. package/src/index.ts +193 -3
@@ -0,0 +1,522 @@
1
+ /**
2
+ * What the per-sandbox egress fence is checked FOR, and the operator-pinned
3
+ * host set that tightens it.
4
+ *
5
+ * `per-sandbox-policy.ts` used to prove the fence by GETting the
6
+ * `ValidatingAdmissionPolicy` and its binding and stopping at "the request
7
+ * succeeded". An object that exists is not an object that fences: a binding
8
+ * whose `validationActions` is `[Audit]` or `[Warn]` logs and admits, a
9
+ * binding that names another policy attaches nothing to this one, a policy
10
+ * with `failurePolicy: Ignore` admits whatever its expressions cannot
11
+ * evaluate, a binding whose namespace selector excludes the sandboxes'
12
+ * namespace never runs there, and a policy whose `matchConditions` name a
13
+ * different user never matches this host. Each reads, from the outside,
14
+ * exactly like an enforced fence, which is the failure the `no-network`
15
+ * baseline's check exists to prevent for its own object: that one compares the
16
+ * named object to the translation field by field, and refuses on any
17
+ * difference.
18
+ *
19
+ * This module is the same standard applied to the fence. It reads what the
20
+ * cluster stored and compares it, field by field, to what the writer relies
21
+ * on, and refuses with a named error carrying the field that failed — before
22
+ * anything is written.
23
+ *
24
+ * ## What it can and cannot prove
25
+ *
26
+ * It proves the CONFIGURATION of the fence: that the binding attaches this
27
+ * policy, denies, fails closed, covers the namespace and the verbs, and that
28
+ * the policy matches the configured identity. It does not evaluate the
29
+ * policy's CEL: whether the shipped expressions refuse what they claim is
30
+ * measured against an API server (see the header of
31
+ * `validatingadmissionpolicy-cilium.yaml`), not derivable from the stored
32
+ * object. The one exception is the companion policy below, whose expressions
33
+ * this module owns and compares verbatim.
34
+ *
35
+ * ## The operator-pinned host set
36
+ *
37
+ * The shipped fence bounds the SHAPE of what a host may write, not the names:
38
+ * any host holding the write verbs can grant any syntactically valid
39
+ * `toFQDNs` entry. `config.egress.perSandbox.permittedHostsParam` names a
40
+ * `ConfigMap` an operator maintains — the reviewed hostnames and ports — and
41
+ * a COMPANION policy (`validatingadmissionpolicy-cilium-permitted-hosts.yaml`)
42
+ * refuses, at admission, any `toFQDNs` name or port outside it. The backend
43
+ * reads the same ConfigMap and refuses locally, with a precise error instead
44
+ * of an opaque 403, but the admission rule stays the authority: the local
45
+ * check is a courtesy and a second reader, never the boundary.
46
+ *
47
+ * A companion rather than a change to the shipped policy because the shipped
48
+ * policy is already applied in clusters, and a host that does not configure
49
+ * the parameter must see byte-for-byte what it saw before. The companion
50
+ * covers CREATE and UPDATE only: a DELETE never widens, and a missing
51
+ * parameter must not make `setNetworkPolicy([])` — the revoke — impossible.
52
+ */
53
+ import { isDeepStrictEqual } from 'node:util';
54
+ import { assertUsableHost, } from './egress-policy.js';
55
+ import { isRecord, matchesLabelSelector, readList } from './ingress-policy.js';
56
+ import { KubernetesAlreadyGoneError, KubernetesCredentialError, } from './k8s-client.js';
57
+ import { configMapPath } from './objects.js';
58
+ /**
59
+ * Raised when the fence EXISTS but would not enforce what this backend relies
60
+ * on — `validationActions` without `Deny`, a binding that names another
61
+ * policy, `failurePolicy: Ignore`, a namespace selector that leaves the
62
+ * sandboxes' namespace out, a `matchConditions` that names another identity,
63
+ * a companion policy whose expressions are not the ones this version ships,
64
+ * or a binding whose parameter is not the configured one.
65
+ *
66
+ * Its own class rather than a reuse of
67
+ * `KubernetesAdmissionFenceMissingError`, because the operator's action
68
+ * differs: that error says to apply a manifest, this one says an applied
69
+ * object is wrong and names the field. `field` is the dotted path inside
70
+ * `resourceKind`, `expected` what the writer needs and `actual` what the
71
+ * cluster holds.
72
+ */
73
+ export class KubernetesAdmissionFenceInertError extends Error {
74
+ resourceKind;
75
+ objectName;
76
+ field;
77
+ expected;
78
+ actual;
79
+ name = 'KubernetesAdmissionFenceInertError';
80
+ constructor(resourceKind, objectName, field, expected, actual) {
81
+ super(`kubernetes: config.egress.perSandbox is configured but the ${resourceKind} named ${objectName} would not fence what this host writes: ${field} is ${actual}, expected ${expected}. An applied admission object that does not enforce reads exactly like one that does, and the host holds create/patch/delete on every CiliumNetworkPolicy in the namespace, so nothing was written. Correct the object (the shipped k8s/manifests/validatingadmissionpolicy-cilium.yaml and validatingadmissionpolicy-cilium-permitted-hosts.yaml are the references) and call again.`);
82
+ this.resourceKind = resourceKind;
83
+ this.objectName = objectName;
84
+ this.field = field;
85
+ this.expected = expected;
86
+ this.actual = actual;
87
+ }
88
+ }
89
+ /**
90
+ * Raised when `permittedHostsParam` is configured and the ConfigMap it names
91
+ * cannot be used: absent, unreadable, or not in the shape the companion
92
+ * policy reads. Every `setNetworkPolicy` call that grants a host is refused
93
+ * with it, and nothing is written.
94
+ */
95
+ export class KubernetesPermittedHostsParamError extends Error {
96
+ param;
97
+ name = 'KubernetesPermittedHostsParamError';
98
+ constructor(param, reason, cause) {
99
+ super(`kubernetes: config.egress.perSandbox.permittedHostsParam names ConfigMap ${param.namespace}/${param.name}, which ${reason}. The admission policy that pins the permitted hosts reads the same object and denies every grant while it is missing or malformed, so this host refuses first, with nothing written. The expected shape is data.hosts (exact hostnames), data.patterns (*.domain patterns) and data.ports (numbers), each one entry per line; see k8s/manifests/validatingadmissionpolicy-cilium-permitted-hosts.yaml.`, cause === undefined ? undefined : { cause });
100
+ this.param = param;
101
+ }
102
+ }
103
+ /**
104
+ * Raised when a `setNetworkPolicy` call names a host or a port the
105
+ * operator-pinned set does not contain. Local, named and before any write; the
106
+ * admission policy would refuse the same body with an opaque 403.
107
+ *
108
+ * `hosts` are the `allowedHosts` entries as written; `ports` are the numbers
109
+ * outside the set, and `unrestrictedPortsFor` the hosts whose rule carries no
110
+ * port restriction at all — every port is outside any list.
111
+ */
112
+ export class KubernetesPermittedHostsError extends Error {
113
+ param;
114
+ hosts;
115
+ ports;
116
+ unrestrictedPortsFor;
117
+ name = 'KubernetesPermittedHostsError';
118
+ constructor(param, hosts, ports, unrestrictedPortsFor) {
119
+ const parts = [];
120
+ if (hosts.length > 0) {
121
+ parts.push(`the host(s) ${hosts.map((h) => JSON.stringify(h)).join(', ')} are not in it`);
122
+ }
123
+ if (ports.length > 0) {
124
+ parts.push(`the port(s) ${ports.join(', ')} are not in it`);
125
+ }
126
+ if (unrestrictedPortsFor.length > 0) {
127
+ parts.push(`${unrestrictedPortsFor.map((h) => JSON.stringify(h)).join(', ')} would be granted on EVERY port, because config.egress.perSandbox.narrowing sets no ports for it, and every port is outside any list (set narrowing.ports or narrowing.hostPorts)`);
128
+ }
129
+ super(`kubernetes: refusing setNetworkPolicy: ConfigMap ${param.namespace}/${param.name} pins the hosts and ports this host may grant, and ${parts.join('; ')}. The shipped admission policy refuses the same body, so it would be a 403 after the fact. Nothing was written.`);
130
+ this.param = param;
131
+ this.hosts = hosts;
132
+ this.ports = ports;
133
+ this.unrestrictedPortsFor = unrestrictedPortsFor;
134
+ }
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Reading an admission object
138
+ // ---------------------------------------------------------------------------
139
+ function describe(value) {
140
+ if (value === undefined)
141
+ return 'absent';
142
+ const text = JSON.stringify(value);
143
+ return text === undefined ? String(value) : text.length > 200 ? `${text.slice(0, 200)}…` : text;
144
+ }
145
+ /** One expression, whitespace-collapsed — a folded YAML scalar and a one-liner compare equal. */
146
+ function normalizeExpression(value) {
147
+ return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
148
+ }
149
+ const CILIUM_GROUP = 'cilium.io';
150
+ const CILIUM_VERSION = 'v2';
151
+ const CILIUM_RESOURCE = 'ciliumnetworkpolicies';
152
+ /** The verbs of a `CiliumNetworkPolicy` write the shipped main policy covers. */
153
+ export const FENCE_OPERATIONS = ['CREATE', 'UPDATE', 'DELETE'];
154
+ /** The verbs the companion covers: a DELETE never widens, see the module header. */
155
+ export const COMPANION_OPERATIONS = ['CREATE', 'UPDATE'];
156
+ function listOf(value) {
157
+ const read = readList(value);
158
+ return read === 'unreadable' ? undefined : (read ?? []);
159
+ }
160
+ /** Does one admission `NamedRuleWithOperations` cover this operation on the CRD? */
161
+ function ruleCovers(rule, operation) {
162
+ if (!isRecord(rule))
163
+ return false;
164
+ const has = (field, wanted) => {
165
+ const list = listOf(rule[field]);
166
+ return list !== undefined && (list.includes(wanted) || list.includes('*'));
167
+ };
168
+ // A rule naming `resourceNames` narrows to those objects, which cannot
169
+ // include the ones this host creates under names it generates.
170
+ const names = listOf(rule.resourceNames);
171
+ if (names === undefined || names.length > 0)
172
+ return false;
173
+ return (has('apiGroups', CILIUM_GROUP) &&
174
+ has('apiVersions', CILIUM_VERSION) &&
175
+ has('resources', CILIUM_RESOURCE) &&
176
+ has('operations', operation));
177
+ }
178
+ /** Does any rule of the list cover the operation? `undefined` when the list is not a list. */
179
+ function rulesCover(rules, operation) {
180
+ const list = listOf(rules);
181
+ if (list === undefined)
182
+ return undefined;
183
+ return list.some((rule) => ruleCovers(rule, operation));
184
+ }
185
+ /**
186
+ * `matchConstraints` (policy) / `matchResources` (binding): both are the same
187
+ * `MatchResources` shape. Proves that, between them, the writes in
188
+ * `operations` reach the validations — resource rules cover them, no exclude
189
+ * rule removes them, and neither selector can be satisfied by an object the
190
+ * host chooses the labels of.
191
+ */
192
+ function assertMatchResources(kind, objectName, path, match, operations, namespace,
193
+ /** A policy MUST state its resource rules; a binding may leave them to the policy. */
194
+ rulesRequired) {
195
+ const fail = (field, expected, actual) => {
196
+ throw new KubernetesAdmissionFenceInertError(kind, objectName, `${path}.${field}`, expected, describe(actual));
197
+ };
198
+ if (match === undefined || match === null) {
199
+ if (rulesRequired)
200
+ fail('resourceRules', 'rules covering cilium.io ciliumnetworkpolicies', match);
201
+ return;
202
+ }
203
+ if (!isRecord(match)) {
204
+ throw new KubernetesAdmissionFenceInertError(kind, objectName, path, 'an object', describe(match));
205
+ }
206
+ if (match.resourceRules !== undefined || rulesRequired) {
207
+ for (const operation of operations) {
208
+ const covered = rulesCover(match.resourceRules, operation);
209
+ if (covered !== true) {
210
+ fail('resourceRules', `a rule covering ${operation} of cilium.io/${CILIUM_VERSION} ${CILIUM_RESOURCE} (a binding that narrows the policy must still reach every write the host issues)`, match.resourceRules);
211
+ }
212
+ }
213
+ }
214
+ const excluded = match.excludeResourceRules;
215
+ if (excluded !== undefined) {
216
+ for (const operation of operations) {
217
+ if (rulesCover(excluded, operation) !== false) {
218
+ fail('excludeResourceRules', `no rule excluding ${operation} of ${CILIUM_RESOURCE}`, excluded);
219
+ }
220
+ }
221
+ }
222
+ // An object selector is matched against the OBJECT's labels, which the
223
+ // writing host chooses: a fence that applies only to objects carrying a
224
+ // label the host can simply leave off fences nothing.
225
+ const objectSelector = match.objectSelector;
226
+ if (objectSelector !== undefined && objectSelector !== null) {
227
+ const empty = isRecord(objectSelector) &&
228
+ Object.keys(isRecord(objectSelector.matchLabels) ? objectSelector.matchLabels : {}).length ===
229
+ 0 &&
230
+ (listOf(objectSelector.matchExpressions) ?? [null]).length === 0;
231
+ if (!empty)
232
+ fail('objectSelector', "absent or empty (the host chooses its objects' labels)", objectSelector);
233
+ }
234
+ const namespaceSelector = match.namespaceSelector;
235
+ if (namespace !== undefined && namespaceSelector !== undefined && namespaceSelector !== null) {
236
+ // Matched against the one label every namespace is guaranteed to carry.
237
+ // A selector on any other label is not decidable from the stored
238
+ // objects, and an unprovable fence is an unproven one.
239
+ const verdict = matchesLabelSelector(namespaceSelector, {
240
+ 'kubernetes.io/metadata.name': namespace,
241
+ });
242
+ if (verdict !== 'yes') {
243
+ fail('namespaceSelector', `a selector that selects namespace ${namespace} (decided from the automatic kubernetes.io/metadata.name label, the only one this host can rely on; a selector on other labels is refused as unprovable)`, namespaceSelector);
244
+ }
245
+ }
246
+ }
247
+ /** The conditions the host identity is checked against, when one is configured. */
248
+ function assertHostIdentity(policyName, policy, hostIdentity) {
249
+ if (hostIdentity === undefined)
250
+ return;
251
+ const expected = `request.userInfo.username == '${hostIdentity}'`;
252
+ const conditions = listOf(policy.matchConditions);
253
+ const names = (conditions ?? []).map((c) => isRecord(c) ? normalizeExpression(c.expression) : '');
254
+ // Exactly that one condition: a second one can only narrow, and one that
255
+ // narrows to nothing fences nothing.
256
+ if (conditions === undefined || names.length !== 1 || names[0] !== expected) {
257
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicy', policyName, 'spec.matchConditions', `exactly one condition, ${expected}`, describe(names.length === 0 ? undefined : names));
258
+ }
259
+ }
260
+ function assertFailClosed(policyName, policy) {
261
+ if (policy.failurePolicy !== 'Fail') {
262
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicy', policyName, 'spec.failurePolicy', '"Fail" (an expression that errors must refuse the write, not admit it)', describe(policy.failurePolicy));
263
+ }
264
+ }
265
+ function specOf(kind, objectName, object) {
266
+ const spec = isRecord(object) ? object.spec : undefined;
267
+ if (!isRecord(spec)) {
268
+ throw new KubernetesAdmissionFenceInertError(kind, objectName, 'spec', 'an object', describe(spec));
269
+ }
270
+ return spec;
271
+ }
272
+ function assertBindingAttaches(bindingName, spec, policyName) {
273
+ if (spec.policyName !== policyName) {
274
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicyBinding', bindingName, 'spec.policyName', JSON.stringify(policyName), describe(spec.policyName));
275
+ }
276
+ const actions = listOf(spec.validationActions);
277
+ if (actions === undefined || !actions.includes('Deny')) {
278
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicyBinding', bindingName, 'spec.validationActions', 'a list including "Deny" (Audit and Warn log and admit)', describe(spec.validationActions));
279
+ }
280
+ }
281
+ /**
282
+ * The main fence: attached, denying, fail-closed, covering the namespace and
283
+ * every verb the host issues, and — when configured — matching the host.
284
+ */
285
+ export function assertFenceEnforces(fence, namespace, perSandbox) {
286
+ assertPolicyAndBinding(fence, namespace, FENCE_OPERATIONS, perSandbox.hostIdentity);
287
+ }
288
+ function assertPolicyAndBinding(fence, namespace, operations, hostIdentity) {
289
+ const policy = specOf('ValidatingAdmissionPolicy', fence.policyName, fence.policy);
290
+ assertFailClosed(fence.policyName, policy);
291
+ assertMatchResources('ValidatingAdmissionPolicy', fence.policyName, 'spec.matchConstraints', policy.matchConstraints, operations, undefined, true);
292
+ const validations = listOf(policy.validations);
293
+ if (validations === undefined || validations.length === 0) {
294
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicy', fence.policyName, 'spec.validations', 'at least one validation (a policy with none validates nothing)', describe(policy.validations));
295
+ }
296
+ assertHostIdentity(fence.policyName, policy, hostIdentity);
297
+ const binding = specOf('ValidatingAdmissionPolicyBinding', fence.bindingName, fence.binding);
298
+ assertBindingAttaches(fence.bindingName, binding, fence.policyName);
299
+ assertMatchResources('ValidatingAdmissionPolicyBinding', fence.bindingName, 'spec.matchResources', binding.matchResources, operations, namespace, false);
300
+ return { policy, binding };
301
+ }
302
+ // ---------------------------------------------------------------------------
303
+ // The companion: the operator-pinned host set
304
+ // ---------------------------------------------------------------------------
305
+ /** The companion policy's name: the main policy's, with this appended. */
306
+ export const PERMITTED_HOSTS_POLICY_SUFFIX = '-hosts';
307
+ /**
308
+ * The companion's names, derived rather than configured: one more name to
309
+ * keep in step with an applied object is one more way for the check to read
310
+ * the wrong object.
311
+ */
312
+ export function permittedHostsFenceNames(perSandbox) {
313
+ const policyName = `${perSandbox.admissionPolicyName}${PERMITTED_HOSTS_POLICY_SUFFIX}`;
314
+ return { policyName, bindingName: `${policyName}-binding` };
315
+ }
316
+ /** The companion's `spec.paramKind`. */
317
+ export const PERMITTED_HOSTS_PARAM_KIND = { apiVersion: 'v1', kind: 'ConfigMap' };
318
+ /**
319
+ * The companion's variables and validation, VERBATIM. Owned here, compared
320
+ * against the cluster's copy by {@link assertPermittedHostsFenceEnforces} and
321
+ * against the shipped YAML by a test, so the three cannot drift: an applied
322
+ * companion with an edited expression is a fence this backend cannot vouch
323
+ * for, and is refused as one.
324
+ *
325
+ * Read as: every egress rule that carries `toFQDNs` must carry `toPorts`, every
326
+ * port of which is in `data.ports`; and every `toFQDNs` entry is a `matchName`
327
+ * in `data.hosts` or a `matchPattern` in `data.patterns`. Lists are
328
+ * newline-separated strings because a ConfigMap holds strings; a key that is
329
+ * missing makes the expression error, and `failurePolicy: Fail` makes an
330
+ * error a refusal.
331
+ */
332
+ export const PERMITTED_HOSTS_VARIABLES = [
333
+ {
334
+ name: 'egress',
335
+ expression: 'has(object.spec) && has(object.spec.egress) ? object.spec.egress : []',
336
+ },
337
+ { name: 'names', expression: "params.data.hosts.split('\\n')" },
338
+ { name: 'patterns', expression: "params.data.patterns.split('\\n')" },
339
+ { name: 'ports', expression: "params.data.ports.split('\\n')" },
340
+ ];
341
+ export const PERMITTED_HOSTS_VALIDATIONS = [
342
+ {
343
+ expression: 'variables.egress.all(r, !has(r.toFQDNs) || (has(r.toPorts) && size(r.toPorts) > 0 && ' +
344
+ 'r.toPorts.all(p, has(p.ports) && size(p.ports) > 0 && ' +
345
+ 'p.ports.all(q, q.port in variables.ports)) && ' +
346
+ 'r.toFQDNs.all(f, (has(f.matchName) && f.matchName in variables.names) || ' +
347
+ '(has(f.matchPattern) && f.matchPattern in variables.patterns))))',
348
+ },
349
+ ];
350
+ /**
351
+ * The companion policy and its binding, compared to what this version ships.
352
+ * Everything {@link assertFenceEnforces} proves for the main fence, over
353
+ * CREATE and UPDATE, plus: the parameter kind, the variables and validation
354
+ * verbatim, and a binding whose `paramRef` is THIS ConfigMap, by name, and
355
+ * denies when it is missing — a binding with no parameter, or one that lets a
356
+ * missing parameter through, pins nothing.
357
+ */
358
+ export function assertPermittedHostsFenceEnforces(fence, namespace, perSandbox, param) {
359
+ const { policy, binding } = assertPolicyAndBinding(fence, namespace, COMPANION_OPERATIONS, perSandbox.hostIdentity);
360
+ const policyFail = (field, expected, actual) => {
361
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicy', fence.policyName, field, expected, describe(actual));
362
+ };
363
+ if (!isDeepStrictEqual(policy.paramKind, PERMITTED_HOSTS_PARAM_KIND)) {
364
+ policyFail('spec.paramKind', describe(PERMITTED_HOSTS_PARAM_KIND), policy.paramKind);
365
+ }
366
+ const variables = (listOf(policy.variables) ?? []).map((v) => isRecord(v) ? { name: v.name, expression: normalizeExpression(v.expression) } : v);
367
+ const expectedVariables = PERMITTED_HOSTS_VARIABLES.map((v) => ({
368
+ name: v.name,
369
+ expression: normalizeExpression(v.expression),
370
+ }));
371
+ if (!isDeepStrictEqual(variables, expectedVariables)) {
372
+ policyFail('spec.variables', describe(expectedVariables), variables);
373
+ }
374
+ const validations = (listOf(policy.validations) ?? []).map((v) => isRecord(v) ? { expression: normalizeExpression(v.expression) } : v);
375
+ const expectedValidations = PERMITTED_HOSTS_VALIDATIONS.map((v) => ({
376
+ expression: normalizeExpression(v.expression),
377
+ }));
378
+ if (!isDeepStrictEqual(validations, expectedValidations)) {
379
+ policyFail('spec.validations', describe(expectedValidations), validations);
380
+ }
381
+ const bindingFail = (field, expected, actual) => {
382
+ throw new KubernetesAdmissionFenceInertError('ValidatingAdmissionPolicyBinding', fence.bindingName, field, expected, describe(actual));
383
+ };
384
+ const paramRef = binding.paramRef;
385
+ if (!isRecord(paramRef)) {
386
+ bindingFail('spec.paramRef', `a reference to ConfigMap ${param.namespace}/${param.name} (a binding with no parameter pins no hosts)`, paramRef);
387
+ return;
388
+ }
389
+ if (paramRef.name !== param.name) {
390
+ bindingFail('spec.paramRef.name', JSON.stringify(param.name), paramRef.name);
391
+ }
392
+ if (paramRef.namespace !== param.namespace) {
393
+ bindingFail('spec.paramRef.namespace', JSON.stringify(param.namespace), paramRef.namespace);
394
+ }
395
+ if (paramRef.selector !== undefined) {
396
+ bindingFail('spec.paramRef.selector', 'absent (the parameter is named, not selected)', paramRef.selector);
397
+ }
398
+ if (paramRef.parameterNotFoundAction !== 'Deny') {
399
+ bindingFail('spec.paramRef.parameterNotFoundAction', '"Deny" (an allow here admits every grant while the ConfigMap is missing)', paramRef.parameterNotFoundAction);
400
+ }
401
+ }
402
+ /**
403
+ * Split one key into its entries. The admission policy splits the same string
404
+ * on `\n` and compares entries byte for byte, so an entry with a stray space
405
+ * or a `\r` would be accepted here and never match there; it is refused as
406
+ * malformed instead. Empty lines are dropped, which is what lets a value end
407
+ * in a newline.
408
+ */
409
+ function entriesOf(param, data, key) {
410
+ const value = data[key];
411
+ if (typeof value !== 'string') {
412
+ throw new KubernetesPermittedHostsParamError(param, `has no data.${key} string (found ${describe(value)}); the policy's expression reads it, so a missing key denies every grant`);
413
+ }
414
+ const entries = value.split('\n').filter((line) => line !== '');
415
+ for (const entry of entries) {
416
+ if (entry.trim() !== entry) {
417
+ throw new KubernetesPermittedHostsParamError(param, `has a data.${key} entry with surrounding whitespace or a carriage return (${JSON.stringify(entry)}); the policy compares entries byte for byte, so it would never match`);
418
+ }
419
+ }
420
+ return entries;
421
+ }
422
+ /**
423
+ * Parse the ConfigMap's `data`, with the same grammar the writer's hosts go
424
+ * through ({@link assertUsableHost}), so a parameter cannot permit a name the
425
+ * writer would refuse to emit.
426
+ */
427
+ export function parsePermittedHosts(param, configMap) {
428
+ const data = isRecord(configMap) ? configMap.data : undefined;
429
+ if (!isRecord(data)) {
430
+ throw new KubernetesPermittedHostsParamError(param, 'has no data');
431
+ }
432
+ const bad = (what, entry, why) => {
433
+ throw new KubernetesPermittedHostsParamError(param, `lists ${what} ${JSON.stringify(entry)}, which ${why}`);
434
+ };
435
+ const hosts = new Set();
436
+ for (const entry of entriesOf(param, data, 'hosts')) {
437
+ if (entry.startsWith('.')) {
438
+ bad('host', entry, 'is a domain entry; data.hosts holds exact names, and a domain is its name here plus a *.domain line in data.patterns');
439
+ }
440
+ try {
441
+ assertUsableHost(entry);
442
+ }
443
+ catch (err) {
444
+ bad('host', entry, `is not a usable hostname (${err instanceof Error ? err.message : String(err)})`);
445
+ }
446
+ if (entry !== entry.toLowerCase())
447
+ bad('host', entry, 'is not lowercase; Cilium compares the lowercase name a lookup returned');
448
+ hosts.add(entry);
449
+ }
450
+ const patterns = new Set();
451
+ for (const entry of entriesOf(param, data, 'patterns')) {
452
+ if (!entry.startsWith('*.'))
453
+ bad('pattern', entry, 'is not of the form *.<domain>');
454
+ const domain = entry.slice(2);
455
+ try {
456
+ assertUsableHost(`.${domain}`);
457
+ }
458
+ catch (err) {
459
+ bad('pattern', entry, `is not a usable domain pattern (${err instanceof Error ? err.message : String(err)})`);
460
+ }
461
+ if (domain !== domain.toLowerCase())
462
+ bad('pattern', entry, 'is not lowercase');
463
+ patterns.add(entry);
464
+ }
465
+ const ports = new Set();
466
+ for (const entry of entriesOf(param, data, 'ports')) {
467
+ const port = Number(entry);
468
+ if (!/^[1-9][0-9]{0,4}$/.test(entry) || port > 65535) {
469
+ bad('port', entry, 'is not a port number (1 to 65535, written without a sign or leading zero)');
470
+ }
471
+ ports.add(port);
472
+ }
473
+ return { hosts, patterns, ports };
474
+ }
475
+ /** GET and parse the parameter. A missing or unreadable ConfigMap refuses. */
476
+ export async function readPermittedHosts(client, param, signal) {
477
+ let configMap;
478
+ try {
479
+ configMap = await client.request('GET', configMapPath(param.namespace, param.name), undefined, signal);
480
+ }
481
+ catch (err) {
482
+ if (err instanceof KubernetesAlreadyGoneError) {
483
+ throw new KubernetesPermittedHostsParamError(param, 'does not exist, so the admission policy bound to it denies every grant');
484
+ }
485
+ if (err instanceof KubernetesCredentialError) {
486
+ throw new KubernetesPermittedHostsParamError(param, 'cannot be read by this host (grant it get on that one ConfigMap; the companion manifest includes the Role)', err);
487
+ }
488
+ throw err;
489
+ }
490
+ return parsePermittedHosts(param, configMap);
491
+ }
492
+ /**
493
+ * Refuse, locally, any host or port outside the permitted set. `hosts` are the
494
+ * canonical `allowedHosts` entries and `portsFor` the port list the writer's
495
+ * narrowing will emit for an entry (`undefined`: no restriction).
496
+ *
497
+ * A `.domain` entry becomes a `matchName` plus a `matchPattern`, and the
498
+ * admission rule checks each against its own list, so both must be present.
499
+ */
500
+ export function assertWithinPermittedHosts(param, permitted, hosts, portsFor) {
501
+ const outside = [];
502
+ const outsidePorts = new Set();
503
+ const unrestricted = [];
504
+ for (const host of hosts) {
505
+ const exact = host.startsWith('.') ? host.slice(1) : host;
506
+ const allowed = permitted.hosts.has(exact) && (!host.startsWith('.') || permitted.patterns.has(`*.${exact}`));
507
+ if (!allowed)
508
+ outside.push(host);
509
+ const ports = portsFor(host);
510
+ if (ports === undefined) {
511
+ unrestricted.push(host);
512
+ continue;
513
+ }
514
+ for (const port of ports)
515
+ if (!permitted.ports.has(port))
516
+ outsidePorts.add(port);
517
+ }
518
+ if (outside.length > 0 || outsidePorts.size > 0 || unrestricted.length > 0) {
519
+ throw new KubernetesPermittedHostsError(param, outside, [...outsidePorts], unrestricted);
520
+ }
521
+ }
522
+ //# sourceMappingURL=per-sandbox-fence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-sandbox-fence.js","sourceRoot":"","sources":["../../../src/backends/kubernetes/per-sandbox-fence.ts"],"names":[],"mappings":"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"}
@@ -46,7 +46,11 @@
46
46
  * operator's own baseline policy. So before the FIRST write, this module
47
47
  * proves an operator applied a `ValidatingAdmissionPolicy` and its binding,
48
48
  * and refuses with {@link KubernetesAdmissionFenceMissingError}, having
49
- * written nothing, if either is absent. The shipped example
49
+ * written nothing, if either is absent — and with
50
+ * {@link KubernetesAdmissionFenceInertError}, naming the field, if either
51
+ * exists and would not enforce (see `per-sandbox-fence.ts`: the same
52
+ * standard the `no-network` baseline's named-object check holds its own
53
+ * object to). The shipped example
50
54
  * (`k8s/manifests/validatingadmissionpolicy-cilium.yaml`) bounds the host
51
55
  * identity to this name prefix, an owner reference whose uid the name has to
52
56
  * match, ONE selector label whose VALUE is that owner's own name (the shape
@@ -56,6 +60,18 @@
56
60
  * kube-dns rule on port 53, no address-based or entity-based peers and no
57
61
  * ingress.
58
62
  *
63
+ * ## Verified after every write
64
+ *
65
+ * Reading the object back proves the object is the one that was sent. It says
66
+ * nothing about the BOUNDARY, which is the union of every policy selecting the
67
+ * pod: a policy applied between this sandbox's create and its grant that
68
+ * allows more is invisible to a read-back of this one. So after the read-back
69
+ * the union is enumerated again, against the baseline translation plus the
70
+ * policy just written (`unionEgressAllowance`), and if anything allows more
71
+ * the object is deleted before the error is thrown — the per-sandbox
72
+ * analogue of the baseline's "no sandbox is handed back". An empty list is not
73
+ * re-checked: a deletion only narrows.
74
+ *
59
75
  * ## Union, not replacement
60
76
  *
61
77
  * The cluster unions every policy selecting a pod, so what this writes ADDS
@@ -78,9 +94,10 @@
78
94
  * network, and it needs a cluster that enforces plus a positive control.
79
95
  */
80
96
  import type { SandboxNetworkPolicy } from '@namzu/sdk';
81
- import { type KubernetesEgressConfig, KubernetesNetworkPolicyHostError, type KubernetesPerSandboxEgressConfig } from './egress-policy.js';
97
+ import { type KubernetesEgressConfig, KubernetesNetworkPolicyHostError, type KubernetesPerSandboxEgressConfig, type KubernetesTranslatedEgressPolicy } from './egress-policy.js';
82
98
  import { type KubernetesClient } from './k8s-client.js';
83
99
  import { type KubernetesOwnerReference } from './objects.js';
100
+ import { KubernetesAdmissionFenceInertError, KubernetesPermittedHostsError, KubernetesPermittedHostsParamError } from './per-sandbox-fence.js';
84
101
  /**
85
102
  * Every per-sandbox policy's name starts with this, and the shipped admission
86
103
  * policy refuses one that does not.
@@ -162,6 +179,7 @@ export declare class KubernetesAdmissionFenceMissingError extends Error {
162
179
  * package index — keeps resolving.
163
180
  */
164
181
  export { KubernetesNetworkPolicyHostError };
182
+ export { KubernetesAdmissionFenceInertError, KubernetesPermittedHostsError, KubernetesPermittedHostsParamError, };
165
183
  /**
166
184
  * Raised when the object an acquire created reported no `metadata.uid`.
167
185
  *
@@ -187,7 +205,7 @@ export declare class KubernetesOwnerUidMissingError extends Error {
187
205
  export interface PerSandboxPolicyFence {
188
206
  assertApplied(signal?: AbortSignal): Promise<void>;
189
207
  }
190
- export declare function buildAdmissionFence(client: KubernetesClient, perSandbox: KubernetesPerSandboxEgressConfig): PerSandboxPolicyFence;
208
+ export declare function buildAdmissionFence(client: KubernetesClient, perSandbox: KubernetesPerSandboxEgressConfig, namespace: string): PerSandboxPolicyFence;
191
209
  /** What one sandbox's policy writer needs to know. */
192
210
  export interface PerSandboxPolicySetterOptions {
193
211
  readonly client: KubernetesClient;
@@ -205,6 +223,30 @@ export interface PerSandboxPolicySetterOptions {
205
223
  * config, through the one resolver.
206
224
  */
207
225
  readonly selectorValue: string;
226
+ /**
227
+ * The post-write union check: given the policy just written and read back,
228
+ * prove that the union of every policy selecting the pod is still the
229
+ * baseline plus that policy, and throw if any other one allows more.
230
+ * Absent when the deployment opted out of union verification
231
+ * (`egress.verify: 'named-object-only'`), exactly as the create path's
232
+ * union check is.
233
+ */
234
+ readonly verifyUnion?: (written: KubernetesTranslatedEgressPolicy, signal?: AbortSignal) => Promise<void>;
235
+ }
236
+ /**
237
+ * Raised when a per-sandbox policy was written, FAILED a post-write check, and
238
+ * the attempt to remove it failed too. The refusal that triggered the removal
239
+ * is `cause`; the policy may still be in force, and the message says so,
240
+ * because the one thing a caller must not conclude is that the refusal left the
241
+ * cluster as it found it.
242
+ */
243
+ export declare class KubernetesPerSandboxPolicyNotRemovedError extends Error {
244
+ readonly policyName: string;
245
+ readonly path: string;
246
+ readonly refusal: unknown;
247
+ readonly removal: unknown;
248
+ readonly name = "KubernetesPerSandboxPolicyNotRemovedError";
249
+ constructor(policyName: string, path: string, refusal: unknown, removal: unknown);
208
250
  }
209
251
  /**
210
252
  * Build the `setNetworkPolicy` this backend attaches to a sandbox handle.