@takodotid/circuit 0.34.2 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -14
- package/dist/chunks/index-20kcjxjg.js +174 -0
- package/dist/chunks/index-2r4bbhac.js +977 -0
- package/dist/chunks/index-n7w3tg5y.js +149 -0
- package/dist/chunks/new-w3zys5y4.js +172 -0
- package/dist/chunks/server-gzsf5me5.js +259 -0
- package/dist/cli/index.js +11347 -9018
- package/dist/helpers/index.js +594 -0
- package/dist/index.js +9 -28
- package/dist/types/core/{addresses.d.ts → address-space.d.ts} +23 -7
- package/dist/types/core/address.d.ts +44 -0
- package/dist/types/core/cabling.d.ts +41 -0
- package/dist/types/core/cross-checks.d.ts +28 -0
- package/dist/types/core/define.d.ts +25 -3
- package/dist/types/core/project-files.d.ts +12 -0
- package/dist/types/core/schema-docs.d.ts +13 -4
- package/dist/types/core/secrets.d.ts +62 -21
- package/dist/types/core/{graph.d.ts → topology.d.ts} +32 -12
- package/dist/types/core/utils.d.ts +8 -0
- package/dist/types/core/validator.d.ts +19 -0
- package/dist/types/helpers/bgp.d.ts +97 -0
- package/dist/types/helpers/checks.d.ts +32 -0
- package/dist/types/helpers/exchange.d.ts +49 -0
- package/dist/types/helpers/filters.d.ts +51 -0
- package/dist/types/helpers/index.d.ts +10 -0
- package/dist/types/helpers/route-server.d.ts +52 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/integrations/librenms.d.ts +60 -0
- package/dist/types/integrations/netbox.d.ts +89 -0
- package/dist/types/integrations/peeringdb.d.ts +24 -0
- package/dist/types/integrations/prometheus.d.ts +24 -0
- package/dist/types/integrations/registries.d.ts +8 -0
- package/dist/types/platforms/bgp-wait.d.ts +18 -0
- package/dist/types/platforms/catalog.d.ts +109 -0
- package/dist/types/platforms/derive.d.ts +17 -0
- package/dist/types/platforms/fortios/firewall.d.ts +45 -0
- package/dist/types/platforms/fortios/index.d.ts +134 -0
- package/dist/types/platforms/fortios/names.d.ts +52 -0
- package/dist/types/platforms/fortios/plan.d.ts +104 -0
- package/dist/types/platforms/fortios/render.d.ts +75 -0
- package/dist/types/platforms/fortios/routing.d.ts +56 -0
- package/dist/types/platforms/fortios/tree.d.ts +37 -0
- package/dist/types/platforms/index.d.ts +13 -0
- package/dist/types/platforms/junos/bgp.d.ts +44 -0
- package/dist/types/platforms/junos/index.d.ts +114 -0
- package/dist/types/platforms/junos/render.d.ts +154 -0
- package/dist/types/platforms/line-diff.d.ts +67 -0
- package/dist/types/platforms/platform.d.ts +120 -0
- package/dist/types/platforms/raisecom-ros/index.d.ts +71 -0
- package/dist/types/platforms/raisecom-ros/render.d.ts +41 -0
- package/dist/types/platforms/routeros/export.d.ts +59 -0
- package/dist/types/platforms/routeros/index.d.ts +96 -0
- package/dist/types/{adapters/devices → platforms}/routeros/models.d.ts +4 -0
- package/dist/types/platforms/routeros/plan.d.ts +54 -0
- package/dist/types/platforms/routeros/render.d.ts +240 -0
- package/dist/types/platforms/vrp/index.d.ts +83 -0
- package/dist/types/platforms/vrp/render.d.ts +96 -0
- package/dist/types/schema/common.d.ts +12 -6
- package/dist/types/schema/index.d.ts +1 -0
- package/dist/types/schema/interfaces.d.ts +14 -6
- package/dist/types/schema/policy.d.ts +18 -10
- package/dist/types/schema/routing.d.ts +14 -1
- package/dist/types/schema/system.d.ts +16 -6
- package/dist/types/schema/time-zones.d.ts +2 -0
- package/dist/types/transport/console.d.ts +79 -23
- package/dist/types/transport/path-check.d.ts +32 -0
- package/dist/types/transport/ssh.d.ts +17 -15
- package/package.json +17 -11
- package/src/cli/graph-format.ts +91 -0
- package/src/cli/index.ts +1074 -484
- package/src/cli/new.ts +208 -175
- package/src/cli/rename.ts +97 -91
- package/src/core/address-space.ts +320 -0
- package/src/core/address.ts +202 -0
- package/src/core/cabling.ts +154 -0
- package/src/core/cross-checks.ts +364 -0
- package/src/core/define.ts +26 -3
- package/src/core/project-files.ts +35 -0
- package/src/core/schema-docs.ts +138 -130
- package/src/core/secrets.ts +170 -76
- package/src/core/topology.ts +656 -0
- package/src/core/utils.ts +37 -0
- package/src/core/validator.ts +407 -0
- package/src/helpers/bgp.ts +301 -0
- package/src/helpers/checks.ts +178 -0
- package/src/helpers/exchange.ts +117 -0
- package/src/helpers/filters.ts +107 -0
- package/src/helpers/index.ts +12 -0
- package/src/helpers/route-server.ts +212 -0
- package/src/index.ts +6 -3
- package/src/integrations/librenms.ts +291 -0
- package/src/integrations/netbox.ts +620 -0
- package/src/integrations/peeringdb.ts +173 -0
- package/src/integrations/prometheus.ts +90 -0
- package/src/integrations/registries.ts +25 -0
- package/src/platforms/bgp-wait.ts +41 -0
- package/src/platforms/catalog.ts +69 -0
- package/src/platforms/derive.ts +117 -0
- package/src/{adapters/devices → platforms}/fortios/TRAPS.md +6 -0
- package/src/platforms/fortios/firewall.ts +377 -0
- package/src/platforms/fortios/index.ts +1060 -0
- package/src/platforms/fortios/names.ts +130 -0
- package/src/platforms/fortios/plan.ts +929 -0
- package/src/platforms/fortios/render.ts +779 -0
- package/src/platforms/fortios/routing.ts +476 -0
- package/src/platforms/fortios/tree.ts +129 -0
- package/src/platforms/index.ts +36 -0
- package/src/{adapters/devices → platforms}/junos/TRAPS.md +8 -0
- package/src/platforms/junos/bgp.ts +293 -0
- package/src/platforms/junos/index.ts +1006 -0
- package/src/platforms/junos/render.ts +967 -0
- package/src/platforms/line-diff.ts +244 -0
- package/src/platforms/platform.ts +122 -0
- package/src/platforms/raisecom-ros/TRAPS.md +40 -0
- package/src/platforms/raisecom-ros/index.ts +731 -0
- package/src/platforms/raisecom-ros/render.ts +328 -0
- package/src/{adapters/devices → platforms}/routeros/TRAPS.md +16 -2
- package/src/platforms/routeros/export.ts +342 -0
- package/src/platforms/routeros/index.ts +636 -0
- package/src/{adapters/devices → platforms}/routeros/models.ts +2 -0
- package/src/platforms/routeros/plan.ts +444 -0
- package/src/platforms/routeros/render.ts +1483 -0
- package/src/platforms/vrp/TRAPS.md +45 -0
- package/src/platforms/vrp/index.ts +652 -0
- package/src/platforms/vrp/render.ts +615 -0
- package/src/schema/common.ts +15 -6
- package/src/schema/index.ts +1 -0
- package/src/schema/interfaces.ts +15 -5
- package/src/schema/policy.ts +22 -10
- package/src/schema/routing.ts +16 -1
- package/src/schema/system.ts +16 -6
- package/src/schema/time-zones.ts +482 -0
- package/src/studio/data.ts +172 -168
- package/src/studio/git.ts +95 -90
- package/src/studio/public/app.js +161 -150
- package/src/studio/public/compare.js +219 -212
- package/src/studio/public/diff.js +77 -75
- package/src/studio/public/dom.js +118 -108
- package/src/studio/public/evaluate.js +584 -545
- package/src/studio/public/export.js +120 -118
- package/src/studio/public/graph.js +1343 -1266
- package/src/studio/public/net.js +115 -109
- package/src/studio/public/tables.js +1135 -1083
- package/src/studio/public/tools.js +561 -529
- package/src/studio/server.ts +202 -196
- package/src/transport/console.ts +363 -271
- package/src/transport/path-check.ts +91 -0
- package/src/transport/ssh.ts +98 -94
- package/templates/colocation/checks.ts +11 -6
- package/templates/colocation/edge-01.ts +9 -9
- package/templates/colocation/routing.ts +2 -2
- package/templates/colocation/tenancy.ts +3 -3
- package/templates/colocation/tor-01.ts +3 -3
- package/templates/edge-router/checks.ts +4 -4
- package/templates/edge-router/edge-01.ts +5 -5
- package/templates/edge-router/routing.ts +2 -2
- package/templates/project/AGENTS.md +5 -1
- package/templates/single-site/checks.ts +2 -2
- package/dist/chunks/index-47debt16.js +0 -956
- package/dist/chunks/index-v8zgr7wm.js +0 -128
- package/dist/chunks/new-ny89s7y7.js +0 -166
- package/dist/chunks/server-tjbb3rqc.js +0 -251
- package/dist/presets/index.js +0 -394
- package/dist/types/adapters/devices/catalog.d.ts +0 -86
- package/dist/types/adapters/devices/derive.d.ts +0 -7
- package/dist/types/adapters/devices/fortios/firewall.d.ts +0 -23
- package/dist/types/adapters/devices/fortios/index.d.ts +0 -58
- package/dist/types/adapters/devices/fortios/names.d.ts +0 -48
- package/dist/types/adapters/devices/fortios/plan.d.ts +0 -28
- package/dist/types/adapters/devices/fortios/render.d.ts +0 -38
- package/dist/types/adapters/devices/fortios/routing.d.ts +0 -32
- package/dist/types/adapters/devices/fortios/tree.d.ts +0 -33
- package/dist/types/adapters/devices/index.d.ts +0 -3
- package/dist/types/adapters/devices/junos/bgp.d.ts +0 -11
- package/dist/types/adapters/devices/junos/index.d.ts +0 -42
- package/dist/types/adapters/devices/junos/render.d.ts +0 -52
- package/dist/types/adapters/devices/lines.d.ts +0 -56
- package/dist/types/adapters/devices/raisecom-ros/index.d.ts +0 -28
- package/dist/types/adapters/devices/routeros/export.d.ts +0 -39
- package/dist/types/adapters/devices/routeros/index.d.ts +0 -30
- package/dist/types/adapters/devices/routeros/plan.d.ts +0 -4
- package/dist/types/adapters/devices/routeros/render.d.ts +0 -100
- package/dist/types/adapters/devices/types.d.ts +0 -40
- package/dist/types/adapters/devices/vrp/index.d.ts +0 -27
- package/dist/types/adapters/registries/index.d.ts +0 -3
- package/dist/types/adapters/registries/librenms.d.ts +0 -15
- package/dist/types/adapters/registries/peeringdb.d.ts +0 -16
- package/dist/types/adapters/registries/prometheus.d.ts +0 -16
- package/dist/types/core/across.d.ts +0 -4
- package/dist/types/core/addr.d.ts +0 -34
- package/dist/types/core/files.d.ts +0 -6
- package/dist/types/core/merge.d.ts +0 -4
- package/dist/types/core/time.d.ts +0 -2
- package/dist/types/core/validate.d.ts +0 -2
- package/dist/types/presets/index.d.ts +0 -183
- package/src/adapters/devices/catalog.ts +0 -41
- package/src/adapters/devices/derive.ts +0 -59
- package/src/adapters/devices/fortios/firewall.ts +0 -362
- package/src/adapters/devices/fortios/index.ts +0 -923
- package/src/adapters/devices/fortios/names.ts +0 -115
- package/src/adapters/devices/fortios/plan.ts +0 -888
- package/src/adapters/devices/fortios/render.ts +0 -768
- package/src/adapters/devices/fortios/routing.ts +0 -444
- package/src/adapters/devices/fortios/tree.ts +0 -121
- package/src/adapters/devices/index.ts +0 -9
- package/src/adapters/devices/junos/bgp.ts +0 -269
- package/src/adapters/devices/junos/index.ts +0 -862
- package/src/adapters/devices/junos/render.ts +0 -872
- package/src/adapters/devices/lines.ts +0 -212
- package/src/adapters/devices/raisecom-ros/TRAPS.md +0 -25
- package/src/adapters/devices/raisecom-ros/index.ts +0 -693
- package/src/adapters/devices/routeros/export.ts +0 -321
- package/src/adapters/devices/routeros/index.ts +0 -346
- package/src/adapters/devices/routeros/plan.ts +0 -398
- package/src/adapters/devices/routeros/render.ts +0 -1490
- package/src/adapters/devices/types.ts +0 -38
- package/src/adapters/devices/vrp/TRAPS.md +0 -26
- package/src/adapters/devices/vrp/index.ts +0 -865
- package/src/adapters/registries/index.ts +0 -19
- package/src/adapters/registries/librenms.ts +0 -92
- package/src/adapters/registries/peeringdb.ts +0 -169
- package/src/adapters/registries/prometheus.ts +0 -71
- package/src/cli/graph.ts +0 -89
- package/src/core/across.ts +0 -350
- package/src/core/addr.ts +0 -174
- package/src/core/addresses.ts +0 -316
- package/src/core/files.ts +0 -32
- package/src/core/graph.ts +0 -640
- package/src/core/merge.ts +0 -19
- package/src/core/time.ts +0 -15
- package/src/core/validate.ts +0 -348
- package/src/presets/index.ts +0 -579
- /package/dist/types/{adapters/devices → platforms}/fortios/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/junos/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/raisecom-ros/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/vrp/models.d.ts +0 -0
- /package/src/{adapters/devices → platforms}/fortios/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/junos/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/raisecom-ros/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/vrp/models.ts +0 -0
package/src/core/validate.ts
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
// Rules every network has to satisfy, whatever its design. Types catch most of this at compile time; these run again at load, because config is data and may not come from TypeScript.
|
|
2
|
-
|
|
3
|
-
import { X509Certificate } from "node:crypto";
|
|
4
|
-
import { adapters } from "../adapters/devices";
|
|
5
|
-
import { portsOf } from "../adapters/devices/catalog";
|
|
6
|
-
import type { Device } from "../schema";
|
|
7
|
-
import { checkAcross } from "./across";
|
|
8
|
-
import { contains, familyOf, isAddress, isCidr } from "./addr";
|
|
9
|
-
import type { Finding, Network } from "./define";
|
|
10
|
-
|
|
11
|
-
type Report = (message: string) => void;
|
|
12
|
-
|
|
13
|
-
export function validate(network: Network): Finding[] {
|
|
14
|
-
const findings: Finding[] = [];
|
|
15
|
-
|
|
16
|
-
const seen = new Set<string>();
|
|
17
|
-
|
|
18
|
-
for (const device of network.devices) {
|
|
19
|
-
if (seen.has(device.name)) findings.push({ level: "error", device: device.name, message: "declared twice" });
|
|
20
|
-
seen.add(device.name);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
for (const device of network.devices) {
|
|
24
|
-
const report: Report = (message) => findings.push({ level: "error", device: device.name, message });
|
|
25
|
-
checkDevice(device, report);
|
|
26
|
-
|
|
27
|
-
const adapter = adapters[device.platform];
|
|
28
|
-
if (!adapter) report(`no adapter for platform ${device.platform}`);
|
|
29
|
-
else for (const field of adapter.unsupported(device)) report(`${device.platform} cannot express ${field}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
findings.push(...checkAcross(network.devices));
|
|
33
|
-
for (const check of network.checks ?? []) findings.push(...check(network.devices));
|
|
34
|
-
return findings;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Names a device declares, and helpers to report a reference to one it does not. */
|
|
38
|
-
class Names {
|
|
39
|
-
readonly ports: string[];
|
|
40
|
-
|
|
41
|
-
constructor(
|
|
42
|
-
readonly device: Device,
|
|
43
|
-
private readonly report: Report
|
|
44
|
-
) {
|
|
45
|
-
let ports: string[] = [];
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
ports = portsOf(device.platform, device.model);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
report((error as Error).message);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
this.ports = ports;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
isInterface(name: string): boolean {
|
|
57
|
-
return name in (this.device.interfaces ?? {}) || this.ports.includes(name);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Report `name` when it is set and not declared. */
|
|
61
|
-
expect(what: string, name: string | undefined, declared: boolean): void {
|
|
62
|
-
if (name !== undefined && !declared) this.report(`${what} '${name}' is not declared`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
expectInterface(what: string, name: string | undefined): void {
|
|
66
|
-
this.expect(what, name, name !== undefined && this.isInterface(name));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
type Checker = (where: string, value: string) => void;
|
|
71
|
-
|
|
72
|
-
function checkDevice(device: Device, report: Report): void {
|
|
73
|
-
const names = new Names(device, report);
|
|
74
|
-
const cidr: Checker = (where, value) => {
|
|
75
|
-
if (!isCidr(value)) report(`${where}: '${value}' is not an address with a length`);
|
|
76
|
-
};
|
|
77
|
-
const ip: Checker = (where, value) => {
|
|
78
|
-
if (!isAddress(value)) report(`${where}: '${value}' is not an address`);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (!device.users?.[device.connection.user]) report(`connection user '${device.connection.user}' is not one of its users`);
|
|
82
|
-
if (!device.connection.host && !device.connection.console)
|
|
83
|
-
report("connection has neither a host nor a console to reach the device through");
|
|
84
|
-
|
|
85
|
-
checkVlans(device, report);
|
|
86
|
-
checkAddresses(device, report, cidr, ip);
|
|
87
|
-
checkPorts(device, names, report);
|
|
88
|
-
checkInterfaces(device, names, report, ip);
|
|
89
|
-
checkRouting(device, names, report, cidr, ip);
|
|
90
|
-
checkPolicies(device, names, report, cidr);
|
|
91
|
-
checkFirewall(device, names, cidr);
|
|
92
|
-
checkServices(device, names, report, cidr);
|
|
93
|
-
checkCertificates(device, names, report);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function checkVlans(device: Device, report: Report): void {
|
|
97
|
-
const ids = new Map<number, string>();
|
|
98
|
-
|
|
99
|
-
for (const [name, vlan] of Object.entries(device.vlans ?? {})) {
|
|
100
|
-
if (!Number.isInteger(vlan.id) || vlan.id < 1 || vlan.id > 4094) report(`VLAN ${name} id ${vlan.id} is outside 1-4094`);
|
|
101
|
-
if (ids.has(vlan.id)) report(`VLANs ${ids.get(vlan.id)} and ${name} share id ${vlan.id}`);
|
|
102
|
-
ids.set(vlan.id, name);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Every address is valid, and no address sits on two interfaces of one routing table. Two VRFs may hold the same address. */
|
|
107
|
-
function checkAddresses(device: Device, report: Report, cidr: Checker, ip: Checker): void {
|
|
108
|
-
const holderOf = new Map<string, string>();
|
|
109
|
-
const holders = [
|
|
110
|
-
...Object.entries(device.ports ?? {}).map(([name, settings]) => ({ where: `port ${name}`, settings })),
|
|
111
|
-
...Object.entries(device.interfaces ?? {}).map(([name, settings]) => ({ where: `interface ${name}`, settings })),
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
for (const { where, settings } of holders) {
|
|
115
|
-
for (const entry of settings?.addresses ?? []) {
|
|
116
|
-
const address = typeof entry === "string" ? entry : entry.address;
|
|
117
|
-
cidr(where, address);
|
|
118
|
-
if (typeof entry !== "string") ip(where, entry.peer);
|
|
119
|
-
|
|
120
|
-
const bare = address.split("/")[0]!;
|
|
121
|
-
const key = `${settings?.vrf ?? ""} ${bare}`;
|
|
122
|
-
if (holderOf.has(key)) report(`${bare} is on both ${holderOf.get(key)} and ${where}`);
|
|
123
|
-
holderOf.set(key, where);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
for (const group of settings?.vrrp ?? []) {
|
|
127
|
-
ip(`${where} VRRP ${group.id}`, group.address);
|
|
128
|
-
if (group.id < 1 || group.id > 255) report(`${where} VRRP id ${group.id} is outside 1-255`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function checkPorts(device: Device, names: Names, report: Report): void {
|
|
134
|
-
const vlans = device.vlans ?? {};
|
|
135
|
-
|
|
136
|
-
for (const [name, port] of Object.entries(device.ports ?? {})) {
|
|
137
|
-
if (!port) continue;
|
|
138
|
-
|
|
139
|
-
const switched = Boolean(port.access_vlan || port.trunk_vlans);
|
|
140
|
-
|
|
141
|
-
if (!names.ports.includes(name)) report(`port ${name} does not exist on ${device.model}`);
|
|
142
|
-
if (port.access_vlan && port.trunk_vlans) report(`port ${name} is both access and trunk`);
|
|
143
|
-
if (port.native_vlan && !port.trunk_vlans) report(`port ${name} has a native VLAN but is not a trunk`);
|
|
144
|
-
if (switched && port.addresses?.length) report(`port ${name} is switched and routed at once`);
|
|
145
|
-
|
|
146
|
-
if (port.lag && (switched || port.addresses)) {
|
|
147
|
-
report(`port ${name} is in a LAG and states its own switching or addresses; the LAG holds them`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
for (const vlan of [port.access_vlan, port.native_vlan, ...(port.trunk_vlans ?? [])]) {
|
|
151
|
-
names.expect(`port ${name} VLAN`, vlan, vlan === undefined || vlan in vlans);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
names.expect(`port ${name} ACL`, port.acl, port.acl !== undefined && port.acl in (device.acls ?? {}));
|
|
155
|
-
names.expect(`port ${name} LAG`, port.lag, port.lag !== undefined && device.interfaces?.[port.lag]?.type === "lag");
|
|
156
|
-
names.expect(`port ${name} VRF`, port.vrf, port.vrf !== undefined && port.vrf in (device.vrfs ?? {}));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function checkInterfaces(device: Device, names: Names, report: Report, ip: Checker): void {
|
|
161
|
-
const vlans = device.vlans ?? {};
|
|
162
|
-
|
|
163
|
-
for (const [name, iface] of Object.entries(device.interfaces ?? {})) {
|
|
164
|
-
if (names.ports.includes(name)) report(`interface ${name} has the name of a port`);
|
|
165
|
-
names.expect(`interface ${name} VRF`, iface.vrf, iface.vrf !== undefined && iface.vrf in (device.vrfs ?? {}));
|
|
166
|
-
|
|
167
|
-
if (iface.type === "vlan") names.expect(`interface ${name} VLAN`, iface.vlan, iface.vlan in vlans);
|
|
168
|
-
if (iface.type === "gre" || iface.type === "vxlan") {
|
|
169
|
-
ip(`interface ${name}`, iface.local);
|
|
170
|
-
ip(`interface ${name}`, iface.remote);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (iface.type === "lag") {
|
|
174
|
-
const members = Object.values(device.ports ?? {}).filter((port) => port?.lag === name);
|
|
175
|
-
if (!members.length) report(`LAG ${name} has no member ports`);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (iface.type === "bridge") {
|
|
179
|
-
if (iface.members.length < 2) report(`bridge ${name} joins fewer than two interfaces`);
|
|
180
|
-
|
|
181
|
-
for (const member of iface.members) {
|
|
182
|
-
const joined = device.interfaces?.[member];
|
|
183
|
-
if (joined?.type !== "vlan") report(`bridge ${name}: member ${member} is not a VLAN interface`);
|
|
184
|
-
else if (joined.addresses?.length) report(`bridge ${name}: member ${member} has an address of its own`);
|
|
185
|
-
|
|
186
|
-
const others = Object.entries(device.interfaces ?? {}).filter(
|
|
187
|
-
([other, settings]) => other !== name && settings.type === "bridge" && settings.members.includes(member)
|
|
188
|
-
);
|
|
189
|
-
if (others.length) report(`bridge ${name}: member ${member} is also in bridge ${others[0]![0]}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (iface.type === "wireguard") {
|
|
194
|
-
for (const peer of iface.peers ?? []) {
|
|
195
|
-
for (const address of peer.allowed_addresses) {
|
|
196
|
-
if (!isCidr(address)) report(`interface ${name} peer ${peer.name}: '${address}' is not an address with a length`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function checkRouting(device: Device, names: Names, report: Report, cidr: Checker, ip: Checker): void {
|
|
204
|
-
const routing = device.routing;
|
|
205
|
-
const policies = device.policies ?? {};
|
|
206
|
-
|
|
207
|
-
for (const route of routing?.static ?? []) {
|
|
208
|
-
cidr("static route", route.prefix);
|
|
209
|
-
if (!route.via && !route.interface && !route.blackhole) report(`static route ${route.prefix} has no next hop`);
|
|
210
|
-
if (route.via) ip(`static route ${route.prefix}`, route.via);
|
|
211
|
-
names.expectInterface(`static route ${route.prefix} interface`, route.interface);
|
|
212
|
-
names.expect(`static route ${route.prefix} VRF`, route.vrf, route.vrf !== undefined && route.vrf in (device.vrfs ?? {}));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const bgp = routing?.bgp;
|
|
216
|
-
const isPolicy = (policy: string | undefined) => policy !== undefined && policy in policies;
|
|
217
|
-
|
|
218
|
-
for (const prefix of bgp?.networks ?? []) cidr("BGP network", prefix);
|
|
219
|
-
|
|
220
|
-
for (const [name, group] of Object.entries(bgp?.groups ?? {})) {
|
|
221
|
-
for (const policy of [group.import, group.export]) {
|
|
222
|
-
names.expect(`BGP group ${name} policy`, policy, isPolicy(policy));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
for (const [name, neighbor] of Object.entries(bgp?.neighbors ?? {})) {
|
|
227
|
-
ip(`neighbor ${name}`, neighbor.address);
|
|
228
|
-
|
|
229
|
-
const group = neighbor.group ? bgp?.groups?.[neighbor.group] : undefined;
|
|
230
|
-
names.expect(`neighbor ${name} group`, neighbor.group, group !== undefined);
|
|
231
|
-
|
|
232
|
-
if (!(neighbor.remote_as ?? group?.remote_as)) report(`neighbor ${name} has no remote_as`);
|
|
233
|
-
|
|
234
|
-
for (const policy of [neighbor.import, neighbor.export]) {
|
|
235
|
-
names.expect(`neighbor ${name} policy`, policy, isPolicy(policy));
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const local = neighbor.local_address ?? group?.local_address;
|
|
239
|
-
if (local && familyOf(local) !== familyOf(neighbor.address)) report(`neighbor ${name} local_address and address differ in family`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
for (const [areaId, area] of Object.entries(routing?.ospf?.areas ?? {})) {
|
|
243
|
-
if (!/^\d+\.\d+\.\d+\.\d+$/.test(areaId)) report(`OSPF area '${areaId}' is not written as a dotted area ID`);
|
|
244
|
-
for (const iface of Object.keys(area.interfaces)) names.expectInterface(`OSPF area ${areaId} interface`, iface);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function checkPolicies(device: Device, names: Names, report: Report, cidr: Checker): void {
|
|
249
|
-
const policies = device.policies ?? {};
|
|
250
|
-
const sets = device.prefix_sets ?? {};
|
|
251
|
-
|
|
252
|
-
for (const [name, rules] of Object.entries(policies)) {
|
|
253
|
-
for (const rule of rules) {
|
|
254
|
-
names.expect(`policy ${name} call`, rule.call, rule.call !== undefined && rule.call in policies);
|
|
255
|
-
const set = rule.match?.prefix_set;
|
|
256
|
-
names.expect(`policy ${name} prefix set`, set, set !== undefined && set in sets);
|
|
257
|
-
if (rule.match?.prefix) cidr(`policy ${name}`, rule.match.prefix);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// A policy that calls itself, directly or not, never finishes.
|
|
262
|
-
const visit = (policy: string, path: string[]): void => {
|
|
263
|
-
if (path.includes(policy)) return report(`policy ${[...path, policy].join(" -> ")} calls itself`);
|
|
264
|
-
|
|
265
|
-
for (const rule of policies[policy] ?? []) if (rule.call) visit(rule.call, [...path, policy]);
|
|
266
|
-
};
|
|
267
|
-
for (const policy of Object.keys(policies)) visit(policy, []);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function checkFirewall(device: Device, names: Names, cidr: Checker): void {
|
|
271
|
-
const firewall = device.firewall;
|
|
272
|
-
const sets = firewall?.address_sets ?? {};
|
|
273
|
-
|
|
274
|
-
for (const [name, members] of Object.entries(sets)) {
|
|
275
|
-
for (const member of members) {
|
|
276
|
-
if (!isAddress(member)) cidr(`address set ${name}`, member);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const filterChains = [firewall?.filter?.input, firewall?.filter?.forward, firewall?.filter?.output];
|
|
281
|
-
const filterRules = filterChains.flatMap((chain) => chain?.rules ?? []);
|
|
282
|
-
const natRules = [...(firewall?.nat?.source ?? []), ...(firewall?.nat?.destination ?? [])];
|
|
283
|
-
|
|
284
|
-
for (const rule of [...filterRules, ...natRules]) {
|
|
285
|
-
const match = rule.match;
|
|
286
|
-
if (!match) continue;
|
|
287
|
-
|
|
288
|
-
for (const set of [match.src_set, match.dst_set]) {
|
|
289
|
-
names.expect("address set", set, set !== undefined && set in sets);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
for (const iface of [match.in_interface, match.out_interface]) names.expectInterface("interface", iface);
|
|
293
|
-
|
|
294
|
-
for (const address of [match.src, match.dst]) {
|
|
295
|
-
if (address) cidr("firewall rule", address);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function checkServices(device: Device, names: Names, report: Report, cidr: Checker): void {
|
|
301
|
-
for (const [name, server] of Object.entries(device.dhcp ?? {})) {
|
|
302
|
-
names.expectInterface(`DHCP ${name} interface`, server.interface);
|
|
303
|
-
cidr(`DHCP ${name}`, server.network);
|
|
304
|
-
if (!isCidr(server.network)) continue;
|
|
305
|
-
|
|
306
|
-
const reserved = (server.reservations ?? []).map((reservation) => reservation.address);
|
|
307
|
-
const addresses = [server.gateway, ...server.pool, ...reserved];
|
|
308
|
-
|
|
309
|
-
for (const address of addresses) {
|
|
310
|
-
if (!contains(server.network, address)) report(`DHCP ${name}: ${address} is outside ${server.network}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
for (const [name, relay] of Object.entries(device.dhcp_relay ?? {})) {
|
|
315
|
-
names.expectInterface(`DHCP relay ${name} interface`, relay.interface);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
for (const iface of device.flow_export?.interfaces ?? []) names.expectInterface("flow export interface", iface);
|
|
319
|
-
|
|
320
|
-
const lldp = device.lldp;
|
|
321
|
-
if (lldp && lldp !== true) for (const iface of lldp.interfaces) names.expectInterface("LLDP interface", iface);
|
|
322
|
-
|
|
323
|
-
const management = device.management;
|
|
324
|
-
for (const iface of management?.native?.interfaces ?? []) names.expectInterface("native management interface", iface);
|
|
325
|
-
for (const prefix of management?.allow ?? []) cidr("management allow", prefix);
|
|
326
|
-
if (management?.snmp && !management.snmp.community && !management.snmp.users) report("SNMP is on with neither a community nor a user");
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function checkCertificates(device: Device, names: Names, report: Report): void {
|
|
330
|
-
const certificates = device.certificates ?? {};
|
|
331
|
-
|
|
332
|
-
for (const [name, certificate] of Object.entries(certificates)) {
|
|
333
|
-
try {
|
|
334
|
-
new X509Certificate(certificate.certificate);
|
|
335
|
-
} catch {
|
|
336
|
-
report(`certificate ${name} is not a PEM certificate`);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
for (const service of ["https", "api_tls"] as const) {
|
|
341
|
-
const name = device.management?.[service]?.certificate;
|
|
342
|
-
names.expect(`${service} certificate`, name, name !== undefined && name in certificates);
|
|
343
|
-
|
|
344
|
-
if (name && certificates[name] && !certificates[name].private_key) {
|
|
345
|
-
report(`${service} certificate ${name} has no private key to serve with`);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|