autocatalog-plugin-kubernetes 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +18 -2
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +24 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-OSLDVR63.js +205 -0
- package/dist/chunk-OSLDVR63.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jag Reehal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# autocatalog-plugin-kubernetes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Check the calls the catalog knows against who a cluster lets reach whom.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -D autocatalog-plugin-kubernetes
|
|
7
|
+
ac from-kubernetes k8s/
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
It reads Deployments, StatefulSets, Namespaces and NetworkPolicies and writes
|
|
11
|
+
`autocatalog.network.json`. Commit it: the site says where a call, declared or
|
|
12
|
+
traced, is one the network refuses, and where a service a gateway exposes can be
|
|
13
|
+
reached around it. See [Network policy](https://jagreehal.github.io/autocatalog/docs/network).
|
|
14
|
+
|
|
15
|
+
| Option | Does |
|
|
16
|
+
| ------------------------------ | --------------------------------------------------------- |
|
|
17
|
+
| `--root <dir>` | the repository to write into |
|
|
18
|
+
| `--service <workload=catalog>` | a workload whose catalog name differs, repeatable |
|
|
19
|
+
| `--check` | fail when the committed file does not match the manifests |
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runFromKubernetes
|
|
4
|
+
} from "./chunk-OSLDVR63.js";
|
|
5
|
+
|
|
6
|
+
// src/bin.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
var program = new Command();
|
|
9
|
+
var collect = (value, previous) => [...previous, value];
|
|
10
|
+
program.name("autocatalog-from-kubernetes").description("Write autocatalog.network.json from Kubernetes manifests").argument("<manifests...>", "manifest files, or the folders that hold them").option(
|
|
11
|
+
"--service <workload=catalog>",
|
|
12
|
+
"a workload whose catalog name differs, repeatable",
|
|
13
|
+
collect,
|
|
14
|
+
[]
|
|
15
|
+
).option("--check", "fail when the committed file does not match these manifests").option("--root <dir>", "the repository to write into", ".").action(
|
|
16
|
+
async (manifests, opts) => {
|
|
17
|
+
process.exit(await runFromKubernetes({ ...opts, manifests }));
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
21
|
+
console.error(error);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { runFromKubernetes } from './index';\n\nconst program = new Command();\n\nconst collect = (value: string, previous: string[]) => [...previous, value];\n\nprogram\n .name('autocatalog-from-kubernetes')\n .description('Write autocatalog.network.json from Kubernetes manifests')\n .argument('<manifests...>', 'manifest files, or the folders that hold them')\n .option(\n '--service <workload=catalog>',\n 'a workload whose catalog name differs, repeatable',\n collect,\n [],\n )\n .option('--check', 'fail when the committed file does not match these manifests')\n .option('--root <dir>', 'the repository to write into', '.')\n .action(\n async (manifests: string[], opts: { service: string[]; check?: boolean; root: string }) => {\n process.exit(await runFromKubernetes({ ...opts, manifests }));\n },\n );\n\nprogram.parseAsync(process.argv).catch((error: Error) => {\n console.error(error);\n process.exit(1);\n});\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AAGxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,IAAM,UAAU,CAAC,OAAe,aAAuB,CAAC,GAAG,UAAU,KAAK;AAE1E,QACG,KAAK,6BAA6B,EAClC,YAAY,0DAA0D,EACtE,SAAS,kBAAkB,+CAA+C,EAC1E;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA,CAAC;AACH,EACC,OAAO,WAAW,6DAA6D,EAC/E,OAAO,gBAAgB,gCAAgC,GAAG,EAC1D;AAAA,EACC,OAAO,WAAqB,SAA+D;AACzF,YAAQ,KAAK,MAAM,kBAAkB,EAAE,GAAG,MAAM,UAAU,CAAC,CAAC;AAAA,EAC9D;AACF;AAEF,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAiB;AACvD,UAAQ,MAAM,KAAK;AACnB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import {
|
|
7
|
+
writeNetworkFile
|
|
8
|
+
} from "autocatalog-core";
|
|
9
|
+
import kleur from "kleur";
|
|
10
|
+
import { parseAllDocuments } from "yaml";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
var labels = z.record(z.string(), z.string()).default({});
|
|
13
|
+
var metadata = z.object({ name: z.string(), namespace: z.string().optional(), labels });
|
|
14
|
+
var manifest = z.looseObject({ apiVersion: z.string(), kind: z.string(), metadata });
|
|
15
|
+
var selector = z.object({
|
|
16
|
+
matchLabels: labels,
|
|
17
|
+
matchExpressions: z.array(
|
|
18
|
+
z.object({ key: z.string(), operator: z.string(), values: z.array(z.string()).default([]) })
|
|
19
|
+
).default([])
|
|
20
|
+
});
|
|
21
|
+
var policyPeer = z.object({
|
|
22
|
+
podSelector: selector.optional(),
|
|
23
|
+
namespaceSelector: selector.optional(),
|
|
24
|
+
ipBlock: z.object({ cidr: z.string() }).optional()
|
|
25
|
+
});
|
|
26
|
+
var networkPolicy = z.object({
|
|
27
|
+
spec: z.object({
|
|
28
|
+
podSelector: selector.default({ matchLabels: {}, matchExpressions: [] }),
|
|
29
|
+
policyTypes: z.array(z.enum(["Ingress", "Egress"])).optional(),
|
|
30
|
+
ingress: z.array(z.object({ from: z.array(policyPeer).optional() })).optional(),
|
|
31
|
+
egress: z.array(z.object({ to: z.array(policyPeer).optional() })).optional()
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
var podTemplate = z.object({
|
|
35
|
+
spec: z.object({
|
|
36
|
+
template: z.object({ metadata: z.object({ labels }).default({ labels: {} }) })
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
var NAME_LABEL = "kubernetes.io/metadata.name";
|
|
40
|
+
var OPERATORS = /* @__PURE__ */ new Set(["In", "NotIn", "Exists", "DoesNotExist"]);
|
|
41
|
+
function holds(operator, value, values) {
|
|
42
|
+
switch (operator) {
|
|
43
|
+
case "In":
|
|
44
|
+
return value !== void 0 && values.includes(value);
|
|
45
|
+
case "NotIn":
|
|
46
|
+
return value === void 0 || !values.includes(value);
|
|
47
|
+
case "Exists":
|
|
48
|
+
return value !== void 0;
|
|
49
|
+
case "DoesNotExist":
|
|
50
|
+
return value === void 0;
|
|
51
|
+
default:
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var selects = (wanted, have) => wanted === void 0 || Object.entries(wanted.matchLabels).every(([key, value]) => have[key] === value) && wanted.matchExpressions.every(
|
|
56
|
+
({ key, operator, values }) => holds(operator, have[key], values)
|
|
57
|
+
);
|
|
58
|
+
var empty = (wanted) => wanted === void 0 || Object.keys(wanted.matchLabels).length === 0 && wanted.matchExpressions.length === 0;
|
|
59
|
+
function readNetwork(texts, services = {}) {
|
|
60
|
+
const resources = texts.flatMap((text) => parseAllDocuments(text).map((document) => document.toJS())).flatMap((document) => {
|
|
61
|
+
const parsed = manifest.safeParse(document);
|
|
62
|
+
return parsed.success ? [parsed.data] : [];
|
|
63
|
+
});
|
|
64
|
+
const warnings = [];
|
|
65
|
+
const namespaceOf = (resource) => resource.metadata.namespace ?? "default";
|
|
66
|
+
const namespaceLabels = new Map(
|
|
67
|
+
resources.flatMap(
|
|
68
|
+
(resource) => resource.kind === "Namespace" ? [[resource.metadata.name, resource.metadata.labels]] : []
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
const labelsOfNamespace = (name) => ({
|
|
72
|
+
...namespaceLabels.get(name),
|
|
73
|
+
[NAME_LABEL]: name
|
|
74
|
+
});
|
|
75
|
+
const pods = resources.flatMap((resource) => {
|
|
76
|
+
if (resource.kind !== "Deployment" && resource.kind !== "StatefulSet") return [];
|
|
77
|
+
const parsed = podTemplate.safeParse(resource);
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
service: services[resource.metadata.name] ?? resource.metadata.name,
|
|
81
|
+
namespace: namespaceOf(resource),
|
|
82
|
+
labels: parsed.success ? parsed.data.spec.template.metadata.labels : {}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
});
|
|
86
|
+
const policies = resources.flatMap((resource) => {
|
|
87
|
+
if (resource.kind !== "NetworkPolicy") return [];
|
|
88
|
+
const parsed = networkPolicy.safeParse(resource);
|
|
89
|
+
if (!parsed.success) {
|
|
90
|
+
warnings.push(`NetworkPolicy ${namespaceOf(resource)}/${resource.metadata.name}: not read`);
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return [
|
|
94
|
+
{ name: resource.metadata.name, namespace: namespaceOf(resource), ...parsed.data.spec }
|
|
95
|
+
];
|
|
96
|
+
});
|
|
97
|
+
const peersOf = (entry, namespace, where) => {
|
|
98
|
+
const unknown = [entry.podSelector, entry.namespaceSelector].flatMap(
|
|
99
|
+
(wanted) => wanted?.matchExpressions.filter(({ operator }) => !OPERATORS.has(operator)) ?? []
|
|
100
|
+
);
|
|
101
|
+
if (unknown.length > 0)
|
|
102
|
+
warnings.push(
|
|
103
|
+
`${where}: ${unknown.map(({ operator }) => operator).join(", ")} is not a selector operator, so that peer lets nothing through here`
|
|
104
|
+
);
|
|
105
|
+
if (entry.ipBlock)
|
|
106
|
+
return ["0.0.0.0/0", "::/0"].includes(entry.ipBlock.cidr) ? "anyone" : [{ external: true }];
|
|
107
|
+
if (entry.namespaceSelector && empty(entry.namespaceSelector) && empty(entry.podSelector))
|
|
108
|
+
return "anyone";
|
|
109
|
+
const named = [
|
|
110
|
+
entry.namespaceSelector?.matchLabels[NAME_LABEL],
|
|
111
|
+
...entry.namespaceSelector?.matchExpressions.flatMap(
|
|
112
|
+
({ key, operator, values }) => key === NAME_LABEL && operator === "In" ? values : []
|
|
113
|
+
) ?? []
|
|
114
|
+
].filter((name) => name !== void 0);
|
|
115
|
+
const namespaces = !entry.namespaceSelector ? [namespace] : [.../* @__PURE__ */ new Set([...namespaceLabels.keys(), ...pods.map((p) => p.namespace), ...named])].filter(
|
|
116
|
+
(candidate) => selects(entry.namespaceSelector, labelsOfNamespace(candidate))
|
|
117
|
+
);
|
|
118
|
+
return namespaces.flatMap((candidate) => {
|
|
119
|
+
const inside = pods.filter((pod) => pod.namespace === candidate);
|
|
120
|
+
if (empty(entry.podSelector)) return [{ namespace: candidate }];
|
|
121
|
+
if (inside.length === 0) return [{ namespace: candidate, some: true }];
|
|
122
|
+
return inside.flatMap(
|
|
123
|
+
(pod) => selects(entry.podSelector, pod.labels) ? [{ service: pod.service }] : []
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
const workloads = pods.map((pod) => {
|
|
128
|
+
const selecting = policies.filter(
|
|
129
|
+
(policy) => policy.namespace === pod.namespace && selects(policy.podSelector, pod.labels)
|
|
130
|
+
);
|
|
131
|
+
const direction = (kind) => {
|
|
132
|
+
const applying = selecting.filter(
|
|
133
|
+
(policy) => (policy.policyTypes ?? ["Ingress", ...policy.egress ? ["Egress"] : []]).includes(kind)
|
|
134
|
+
);
|
|
135
|
+
if (applying.length === 0) return void 0;
|
|
136
|
+
const rules = applying.flatMap(
|
|
137
|
+
(policy) => (kind === "Ingress" ? policy.ingress ?? [] : policy.egress ?? []).map((rule) => ({
|
|
138
|
+
peers: "from" in rule ? rule.from : "to" in rule ? rule.to : void 0,
|
|
139
|
+
where: `NetworkPolicy ${policy.namespace}/${policy.name}`
|
|
140
|
+
}))
|
|
141
|
+
);
|
|
142
|
+
if (rules.some(({ peers: peers2 }) => peers2 === void 0 || peers2.length === 0)) return void 0;
|
|
143
|
+
const resolved = rules.flatMap(
|
|
144
|
+
({ peers: peers2 = [], where }) => peers2.map((entry) => peersOf(entry, pod.namespace, where))
|
|
145
|
+
);
|
|
146
|
+
if (resolved.some((peer) => peer === "anyone")) return void 0;
|
|
147
|
+
const peers = resolved.flatMap((peer) => peer === "anyone" ? [] : peer);
|
|
148
|
+
return [...new Map(peers.map((p) => [JSON.stringify(p), p])).values()];
|
|
149
|
+
};
|
|
150
|
+
const ingress = direction("Ingress");
|
|
151
|
+
const egress = direction("Egress");
|
|
152
|
+
return {
|
|
153
|
+
service: pod.service,
|
|
154
|
+
namespace: pod.namespace,
|
|
155
|
+
...ingress && { ingress },
|
|
156
|
+
...egress && { egress }
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
return { file: { workloads }, warnings };
|
|
160
|
+
}
|
|
161
|
+
async function manifestFiles(given) {
|
|
162
|
+
if (!(await fs.stat(given)).isDirectory()) return [given];
|
|
163
|
+
const entries = await fs.readdir(given, { recursive: true, withFileTypes: true });
|
|
164
|
+
return entries.filter((entry) => entry.isFile() && /\.ya?ml$/i.test(entry.name)).map((entry) => path.join(entry.parentPath, entry.name)).sort();
|
|
165
|
+
}
|
|
166
|
+
async function runFromKubernetes(opts) {
|
|
167
|
+
const services = Object.fromEntries(
|
|
168
|
+
(opts.service ?? []).flatMap((pair) => {
|
|
169
|
+
const [workload, catalog] = pair.split("=");
|
|
170
|
+
return workload && catalog ? [[workload, catalog]] : [];
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
let read;
|
|
174
|
+
try {
|
|
175
|
+
const files = (await Promise.all(opts.manifests.map(manifestFiles))).flat();
|
|
176
|
+
read = readNetwork(await Promise.all(files.map((file) => fs.readFile(file, "utf8"))), services);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error(
|
|
179
|
+
kleur.red(
|
|
180
|
+
`\u2717 ${opts.manifests.join(", ")}: ${error instanceof Error ? error.message : String(error)}`
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
for (const warning of read.warnings) console.warn(kleur.yellow(`! ${warning}`));
|
|
186
|
+
const root = path.resolve(opts.root);
|
|
187
|
+
const source = opts.manifests.map((file) => path.relative(root, path.resolve(file)).split(path.sep).join("/")).join(", ");
|
|
188
|
+
const result = await writeNetworkFile(root, { ...read.file, source }, { check: opts.check });
|
|
189
|
+
if (result === "stale") {
|
|
190
|
+
console.error(
|
|
191
|
+
kleur.red("\u2717 autocatalog.network.json does not match the manifests: run ac from-kubernetes")
|
|
192
|
+
);
|
|
193
|
+
return 1;
|
|
194
|
+
}
|
|
195
|
+
console.log(
|
|
196
|
+
`${result === "current" ? "\u2713 autocatalog.network.json is current" : "\u2713 wrote autocatalog.network.json"}: ${read.file.workloads.length} workloads`
|
|
197
|
+
);
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
readNetwork,
|
|
203
|
+
runFromKubernetes
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=chunk-OSLDVR63.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n writeNetworkFile,\n type NetworkFile,\n type NetworkPeer,\n type NetworkWorkload,\n} from 'autocatalog-core';\nimport kleur from 'kleur';\nimport { parseAllDocuments } from 'yaml';\nimport { z } from 'zod';\n\n// Who may reach whom, as Kubernetes says it: a NetworkPolicy selects pods in its\n// namespace and lists the peers each direction allows. A pod no policy selects\n// for a direction is open in that direction. Workloads are Deployments and\n// StatefulSets, each a catalog service by name.\n\nconst labels = z.record(z.string(), z.string()).default({});\n\nconst metadata = z.object({ name: z.string(), namespace: z.string().optional(), labels });\n\nconst manifest = z.looseObject({ apiVersion: z.string(), kind: z.string(), metadata });\n\ntype Manifest = z.infer<typeof manifest>;\n\nconst selector = z.object({\n matchLabels: labels,\n matchExpressions: z\n .array(\n z.object({ key: z.string(), operator: z.string(), values: z.array(z.string()).default([]) }),\n )\n .default([]),\n});\n\ntype Selector = z.infer<typeof selector>;\n\nconst policyPeer = z.object({\n podSelector: selector.optional(),\n namespaceSelector: selector.optional(),\n ipBlock: z.object({ cidr: z.string() }).optional(),\n});\n\nconst networkPolicy = z.object({\n spec: z.object({\n podSelector: selector.default({ matchLabels: {}, matchExpressions: [] }),\n policyTypes: z.array(z.enum(['Ingress', 'Egress'])).optional(),\n ingress: z.array(z.object({ from: z.array(policyPeer).optional() })).optional(),\n egress: z.array(z.object({ to: z.array(policyPeer).optional() })).optional(),\n }),\n});\n\nconst podTemplate = z.object({\n spec: z.object({\n template: z.object({ metadata: z.object({ labels }).default({ labels: {} }) }),\n }),\n});\n\n/** Kubernetes labels every namespace with its own name. */\nconst NAME_LABEL = 'kubernetes.io/metadata.name';\n\n/** Kubernetes' selector operators: anything else selects nothing. */\nconst OPERATORS = new Set(['In', 'NotIn', 'Exists', 'DoesNotExist']);\n\n/** Whether a label's value meets one selector expression. */\nfunction holds(operator: string, value: string | undefined, values: string[]): boolean {\n switch (operator) {\n case 'In':\n return value !== undefined && values.includes(value);\n case 'NotIn':\n return value === undefined || !values.includes(value);\n case 'Exists':\n return value !== undefined;\n case 'DoesNotExist':\n return value === undefined;\n default:\n return false;\n }\n}\n\n/** Whether a selector picks a set of labels: every label it names, and every expression. */\nconst selects = (wanted: Selector | undefined, have: Record<string, string>) =>\n wanted === undefined ||\n (Object.entries(wanted.matchLabels).every(([key, value]) => have[key] === value) &&\n wanted.matchExpressions.every(({ key, operator, values }) =>\n holds(operator, have[key], values),\n ));\n\n/** A selector that picks everything. */\nconst empty = (wanted: Selector | undefined) =>\n wanted === undefined ||\n (Object.keys(wanted.matchLabels).length === 0 && wanted.matchExpressions.length === 0);\n\nexport interface KubernetesNetworkRead {\n file: NetworkFile;\n /** What the manifests hold that the file cannot say, in words. */\n warnings: string[];\n}\n\n/** Who may reach each workload, and what it may reach. `services` maps a workload name to the catalog's. */\nexport function readNetwork(\n texts: string[],\n services: Record<string, string> = {},\n): KubernetesNetworkRead {\n const resources = texts\n .flatMap((text) => parseAllDocuments(text).map((document) => document.toJS()))\n .flatMap((document) => {\n const parsed = manifest.safeParse(document);\n\n return parsed.success ? [parsed.data] : [];\n });\n\n const warnings: string[] = [];\n const namespaceOf = (resource: Manifest) => resource.metadata.namespace ?? 'default';\n\n const namespaceLabels = new Map(\n resources.flatMap((resource) =>\n resource.kind === 'Namespace'\n ? [[resource.metadata.name, resource.metadata.labels] as const]\n : [],\n ),\n );\n\n const labelsOfNamespace = (name: string) => ({\n ...namespaceLabels.get(name),\n [NAME_LABEL]: name,\n });\n\n const pods = resources.flatMap((resource) => {\n if (resource.kind !== 'Deployment' && resource.kind !== 'StatefulSet') return [];\n\n const parsed = podTemplate.safeParse(resource);\n\n return [\n {\n service: services[resource.metadata.name] ?? resource.metadata.name,\n namespace: namespaceOf(resource),\n labels: parsed.success ? parsed.data.spec.template.metadata.labels : {},\n },\n ];\n });\n\n const policies = resources.flatMap((resource) => {\n if (resource.kind !== 'NetworkPolicy') return [];\n\n const parsed = networkPolicy.safeParse(resource);\n\n if (!parsed.success) {\n warnings.push(`NetworkPolicy ${namespaceOf(resource)}/${resource.metadata.name}: not read`);\n\n return [];\n }\n\n return [\n { name: resource.metadata.name, namespace: namespaceOf(resource), ...parsed.data.spec },\n ];\n });\n\n /** The peers one `from` or `to` entry allows, from the policy in `namespace`. */\n const peersOf = (\n entry: z.infer<typeof policyPeer>,\n namespace: string,\n where: string,\n ): NetworkPeer[] | 'anyone' => {\n const unknown = [entry.podSelector, entry.namespaceSelector].flatMap(\n (wanted) => wanted?.matchExpressions.filter(({ operator }) => !OPERATORS.has(operator)) ?? [],\n );\n\n if (unknown.length > 0)\n warnings.push(\n `${where}: ${unknown.map(({ operator }) => operator).join(', ')} is not a selector operator, so that peer lets nothing through here`,\n );\n\n if (entry.ipBlock)\n return ['0.0.0.0/0', '::/0'].includes(entry.ipBlock.cidr) ? 'anyone' : [{ external: true }];\n\n // Every pod in every namespace: anyone in the cluster.\n if (entry.namespaceSelector && empty(entry.namespaceSelector) && empty(entry.podSelector))\n return 'anyone';\n\n // The namespaces this entry covers: its own without a namespace selector. One\n // these files do not declare, such as the gateway's, is known by its name label.\n const named = [\n entry.namespaceSelector?.matchLabels[NAME_LABEL],\n ...(entry.namespaceSelector?.matchExpressions.flatMap(({ key, operator, values }) =>\n key === NAME_LABEL && operator === 'In' ? values : [],\n ) ?? []),\n ].filter((name): name is string => name !== undefined);\n\n const namespaces = !entry.namespaceSelector\n ? [namespace]\n : [...new Set([...namespaceLabels.keys(), ...pods.map((p) => p.namespace), ...named])].filter(\n (candidate) => selects(entry.namespaceSelector, labelsOfNamespace(candidate)),\n );\n\n return namespaces.flatMap((candidate): NetworkPeer[] => {\n const inside = pods.filter((pod) => pod.namespace === candidate);\n\n // A whole namespace, or one whose workloads these files do not hold, such as the gateway's.\n if (empty(entry.podSelector)) return [{ namespace: candidate }];\n\n // Some pods of a namespace these files hold no workloads of, such as the\n // gateway's: which ones cannot be told, so never all of it.\n if (inside.length === 0) return [{ namespace: candidate, some: true }];\n\n return inside.flatMap((pod) =>\n selects(entry.podSelector, pod.labels) ? [{ service: pod.service }] : [],\n );\n });\n };\n\n const workloads: NetworkWorkload[] = pods.map((pod) => {\n const selecting = policies.filter(\n (policy) => policy.namespace === pod.namespace && selects(policy.podSelector, pod.labels),\n );\n\n const direction = (kind: 'Ingress' | 'Egress'): NetworkPeer[] | undefined => {\n // Without policyTypes, a policy restricts ingress, and egress too when it lists egress rules.\n const applying = selecting.filter((policy) =>\n (policy.policyTypes ?? ['Ingress', ...(policy.egress ? ['Egress'] : [])]).includes(kind),\n );\n\n if (applying.length === 0) return undefined;\n\n const rules = applying.flatMap((policy) =>\n (kind === 'Ingress' ? (policy.ingress ?? []) : (policy.egress ?? [])).map((rule) => ({\n peers: 'from' in rule ? rule.from : 'to' in rule ? rule.to : undefined,\n where: `NetworkPolicy ${policy.namespace}/${policy.name}`,\n })),\n );\n\n // A rule with no peers, missing or empty, lets everyone through.\n if (rules.some(({ peers }) => peers === undefined || peers.length === 0)) return undefined;\n\n const resolved = rules.flatMap(({ peers = [], where }) =>\n peers.map((entry) => peersOf(entry, pod.namespace, where)),\n );\n\n // Anyone, or the whole internet, opens the direction as surely as no policy does.\n if (resolved.some((peer) => peer === 'anyone')) return undefined;\n\n const peers = resolved.flatMap((peer) => (peer === 'anyone' ? [] : peer));\n\n return [...new Map(peers.map((p) => [JSON.stringify(p), p])).values()];\n };\n\n const ingress = direction('Ingress');\n const egress = direction('Egress');\n\n return {\n service: pod.service,\n namespace: pod.namespace,\n ...(ingress && { ingress }),\n ...(egress && { egress }),\n };\n });\n\n return { file: { workloads }, warnings };\n}\n\n/** A file as given, or every YAML file below a folder, in path order. */\nasync function manifestFiles(given: string): Promise<string[]> {\n if (!(await fs.stat(given)).isDirectory()) return [given];\n\n const entries = await fs.readdir(given, { recursive: true, withFileTypes: true });\n\n return entries\n .filter((entry) => entry.isFile() && /\\.ya?ml$/i.test(entry.name))\n .map((entry) => path.join(entry.parentPath, entry.name))\n .sort();\n}\n\nexport async function runFromKubernetes(opts: {\n manifests: string[];\n root: string;\n service?: string[];\n check?: boolean;\n}): Promise<number> {\n const services = Object.fromEntries(\n (opts.service ?? []).flatMap((pair) => {\n const [workload, catalog] = pair.split('=');\n\n return workload && catalog ? [[workload, catalog]] : [];\n }),\n );\n\n let read: KubernetesNetworkRead;\n\n try {\n const files = (await Promise.all(opts.manifests.map(manifestFiles))).flat();\n\n read = readNetwork(await Promise.all(files.map((file) => fs.readFile(file, 'utf8'))), services);\n } catch (error) {\n console.error(\n kleur.red(\n `✗ ${opts.manifests.join(', ')}: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n\n return 1;\n }\n\n for (const warning of read.warnings) console.warn(kleur.yellow(`! ${warning}`));\n\n const root = path.resolve(opts.root);\n\n const source = opts.manifests\n .map((file) => path.relative(root, path.resolve(file)).split(path.sep).join('/'))\n .join(', ');\n\n const result = await writeNetworkFile(root, { ...read.file, source }, { check: opts.check });\n\n if (result === 'stale') {\n console.error(\n kleur.red('✗ autocatalog.network.json does not match the manifests: run ac from-kubernetes'),\n );\n\n return 1;\n }\n\n console.log(\n `${result === 'current' ? '✓ autocatalog.network.json is current' : '✓ wrote autocatalog.network.json'}: ${read.file.workloads.length} workloads`,\n );\n\n return 0;\n}\n"],"mappings":"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","names":["peers"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NetworkFile } from 'autocatalog-core';
|
|
2
|
+
|
|
3
|
+
interface KubernetesNetworkRead {
|
|
4
|
+
file: NetworkFile;
|
|
5
|
+
/** What the manifests hold that the file cannot say, in words. */
|
|
6
|
+
warnings: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Who may reach each workload, and what it may reach. `services` maps a workload name to the catalog's. */
|
|
9
|
+
declare function readNetwork(texts: string[], services?: Record<string, string>): KubernetesNetworkRead;
|
|
10
|
+
declare function runFromKubernetes(opts: {
|
|
11
|
+
manifests: string[];
|
|
12
|
+
root: string;
|
|
13
|
+
service?: string[];
|
|
14
|
+
check?: boolean;
|
|
15
|
+
}): Promise<number>;
|
|
16
|
+
|
|
17
|
+
export { type KubernetesNetworkRead, readNetwork, runFromKubernetes };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autocatalog-plugin-kubernetes",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ac from-kubernetes: check calls against who a cluster lets reach whom.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jagreehal/autocatalog.git",
|
|
9
|
+
"directory": "packages/events/plugin-kubernetes"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://jagreehal.github.io/autocatalog",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jagreehal/autocatalog/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"bin": {
|
|
19
|
+
"autocatalog-from-kubernetes": "./dist/bin.js"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"autocatalog": {
|
|
31
|
+
"plugin": {
|
|
32
|
+
"command": "from-kubernetes",
|
|
33
|
+
"description": "Read the network policies of Kubernetes manifests",
|
|
34
|
+
"entry": "./dist/bin.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"commander": "^15.0.0",
|
|
39
|
+
"kleur": "^4.1.5",
|
|
40
|
+
"yaml": "^2.9.1",
|
|
41
|
+
"zod": "^4.6.5",
|
|
42
|
+
"autocatalog-core": "0.10.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^26.6.4",
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^6.0.3",
|
|
48
|
+
"vitest": "^5.0.3"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"test": "vitest run"
|
|
53
|
+
}
|
|
6
54
|
}
|