@takodotid/circuit 0.34.3 → 0.36.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 +14 -15
- 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 +11345 -9024
- 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 +18 -12
- 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 -26
- package/src/adapters/devices/raisecom-ros/index.ts +0 -700
- 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
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
// Packet filtering as policies through the device and local-in policies to it, who reaches the device's own services, NAT as central source NAT and virtual IPs, and stateless ACLs on ports.
|
|
2
|
-
|
|
3
|
-
import { host } from "../../../core/addr";
|
|
4
|
-
import type { AclRule, Device, Family, FilterMatch, FilterRule, NatRule, Ports, Protocol } from "../../../schema";
|
|
5
|
-
import { familyOfPrefix, isTransparent, nameOf, network, subnet, withLength } from "./names";
|
|
6
|
-
import { Block, quote, quoteAll, UNSET } from "./tree";
|
|
7
|
-
|
|
8
|
-
/** IP protocol numbers, for a protocol the platform names only by number. */
|
|
9
|
-
export const PROTOCOL_NUMBER: Record<Protocol, number> = { tcp: 6, udp: 17, icmp: 1, gre: 47, esp: 50, ah: 51, ospf: 89, vrrp: 112 };
|
|
10
|
-
|
|
11
|
-
/** The device's own services, by the config's name, with the protocol and port each listens on unless the config moves it. */
|
|
12
|
-
const SERVICES = {
|
|
13
|
-
ssh: { protocol: "tcp", port: 22 },
|
|
14
|
-
https: { protocol: "tcp", port: 443 },
|
|
15
|
-
http: { protocol: "tcp", port: 80 },
|
|
16
|
-
telnet: { protocol: "tcp", port: 23 },
|
|
17
|
-
snmp: { protocol: "udp", port: 161 },
|
|
18
|
-
native: { protocol: "tcp", port: 541 },
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
/** NAT rules of both directions, which central NAT needs turned on. */
|
|
22
|
-
export function natRules(device: Device): NatRule<string, string>[] {
|
|
23
|
-
const nat = device.firewall?.nat;
|
|
24
|
-
return [...(nat?.source ?? []), ...(nat?.destination ?? [])];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Which families a rule applies to: its own, its addresses', or both. */
|
|
28
|
-
export function familiesOf(device: Device, match: FilterMatch<string, string> | undefined): Family[] {
|
|
29
|
-
if (match?.family) return [match.family];
|
|
30
|
-
|
|
31
|
-
const prefixes = [match?.src, match?.dst].filter((prefix): prefix is string => prefix !== undefined);
|
|
32
|
-
if (prefixes.length) return [...new Set(prefixes.map(familyOfPrefix))];
|
|
33
|
-
|
|
34
|
-
let families: Family[] = ["ipv4", "ipv6"];
|
|
35
|
-
|
|
36
|
-
for (const set of [match?.src_set, match?.dst_set]) {
|
|
37
|
-
if (!set) continue;
|
|
38
|
-
|
|
39
|
-
const present = new Set((device.firewall?.address_sets?.[set] ?? []).map(familyOfPrefix));
|
|
40
|
-
families = families.filter((family) => present.has(family));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return families;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** A port or ports as the platform writes them in a service: `22`, `1000-2000`. */
|
|
47
|
-
export function portList(ports: Ports | undefined): string[] {
|
|
48
|
-
if (ports === undefined) return [];
|
|
49
|
-
|
|
50
|
-
const list = Array.isArray(ports) ? ports : [ports as number | { min: number; max: number }];
|
|
51
|
-
return list.map((port) => (typeof port === "number" ? String(port) : `${port.min}-${port.max}`));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Every protocol, as a service of Circuit's own, for a device in transparent mode. */
|
|
55
|
-
export const ALL_IN_TRANSPARENT = "circuit-all";
|
|
56
|
-
|
|
57
|
-
/** The service a rule matches, by name, with what it holds. `ALL` when the rule names no protocol and no port; ports without a protocol are TCP's and UDP's. */
|
|
58
|
-
export function serviceOf(match: FilterMatch<string, string> | undefined, family: Family): { name: string; block?: Block } {
|
|
59
|
-
const protocol = match?.protocol;
|
|
60
|
-
const destinations = portList(match?.dst_port);
|
|
61
|
-
const sources = portList(match?.src_port);
|
|
62
|
-
|
|
63
|
-
if (!protocol && !destinations.length && !sources.length) return { name: "ALL" };
|
|
64
|
-
|
|
65
|
-
if (!protocol || protocol === "tcp" || protocol === "udp") {
|
|
66
|
-
const ranges = (destinations.length ? destinations : ["1-65535"]).flatMap((destination) =>
|
|
67
|
-
sources.length ? sources.map((source) => `${destination}:${source}`) : [destination]
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const protocols = protocol ? [protocol] : (["tcp", "udp"] as const);
|
|
71
|
-
const name = `${protocols.join("+")}/${destinations.join(",") || "any"}${sources.length ? ` from ${sources.join(",")}` : ""}`;
|
|
72
|
-
const block = new Block();
|
|
73
|
-
for (const each of protocols) block.set(`${each}-portrange`, ranges.join(" "));
|
|
74
|
-
return { name, block };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (protocol === "icmp") {
|
|
78
|
-
const kind = family === "ipv4" ? "ICMP" : "ICMP6";
|
|
79
|
-
return { name: kind.toLowerCase(), block: new Block().set("protocol", kind).set("icmptype", UNSET) };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const number = PROTOCOL_NUMBER[protocol];
|
|
83
|
-
return { name: `protocol/${number}`, block: new Block().set("protocol", "IP").set("protocol-number", number) };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** A rule the platform keeps without being told: replies to a session it let through, and packets that belong to one, pass on their own. */
|
|
87
|
-
export function isImplied(rule: FilterRule<string, string>): boolean {
|
|
88
|
-
const state = rule.match?.state;
|
|
89
|
-
return (
|
|
90
|
-
rule.action === "accept" &&
|
|
91
|
-
state !== undefined &&
|
|
92
|
-
state.length > 0 &&
|
|
93
|
-
state.every((one) => one === "established" || one === "related")
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** The address objects, groups and services rules name, each created once. */
|
|
98
|
-
class Objects {
|
|
99
|
-
constructor(
|
|
100
|
-
private readonly root: Block,
|
|
101
|
-
private readonly device: Device
|
|
102
|
-
) {}
|
|
103
|
-
|
|
104
|
-
/** The address a rule names on one side: one prefix, a set, or everything. */
|
|
105
|
-
address(prefix: string | undefined, set: string | undefined, family: Family): string {
|
|
106
|
-
if (set) return this.group(set, this.device.firewall?.address_sets?.[set] ?? [], family);
|
|
107
|
-
if (!prefix) return "all";
|
|
108
|
-
|
|
109
|
-
return this.prefix(prefix, family);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private prefix(prefix: string, family: Family): string {
|
|
113
|
-
const name = network(prefix);
|
|
114
|
-
|
|
115
|
-
if (family === "ipv4") this.root.block("firewall address").entry(name).set("subnet", subnet(name));
|
|
116
|
-
else this.root.block("firewall address6").entry(name).set("ip6", name);
|
|
117
|
-
|
|
118
|
-
return name;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** A group of the members of one family, named once for both families. */
|
|
122
|
-
group(name: string, members: readonly string[], family: Family): string {
|
|
123
|
-
const names = members.filter((member) => familyOfPrefix(member) === family).map((member) => this.prefix(member, family));
|
|
124
|
-
|
|
125
|
-
const table = family === "ipv4" ? "firewall addrgrp" : "firewall addrgrp6";
|
|
126
|
-
this.root
|
|
127
|
-
.block(table)
|
|
128
|
-
.entry(name)
|
|
129
|
-
.set("member", quoteAll([...new Set(names)]));
|
|
130
|
-
return name;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** A pool of one address, for source NAT to it. */
|
|
134
|
-
pool(address: string): void {
|
|
135
|
-
this.root.block("firewall ippool").entry(address).set("startip", address).set("endip", address);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
service(match: FilterMatch<string, string> | undefined, family: Family): string {
|
|
139
|
-
const { name, block } = serviceOf(match, family);
|
|
140
|
-
|
|
141
|
-
// Transparent mode deletes the factory's services, `ALL` with them, so every protocol is a service of Circuit's own there.
|
|
142
|
-
if (name === "ALL" && isTransparent(this.device)) {
|
|
143
|
-
this.root.block("firewall service custom").entry(ALL_IN_TRANSPARENT).set("protocol", "IP");
|
|
144
|
-
return ALL_IN_TRANSPARENT;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!block) return name;
|
|
148
|
-
|
|
149
|
-
const entry = this.root.block("firewall service custom").entry(name);
|
|
150
|
-
for (const [key, value] of block.settings) entry.set(key, value);
|
|
151
|
-
return name;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/** Packet filtering, who reaches the device, NAT and stateless ACLs. */
|
|
156
|
-
export function firewall(root: Block, device: Device): void {
|
|
157
|
-
const fw = device.firewall;
|
|
158
|
-
const objects = new Objects(root, device);
|
|
159
|
-
|
|
160
|
-
const forward = fw?.filter?.forward;
|
|
161
|
-
const policies = { ipv4: 0, ipv6: 0 };
|
|
162
|
-
|
|
163
|
-
for (const rule of forward?.rules ?? []) {
|
|
164
|
-
if (isImplied(rule)) continue;
|
|
165
|
-
|
|
166
|
-
for (const family of familiesOf(device, rule.match)) {
|
|
167
|
-
const table = root.block(family === "ipv4" ? "firewall policy" : "firewall policy6");
|
|
168
|
-
policy(table.entry(++policies[family]), objects, device, rule, family);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// A packet no rule matched is accepted unless the chain drops it; the platform's own last word is deny.
|
|
173
|
-
if (forward?.default !== "drop") {
|
|
174
|
-
for (const family of ["ipv4", "ipv6"] as const) {
|
|
175
|
-
const table = root.block(family === "ipv4" ? "firewall policy" : "firewall policy6");
|
|
176
|
-
policy(table.entry(++policies[family]), objects, device, { action: "accept" }, family);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const localIns = { ipv4: 0, ipv6: 0 };
|
|
181
|
-
const localInEntry = (family: Family) =>
|
|
182
|
-
root.block(family === "ipv4" ? "firewall local-in-policy" : "firewall local-in-policy6").entry(++localIns[family]);
|
|
183
|
-
|
|
184
|
-
management(localInEntry, objects, device);
|
|
185
|
-
|
|
186
|
-
const input = fw?.filter?.input;
|
|
187
|
-
|
|
188
|
-
for (const rule of input?.rules ?? []) {
|
|
189
|
-
if (isImplied(rule)) continue;
|
|
190
|
-
|
|
191
|
-
for (const family of familiesOf(device, rule.match)) localIn(localInEntry(family), objects, device, rule, family);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (input?.default === "drop") {
|
|
195
|
-
for (const family of ["ipv4", "ipv6"] as const) localIn(localInEntry(family), objects, device, { action: "drop" }, family);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
(fw?.nat?.source ?? []).forEach((rule, index) =>
|
|
199
|
-
sourceNat(root.block("firewall central-snat-map").entry(index + 1), objects, device, rule)
|
|
200
|
-
);
|
|
201
|
-
(fw?.nat?.destination ?? []).forEach((rule, index) =>
|
|
202
|
-
destinationNat(root.block("firewall vip").entry(`dnat-${index + 1}`), device, rule)
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
acls(root, objects, device);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Who reaches the device's own services: each service with sources to allow accepts them and refuses everyone else, ahead of the input chain. Services sharing their sources share the policies, named after `management.allow` or the service.
|
|
210
|
-
*/
|
|
211
|
-
function management(entry: (family: Family) => Block, objects: Objects, device: Device): void {
|
|
212
|
-
const config = device.management ?? {};
|
|
213
|
-
const guards = new Map<string, { sources: readonly string[]; services: string[] }>();
|
|
214
|
-
|
|
215
|
-
for (const [service, listener] of Object.entries(SERVICES) as [keyof typeof SERVICES, (typeof SERVICES)[keyof typeof SERVICES]][]) {
|
|
216
|
-
const settings = config[service];
|
|
217
|
-
const sources = settings?.allow ?? config.allow;
|
|
218
|
-
if (!settings || !sources?.length) continue;
|
|
219
|
-
|
|
220
|
-
const name = settings.allow ? `circuit-${service}` : "circuit-management";
|
|
221
|
-
const guard = guards.get(name) ?? { sources, services: [] };
|
|
222
|
-
guard.services.push(objects.service({ protocol: listener.protocol, dst_port: settings.port ?? listener.port }, "ipv4"));
|
|
223
|
-
guards.set(name, guard);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
for (const [name, guard] of guards) {
|
|
227
|
-
for (const family of ["ipv4", "ipv6"] as const) {
|
|
228
|
-
const services = quoteAll(guard.services);
|
|
229
|
-
|
|
230
|
-
if (guard.sources.some((source) => familyOfPrefix(source) === family)) {
|
|
231
|
-
const accept = entry(family);
|
|
232
|
-
accept.set("intf", quote("any"));
|
|
233
|
-
accept.set("srcaddr", quote(objects.group(name, guard.sources, family)));
|
|
234
|
-
accept.set("dstaddr", quote("all"));
|
|
235
|
-
accept.set("action", "accept");
|
|
236
|
-
accept.set("service", services);
|
|
237
|
-
accept.set("schedule", quote("always"));
|
|
238
|
-
accept.set("comments", quote("Management from allowed sources"));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const deny = entry(family);
|
|
242
|
-
deny.set("intf", quote("any"));
|
|
243
|
-
deny.set("srcaddr", quote("all"));
|
|
244
|
-
deny.set("dstaddr", quote("all"));
|
|
245
|
-
deny.set("action", "deny");
|
|
246
|
-
deny.set("service", services);
|
|
247
|
-
deny.set("schedule", quote("always"));
|
|
248
|
-
deny.set("comments", quote("Management from anywhere else"));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/** A policy for traffic through the device. A rejection answers the sender; the platform's deny is silent otherwise. */
|
|
254
|
-
function policy(entry: Block, objects: Objects, device: Device, rule: FilterRule<string, string>, family: Family): void {
|
|
255
|
-
const match = rule.match;
|
|
256
|
-
|
|
257
|
-
entry.set("srcintf", quote(match?.in_interface ? nameOf(device, match.in_interface) : "any"));
|
|
258
|
-
entry.set("dstintf", quote(match?.out_interface ? nameOf(device, match.out_interface) : "any"));
|
|
259
|
-
entry.set("srcaddr", quote(objects.address(match?.src, match?.src_set, family)));
|
|
260
|
-
entry.set("dstaddr", quote(objects.address(match?.dst, match?.dst_set, family)));
|
|
261
|
-
entry.set("action", rule.action === "accept" ? "accept" : "deny");
|
|
262
|
-
entry.set("schedule", quote("always"));
|
|
263
|
-
entry.set("service", quote(objects.service(match, family)));
|
|
264
|
-
|
|
265
|
-
// The device gives a deny policy `logtraffic disable` of its own accord.
|
|
266
|
-
if (rule.action !== "accept") entry.set("logtraffic", "disable");
|
|
267
|
-
if (rule.action === "accept" && !device.hardware_offload && !rule.offload) entry.set("auto-asic-offload", "disable");
|
|
268
|
-
if (rule.description) entry.set("comments", quote(rule.description));
|
|
269
|
-
if (rule.action === "reject") entry.set("send-deny-packet", "enable");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** A local-in policy, for traffic to the device itself. */
|
|
273
|
-
function localIn(entry: Block, objects: Objects, device: Device, rule: FilterRule<string, string>, family: Family): void {
|
|
274
|
-
const match = rule.match;
|
|
275
|
-
|
|
276
|
-
entry.set("intf", quote(match?.in_interface ? nameOf(device, match.in_interface) : "any"));
|
|
277
|
-
entry.set("srcaddr", quote(objects.address(match?.src, match?.src_set, family)));
|
|
278
|
-
entry.set("dstaddr", quote(objects.address(match?.dst, match?.dst_set, family)));
|
|
279
|
-
entry.set("action", rule.action === "accept" ? "accept" : "deny");
|
|
280
|
-
entry.set("service", quote(objects.service(match, family)));
|
|
281
|
-
entry.set("schedule", quote("always"));
|
|
282
|
-
if (rule.description) entry.set("comments", quote(rule.description));
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/** Source NAT: to the outgoing interface's address, or to a pool of one address. */
|
|
286
|
-
function sourceNat(entry: Block, objects: Objects, device: Device, rule: NatRule<string, string>): void {
|
|
287
|
-
const match = rule.match;
|
|
288
|
-
|
|
289
|
-
entry.set("orig-addr", quote(objects.address(match?.src, match?.src_set, "ipv4")));
|
|
290
|
-
entry.set("srcintf", quote(match?.in_interface ? nameOf(device, match.in_interface) : "any"));
|
|
291
|
-
entry.set("dst-addr", quote(objects.address(match?.dst, match?.dst_set, "ipv4")));
|
|
292
|
-
entry.set("dstintf", quote(match?.out_interface ? nameOf(device, match.out_interface) : "any"));
|
|
293
|
-
if (match?.protocol) entry.set("protocol", PROTOCOL_NUMBER[match.protocol]);
|
|
294
|
-
|
|
295
|
-
const ports = portList(match?.src_port);
|
|
296
|
-
|
|
297
|
-
if (rule.action === "snat") {
|
|
298
|
-
objects.pool(rule.to);
|
|
299
|
-
entry.set("nat-ippool", quote(rule.to));
|
|
300
|
-
// A pool keeps the source port only when told which.
|
|
301
|
-
if (ports.length) entry.set("orig-port", ports[0]!).set("nat-port", rule.to_port ?? ports[0]!);
|
|
302
|
-
} else if (ports.length) {
|
|
303
|
-
entry.set("orig-port", ports[0]!);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (rule.description) entry.set("comments", quote(rule.description));
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/** Destination NAT, as a virtual IP: the address it answers on, and the port it forwards when the rule names one. */
|
|
310
|
-
function destinationNat(entry: Block, device: Device, rule: NatRule<string, string>): void {
|
|
311
|
-
if (rule.action !== "dnat") return;
|
|
312
|
-
|
|
313
|
-
const match = rule.match;
|
|
314
|
-
if (rule.description) entry.set("comment", quote(rule.description));
|
|
315
|
-
if (match?.dst) entry.set("extip", host(withLength(match.dst)));
|
|
316
|
-
entry.set("extintf", quote(match?.in_interface ? nameOf(device, match.in_interface) : "any"));
|
|
317
|
-
|
|
318
|
-
const sources = match?.src ? [match.src] : match?.src_set ? (device.firewall?.address_sets?.[match.src_set] ?? []) : [];
|
|
319
|
-
if (sources.length) entry.set("src-filter", quoteAll(sources.map(network)));
|
|
320
|
-
|
|
321
|
-
const ports = portList(match?.dst_port);
|
|
322
|
-
if (ports.length) entry.set("portforward", "enable");
|
|
323
|
-
entry.set("mappedip", quote(rule.to));
|
|
324
|
-
|
|
325
|
-
if (ports.length) {
|
|
326
|
-
if (match?.protocol === "udp") entry.set("protocol", "udp");
|
|
327
|
-
entry.set("extport", ports[0]!);
|
|
328
|
-
entry.set("mappedport", rule.to_port ?? ports[0]!);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* A port's ACL as the platform's stateless ACLs, which only drop: each rule that drops becomes one, of each family it applies to. What no rule drops passes, so the rules that accept after the last drop are already there; `unsupported()` refuses one before a drop.
|
|
334
|
-
*/
|
|
335
|
-
function acls(root: Block, objects: Objects, device: Device): void {
|
|
336
|
-
const counters = { ipv4: 0, ipv6: 0 };
|
|
337
|
-
|
|
338
|
-
for (const [port, settings] of Object.entries(device.ports ?? {})) {
|
|
339
|
-
if (!settings?.acl) continue;
|
|
340
|
-
|
|
341
|
-
for (const rule of device.acls?.[settings.acl] ?? []) {
|
|
342
|
-
if (rule.action !== "drop") continue;
|
|
343
|
-
|
|
344
|
-
const match = rule.match as FilterMatch<string, string> | undefined;
|
|
345
|
-
|
|
346
|
-
for (const family of familiesOf(device, match)) {
|
|
347
|
-
const entry = root.block(family === "ipv4" ? "firewall acl" : "firewall acl6").entry(++counters[family]);
|
|
348
|
-
if (rule.description) entry.set("comments", quote(rule.description));
|
|
349
|
-
entry.set("interface", quote(nameOf(device, port)));
|
|
350
|
-
entry.set("srcaddr", quote(objects.address(match?.src, undefined, family)));
|
|
351
|
-
entry.set("dstaddr", quote(objects.address(match?.dst, undefined, family)));
|
|
352
|
-
entry.set("service", quote(objects.service(match, family)));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/** Whether a port's ACL drops before it accepts: an accept before a drop is an exception the platform's ACLs cannot hold. */
|
|
359
|
-
export function acceptsBeforeDrop(rules: readonly AclRule<string>[]): boolean {
|
|
360
|
-
const lastDrop = rules.map((rule) => rule.action).lastIndexOf("drop");
|
|
361
|
-
return rules.slice(0, Math.max(0, lastDrop)).some((rule) => rule.action === "accept");
|
|
362
|
-
}
|