@takodotid/circuit 0.39.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +305 -29
- package/dist/helpers/index.js +129 -147
- package/dist/types/core/validator.d.ts +2 -0
- package/dist/types/helpers/bgp.d.ts +53 -9
- package/dist/types/helpers/checks.d.ts +4 -0
- package/dist/types/helpers/index.d.ts +2 -2
- package/dist/types/helpers/upstreams.d.ts +115 -181
- package/dist/types/platforms/derive.d.ts +1 -1
- package/dist/types/platforms/fortios/firewall.d.ts +2 -0
- package/dist/types/platforms/fortios/index.d.ts +7 -1
- package/dist/types/platforms/fortios/render.d.ts +2 -0
- package/dist/types/platforms/fortios/vdoms.d.ts +50 -0
- package/dist/types/platforms/routeros/render.d.ts +7 -1
- package/dist/types/schema/device.d.ts +21 -1
- package/dist/types/schema/firewall.d.ts +5 -1
- package/dist/types/schema/policy.d.ts +15 -6
- package/package.json +1 -1
- package/src/core/validator.ts +35 -2
- package/src/helpers/bgp.ts +117 -67
- package/src/helpers/checks.ts +23 -0
- package/src/helpers/index.ts +2 -2
- package/src/helpers/upstreams.ts +157 -271
- package/src/platforms/derive.ts +3 -1
- package/src/platforms/fortios/firewall.ts +9 -0
- package/src/platforms/fortios/index.ts +90 -28
- package/src/platforms/fortios/render.ts +27 -5
- package/src/platforms/fortios/vdoms.ts +217 -0
- package/src/platforms/routeros/TRAPS.md +1 -0
- package/src/platforms/routeros/render.ts +36 -3
- package/src/schema/device.ts +22 -1
- package/src/schema/firewall.ts +5 -1
- package/src/schema/policy.ts +17 -6
|
@@ -1,48 +1,67 @@
|
|
|
1
|
-
import type { Exchange as ExchangeLan, Family, FilterRule, Gre, InterfaceAddress, IP,
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Exchange as ExchangeLan, Family, FilterRule, Gre, InterfaceAddress, IP, Neighbor, NeighborSettings, PolicyRule, Port, Secret, Vlan, VlanInterface } from "../schema/index.js";
|
|
2
|
+
/** A route policy an upstream's sessions use: its name on the device, and its rules, in order. */
|
|
3
|
+
export type UpstreamPolicy = {
|
|
4
|
+
/** The policy's name, as the device carries it. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Its rules, in order. Nothing is added to them unless `mapImports` or `mapExports` adds it. */
|
|
7
|
+
rules: readonly PolicyRule<string, string>[];
|
|
8
|
+
};
|
|
9
|
+
/** A VLAN by its name and its ID. */
|
|
10
|
+
export type NamedVlan = {
|
|
11
|
+
name: string;
|
|
12
|
+
id: number;
|
|
13
|
+
};
|
|
14
|
+
/** One network we exchange routes with: a transit we buy, an exchange's route servers, or a peer. Every name it puts on a device is written here. */
|
|
4
15
|
export type Upstream = {
|
|
5
16
|
/** Its name as people write it, for descriptions: `Transit A`. The key when absent. */
|
|
6
17
|
name?: string;
|
|
7
18
|
asn: number;
|
|
8
19
|
/** What it is to us: `provider` for a transit, `rs` for an exchange's route servers, `peer` for a direct session. */
|
|
9
20
|
role: "provider" | "rs" | "peer";
|
|
10
|
-
/** Where it lands on our side: a VLAN,
|
|
21
|
+
/** Where it lands on our side: a VLAN, the router's interface on it, and the switch port it arrives on. A peer over a tunnel, or over an exchange's VLAN, has none. */
|
|
11
22
|
handoff?: {
|
|
12
|
-
/** The VLAN
|
|
13
|
-
vlan:
|
|
14
|
-
/** It sends its VLAN tagged: its port gets
|
|
15
|
-
native?:
|
|
16
|
-
/**
|
|
17
|
-
|
|
23
|
+
/** The VLAN it arrives on. */
|
|
24
|
+
vlan: NamedVlan;
|
|
25
|
+
/** It sends its VLAN tagged: its switch port gets this native VLAN, which nothing else is in. Untagged when absent. */
|
|
26
|
+
native?: NamedVlan;
|
|
27
|
+
/** The router's VLAN interface on it. */
|
|
28
|
+
interface: string;
|
|
18
29
|
/** Our addresses on it. `{ address, peer }` for a point-to-point address whose far end is outside its own prefix. */
|
|
19
30
|
addresses: readonly (InterfaceAddress | {
|
|
20
31
|
address: InterfaceAddress;
|
|
21
32
|
peer: IP;
|
|
22
33
|
})[];
|
|
23
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* The switch port it arrives on. LLDP and spanning tree are off there unless `lldp` or `stp` says otherwise: an upstream's port faces someone else's network, which should neither learn ours nor join our spanning tree.
|
|
36
|
+
*/
|
|
24
37
|
port?: {
|
|
25
38
|
device: string;
|
|
26
39
|
port: string;
|
|
27
40
|
storm_control?: Port<string, string, string, string>["storm_control"];
|
|
41
|
+
/** LLDP on the port. Off when absent. */
|
|
42
|
+
lldp?: boolean;
|
|
43
|
+
/** Spanning tree on the port. Off when absent. */
|
|
44
|
+
stp?: boolean;
|
|
28
45
|
};
|
|
29
46
|
/** An exchange's LAN, for `circuit peeringdb`. */
|
|
30
47
|
exchange?: ExchangeLan;
|
|
31
48
|
};
|
|
32
|
-
/** A GRE tunnel to it
|
|
49
|
+
/** A GRE tunnel to it, and the router's interface for it. */
|
|
33
50
|
tunnel?: {
|
|
51
|
+
interface: string;
|
|
34
52
|
local: IP;
|
|
35
53
|
remote: IP;
|
|
36
54
|
addresses: readonly InterfaceAddress[];
|
|
37
55
|
mtu?: number;
|
|
38
|
-
number?: number;
|
|
39
56
|
};
|
|
40
57
|
/** Our BGP sessions with it, by name: its address, and ours when it is not the interface's. */
|
|
41
58
|
sessions: Readonly<Record<string, {
|
|
42
59
|
address: IP;
|
|
43
60
|
local_address?: IP;
|
|
44
61
|
}>>;
|
|
45
|
-
/**
|
|
62
|
+
/** A BGP group its sessions share, by this name: the group holds the AS, role, policies and multihop settings, and each session names it. Each session holds them itself when absent. */
|
|
63
|
+
group?: string;
|
|
64
|
+
/** The sessions are more than one hop away, from this address of ours. */
|
|
46
65
|
multihop?: {
|
|
47
66
|
local_address: IP;
|
|
48
67
|
families?: readonly Family[];
|
|
@@ -53,185 +72,100 @@ export type Upstream = {
|
|
|
53
72
|
ipv4?: number;
|
|
54
73
|
ipv6?: number;
|
|
55
74
|
};
|
|
56
|
-
/** What we take. Nothing when absent. */
|
|
57
|
-
import?:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
description?: string;
|
|
62
|
-
};
|
|
63
|
-
/** Its routes, through `Bgp.learn`: the `before` rules, sanity, this preference, the tag. Only the default when absent. */
|
|
64
|
-
learn?: {
|
|
65
|
-
local_pref?: number;
|
|
66
|
-
before?: readonly PolicyRule<never, never>[];
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
/** Rules of yours in its export, between the community actions and what we announce, such as a prepend. */
|
|
70
|
-
export?: readonly PolicyRule<never, never>[];
|
|
71
|
-
/** What we announce to it. Everything else is rejected. */
|
|
72
|
-
announce: readonly Announced[];
|
|
73
|
-
/** Drop RFC 1918 sources arriving from it. `privateDrops()` lists the VLANs. */
|
|
75
|
+
/** What we take, as its policy. Nothing when absent. */
|
|
76
|
+
import?: UpstreamPolicy;
|
|
77
|
+
/** What we send it, as its policy. */
|
|
78
|
+
export: UpstreamPolicy;
|
|
79
|
+
/** Its VLAN is listed by `privateDrops()`, for an ACL that drops RFC 1918 sources arriving from it. Nothing else is done with it. */
|
|
74
80
|
drop_private?: true;
|
|
75
81
|
};
|
|
76
|
-
/** Upstreams by key.
|
|
82
|
+
/** Upstreams by key. */
|
|
77
83
|
export type UpstreamCatalog = Readonly<Record<string, Upstream>>;
|
|
78
|
-
/**
|
|
79
|
-
type
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
readonly
|
|
84
|
-
readonly peer?: Pattern;
|
|
84
|
+
/** What every upstream's policies share, written once: plain functions over each policy's rules. */
|
|
85
|
+
export type UpstreamOptions = {
|
|
86
|
+
/** Each import's rules as the device gets them, from the rules the upstream writes. The rules as written when absent. */
|
|
87
|
+
mapImports?: (rules: readonly PolicyRule<string, string>[], key: string, upstream: Upstream) => readonly PolicyRule<string, string>[];
|
|
88
|
+
/** Each export's rules as the device gets them, from the rules the upstream writes. The rules as written when absent. */
|
|
89
|
+
mapExports?: (rules: readonly PolicyRule<string, string>[], key: string, upstream: Upstream) => readonly PolicyRule<string, string>[];
|
|
85
90
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/** The native VLAN of a handoff sent tagged. `{key}-native` when absent. */
|
|
91
|
-
native?: Pattern;
|
|
92
|
-
/** The router's interface: per role, and `tunnel` for one reached over a tunnel. `{key}` when absent. */
|
|
93
|
-
interface?: Pattern | {
|
|
94
|
-
readonly provider?: Pattern;
|
|
95
|
-
readonly rs?: Pattern;
|
|
96
|
-
readonly peer?: Pattern;
|
|
97
|
-
readonly tunnel?: Pattern;
|
|
98
|
-
};
|
|
99
|
-
/** The import policy. `{KEY}-IN` when absent. */
|
|
100
|
-
import?: Pattern;
|
|
101
|
-
/** The export policy. `{KEY}-OUT` when absent. */
|
|
102
|
-
export?: Pattern;
|
|
103
|
-
/** The BGP group of an exchange's route servers or of multihop sessions. `{key}` when absent. */
|
|
104
|
-
group?: Pattern;
|
|
105
|
-
/** The description of a handoff's switch port. Its `name` when absent. No device names it, so it may be any function. */
|
|
106
|
-
port?: (key: string, upstream: Upstream) => string;
|
|
107
|
-
};
|
|
108
|
-
/** How each upstream's policies are written: shared by every upstream of a catalog. */
|
|
109
|
-
export type UpstreamPolicies<P extends string> = {
|
|
110
|
-
/** A policy holding `Bgp.sanity()` that each import calls. The sanity rules are written in each import when absent. */
|
|
111
|
-
sanity?: P;
|
|
112
|
-
/** Where a route came from, by upstream: a community or a rule, such as `communityScheme(...).tag(...)`. Nothing is tagged when absent. */
|
|
113
|
-
tag?: (key: string, upstream: Upstream) => StandardCommunity | LargeCommunity | PolicyRule<never, never>;
|
|
114
|
-
/** Rules first in each export, such as `communityScheme(...).actions(upstream.asn)`. */
|
|
115
|
-
actions?: (key: string, upstream: Upstream) => readonly PolicyRule<never, never>[];
|
|
116
|
-
/** The source address for routes of its family, one per family. */
|
|
117
|
-
sources?: readonly IP[];
|
|
118
|
-
};
|
|
119
|
-
/** The names a device can refer to, and their patterns when the project gives none. */
|
|
120
|
-
declare const DEFAULTS: {
|
|
121
|
-
readonly vlan: "{key}";
|
|
122
|
-
readonly native: "{key}-native";
|
|
123
|
-
readonly interface: "{key}";
|
|
124
|
-
readonly import: "{KEY}-IN";
|
|
125
|
-
readonly export: "{KEY}-OUT";
|
|
126
|
-
readonly group: "{key}";
|
|
127
|
-
};
|
|
128
|
-
type Defaults = typeof DEFAULTS;
|
|
129
|
-
type Field = keyof Defaults;
|
|
130
|
-
type Key<C> = keyof C & string;
|
|
131
|
-
/** The upstreams that land on a VLAN of ours. */
|
|
132
|
-
type Landed<C> = {
|
|
133
|
-
[K in Key<C>]: C[K] extends {
|
|
134
|
-
handoff: object;
|
|
135
|
-
} ? K : never;
|
|
136
|
-
}[Key<C>];
|
|
137
|
-
/** The upstreams that send their VLAN tagged. */
|
|
138
|
-
type Tagged<C> = {
|
|
139
|
-
[K in Key<C>]: C[K] extends {
|
|
91
|
+
type Values<T> = T[keyof T];
|
|
92
|
+
/** Each VLAN name the catalog writes. */
|
|
93
|
+
type VlanName<C> = Values<{
|
|
94
|
+
[K in keyof C]: C[K] extends {
|
|
140
95
|
handoff: {
|
|
141
|
-
|
|
96
|
+
vlan: {
|
|
97
|
+
name: infer N extends string;
|
|
98
|
+
};
|
|
142
99
|
};
|
|
143
|
-
} ?
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
type
|
|
147
|
-
[K in
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
};
|
|
190
|
-
} ? N : number;
|
|
191
|
-
/** The name built for upstream `K`: its pattern with the key, the key in capitals and its number filled in. */
|
|
192
|
-
type Named<C, N, F extends Field, K extends Key<C>> = Replace<Replace<Replace<Chosen<PatternOf<N, F>, C[K], F>, "{key}", K>, "{KEY}", Uppercase<K>>, "{number}", `${NumberOf<C[K]>}`>;
|
|
193
|
-
/** Each of these upstreams' names. */
|
|
194
|
-
type Names<C, N, F extends Field, Ks extends Key<C>> = {
|
|
195
|
-
[K in Ks]: Named<C, N, F, K>;
|
|
196
|
-
}[Ks];
|
|
197
|
-
/** The catalog as written, with any key the shape does not have refused, at every level. */
|
|
198
|
-
type Exact<S, T> = T extends readonly unknown[] | string | number | boolean | Secret | ((...args: never[]) => unknown) ? T : {
|
|
199
|
-
[K in keyof T]: K extends keyof S ? Exact<NonNullable<S[K]>, T[K]> : never;
|
|
200
|
-
};
|
|
201
|
-
/** The names as written, with a name or a role Upstreams does not make refused. */
|
|
202
|
-
type ExactNames<N> = {
|
|
203
|
-
[F in keyof N]: F extends keyof UpstreamNames ? N[F] extends string | ((...args: never[]) => unknown) ? N[F] : {
|
|
204
|
-
[R in keyof N[F]]: R extends "provider" | "rs" | "peer" | "tunnel" ? N[F][R] : never;
|
|
205
|
-
} : never;
|
|
206
|
-
};
|
|
100
|
+
} ? N : never;
|
|
101
|
+
}>;
|
|
102
|
+
/** Each native VLAN name. */
|
|
103
|
+
type NativeName<C> = Values<{
|
|
104
|
+
[K in keyof C]: C[K] extends {
|
|
105
|
+
handoff: {
|
|
106
|
+
native: {
|
|
107
|
+
name: infer N extends string;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
} ? N : never;
|
|
111
|
+
}>;
|
|
112
|
+
/** Each router interface on a handoff. */
|
|
113
|
+
type HandoffInterface<C> = Values<{
|
|
114
|
+
[K in keyof C]: C[K] extends {
|
|
115
|
+
handoff: {
|
|
116
|
+
interface: infer N extends string;
|
|
117
|
+
};
|
|
118
|
+
} ? N : never;
|
|
119
|
+
}>;
|
|
120
|
+
/** Each router interface for a tunnel. */
|
|
121
|
+
type TunnelInterface<C> = Values<{
|
|
122
|
+
[K in keyof C]: C[K] extends {
|
|
123
|
+
tunnel: {
|
|
124
|
+
interface: infer N extends string;
|
|
125
|
+
};
|
|
126
|
+
} ? N : never;
|
|
127
|
+
}>;
|
|
128
|
+
/** Each policy name. */
|
|
129
|
+
type PolicyName<C> = Values<{
|
|
130
|
+
[K in keyof C]: (C[K] extends {
|
|
131
|
+
import: {
|
|
132
|
+
name: infer N extends string;
|
|
133
|
+
};
|
|
134
|
+
} ? N : never) | (C[K] extends {
|
|
135
|
+
export: {
|
|
136
|
+
name: infer N extends string;
|
|
137
|
+
};
|
|
138
|
+
} ? N : never);
|
|
139
|
+
}>;
|
|
140
|
+
/** Each BGP group name. */
|
|
141
|
+
type GroupName<C> = Values<{
|
|
142
|
+
[K in keyof C]: C[K] extends {
|
|
143
|
+
group: infer N extends string;
|
|
144
|
+
} ? N : never;
|
|
145
|
+
}>;
|
|
207
146
|
/** A network's upstreams written once, and each device's part built from them. */
|
|
208
147
|
export declare class Upstreams {
|
|
209
148
|
/**
|
|
210
|
-
* Each upstream written once: its AS, where it lands, its sessions, limits and
|
|
211
|
-
*
|
|
212
|
-
* - VLAN `<key>`, and `<key>-native` for one that sends its VLAN tagged. The router's interface is `<key>` too, a VLAN interface or a GRE tunnel.
|
|
213
|
-
* - Policies `<KEY>-IN` and `<KEY>-OUT`; a BGP group `<key>` for an exchange's route servers or multihop sessions.
|
|
149
|
+
* Each upstream written once: its AS, where it lands, its sessions, limits and policies, and every name it puts on a device. Everything a device needs is built from it:
|
|
214
150
|
*
|
|
215
|
-
*
|
|
151
|
+
* - On the router: `vlans()`, `interfaces()`, `uplinks()`, `groups()`, `neighbors()`, `policies()`, `tunnelRules()`, `addresses()`.
|
|
152
|
+
* - On a switch: `vlans(device)`, `ports(device)`, `vlansOn(device)`.
|
|
153
|
+
* - For checks and ACLs: `untrusted()`, `natives()`, `privateDrops()`.
|
|
216
154
|
*
|
|
217
|
-
*
|
|
155
|
+
* An import or export is the rules the upstream writes, in order, passed through `mapImports` or `mapExports` when given: what every upstream shares, such as a call to a sanity policy or the community actions, goes there. Nothing else is added. Lists follow the catalog's order, where order counts on a device. A key the shape doesn't have is refused by the type, by its name.
|
|
218
156
|
*/
|
|
219
|
-
static define<const C extends UpstreamCatalog
|
|
220
|
-
[K in
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
policies: <const P extends string = never>(options?: UpstreamPolicies<P>) => Record<Names<C, N, "export", Key<C>> | Names<C, N, "import", Importing<C>>, PolicyRule<P, never>[]>;
|
|
232
|
-
untrusted: () => (Names<C, N, "native", Tagged<C>> | Names<C, N, "vlan", Landed<C>>)[];
|
|
233
|
-
natives: () => Names<C, N, "native", Tagged<C>>[];
|
|
234
|
-
privateDrops: () => Names<C, N, "vlan", Landed<C>>[];
|
|
157
|
+
static define<const C extends UpstreamCatalog>(catalog: C & UpstreamCatalog, options?: UpstreamOptions): {
|
|
158
|
+
vlans: (device?: string) => { [K in VlanName<C> | NativeName<C>]: Vlan; };
|
|
159
|
+
vlansOn: (device: string) => VlanName<C>[];
|
|
160
|
+
ports: (device: string) => Record<string, Port<NativeName<C> | VlanName<C>, never, never, never>>;
|
|
161
|
+
interfaces: () => { [K in HandoffInterface<C>]: VlanInterface<VlanName<C>, never>; } & { [K in TunnelInterface<C>]: Gre<never>; };
|
|
162
|
+
uplinks: () => HandoffInterface<C>[];
|
|
163
|
+
groups: () => Record<GroupName<C>, NeighborSettings<PolicyName<C>>>;
|
|
164
|
+
neighbors: () => Record<string, Neighbor<PolicyName<C>, GroupName<C>>>;
|
|
165
|
+
policies: () => Record<PolicyName<C>, PolicyRule<never, never>[]>;
|
|
166
|
+
untrusted: () => (NativeName<C> | VlanName<C>)[];
|
|
167
|
+
natives: () => NativeName<C>[];
|
|
168
|
+
privateDrops: () => VlanName<C>[];
|
|
235
169
|
tunnelRules: () => FilterRule<never, never>[];
|
|
236
170
|
addresses: () => IP[];
|
|
237
171
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Device } from "../schema/index.js";
|
|
2
2
|
/** A feature a platform renders once proved on one of its devices. */
|
|
3
|
-
export type ProvedFeature = "bfd-timers" | "static-route-bfd" | "multihop-bfd" | "alarm-severity" | "mac-limit" | "ethertype-filter";
|
|
3
|
+
export type ProvedFeature = "bfd-timers" | "static-route-bfd" | "multihop-bfd" | "alarm-severity" | "mac-limit" | "ethertype-filter" | "management-vdom";
|
|
4
4
|
export declare class Derive {
|
|
5
5
|
/** Each LAG's number: its own `id`, or its position among the device's LAGs. */
|
|
6
6
|
static lagIds(device: Device): Map<string, number>;
|
|
@@ -20,6 +20,8 @@ export declare class FortiOsFirewall {
|
|
|
20
20
|
};
|
|
21
21
|
/** 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. */
|
|
22
22
|
static isImplied(rule: FilterRule<string, string>): boolean;
|
|
23
|
+
/** Who reaches the device's own services, alone: for a management VDOM, which routes no traffic of its own. */
|
|
24
|
+
static managementOnly(root: FortiOsBlock, device: Device): void;
|
|
23
25
|
/** Packet filtering, who reaches the device, NAT and stateless ACLs. */
|
|
24
26
|
static render(root: FortiOsBlock, device: Device): void;
|
|
25
27
|
/**
|
|
@@ -92,12 +92,18 @@ export declare class FortiOs extends Platform {
|
|
|
92
92
|
private static consoleSession;
|
|
93
93
|
/** Log in, open the CLI, and run `work` in it: through the console when asked, else over SSH. */
|
|
94
94
|
private static withCli;
|
|
95
|
-
/** Refuse a device on a release this adapter was not proved on, before anything is read or sent. */
|
|
95
|
+
/** Refuse a device on a release this adapter was not proved on, before anything is read or sent. Gives what `get system status` printed. */
|
|
96
96
|
private static checkVersion;
|
|
97
|
+
/** Run `work` among the device's own settings: inside `config global` on a device with VDOMs, as it is on one without. */
|
|
98
|
+
private static inGlobal;
|
|
97
99
|
/** Secrets the device prints: a password or an SNMPv3 key as a cipher, a certificate's key encrypted, and the SNMP community in clear. None leave the read. */
|
|
98
100
|
private static scrub;
|
|
99
101
|
/** Every section Circuit manages, as the device holds it, credentials scrubbed. */
|
|
100
102
|
private static readConfig;
|
|
103
|
+
/**
|
|
104
|
+
* A device in multi-VDOM mode, read where each section lives: the device's own under `config global`, the rest in `root`, and a management VDOM's sections named as the render names them. A VDOM that does not exist yet is not entered, since `edit` would make it.
|
|
105
|
+
*/
|
|
106
|
+
private static readVdoms;
|
|
101
107
|
/** Leave every `config` and `edit` level, back to the top prompt. */
|
|
102
108
|
private static leave;
|
|
103
109
|
/** Send the lines that set how the device saves changes. */
|
|
@@ -42,6 +42,8 @@ export declare class FortiOsRender {
|
|
|
42
42
|
private static allowAccess;
|
|
43
43
|
/** The whole configuration the device should run, in the form `show` prints. */
|
|
44
44
|
static of(device: Device): string;
|
|
45
|
+
/** A management VDOM's routes, and who reaches the device's own services through it, as sections of their own after everything else. */
|
|
46
|
+
private static managementVdom;
|
|
45
47
|
/** A certificate with its key is the device's own; one without is only trusted. */
|
|
46
48
|
private static certificates;
|
|
47
49
|
private static global;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Device } from "../../schema/index.js";
|
|
2
|
+
import type { Plan } from "../platform.js";
|
|
3
|
+
import { FortiOsBlock } from "./tree.js";
|
|
4
|
+
/** Where a section lives in multi-VDOM mode: the device's own, or one VDOM's. */
|
|
5
|
+
type Scope = {
|
|
6
|
+
kind: "global";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "vdom";
|
|
9
|
+
vdom: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class FortiOsVdoms {
|
|
12
|
+
/** The VDOM everything is in unless named otherwise. */
|
|
13
|
+
static readonly ROOT = "root";
|
|
14
|
+
/**
|
|
15
|
+
* Sections under `config global` in multi-VDOM mode, from FortiOS 6.2's CLI reference: the device's identity, logins, interfaces, name resolution, time, SNMP, logging targets, flow export and session helpers. Every other section belongs to a VDOM. Not proved on a device yet.
|
|
16
|
+
*/
|
|
17
|
+
static readonly GLOBAL: Set<string>;
|
|
18
|
+
/** What a management VDOM holds: its routes, and who reaches the device's own services through it, with the objects those name. */
|
|
19
|
+
static readonly MANAGEMENT_SECTIONS: readonly ["firewall address", "firewall address6", "firewall addrgrp", "firewall addrgrp6", "firewall service custom", "router static", "router static6", "firewall local-in-policy", "firewall local-in-policy6"];
|
|
20
|
+
/** The management VDOM's name, when the device has one. */
|
|
21
|
+
static managementOf(device: Device): string | undefined;
|
|
22
|
+
/** The VDOM a port or interface is in, by Circuit's name. A port in a LAG is where its LAG is. */
|
|
23
|
+
static vdomOf(device: Device, name: string): string;
|
|
24
|
+
/** A management VDOM's section as the render and the read write it. */
|
|
25
|
+
static key(vdom: string, section: string): string;
|
|
26
|
+
/** A section's scope, and its name as the device writes it. */
|
|
27
|
+
static scopeOf(key: string): {
|
|
28
|
+
scope: Scope;
|
|
29
|
+
section: string;
|
|
30
|
+
};
|
|
31
|
+
/** Whether a configuration runs in multi-VDOM mode. */
|
|
32
|
+
static isMulti(tree: FortiOsBlock): boolean;
|
|
33
|
+
/** Lines sent in a scope: inside `config global`, or inside one VDOM. */
|
|
34
|
+
static wrap(scope: Scope, lines: readonly string[]): string[];
|
|
35
|
+
/** What `show` printed in a management VDOM, each top-level section named as the render names it. */
|
|
36
|
+
static named(vdom: string, text: string): string;
|
|
37
|
+
/** The VDOMs `diagnose sys vd list` names, such as `name=mgmt/mgmt index=1`. */
|
|
38
|
+
static listed(text: string): string[];
|
|
39
|
+
/**
|
|
40
|
+
* A plan for a device that may run in multi-VDOM mode. In one VDOM it is `planOne` as it is. In several, each scope is planned on its own and its steps sent in that scope: the global steps first, then each VDOM's, then the global removals, so an interface is gone only once no VDOM names it.
|
|
41
|
+
*
|
|
42
|
+
* Turning multi-VDOM mode on or off, and moving an interface into another VDOM, are left to an operator: the first moves everything into `root` and ends every session, and the second can cut the way in. A plan that needs either holds only the problem.
|
|
43
|
+
*/
|
|
44
|
+
static plan(desired: string, current: string, planOne: (desired: string, current: string) => Plan): Plan;
|
|
45
|
+
/** Interfaces the config puts in another VDOM than the device has them in. */
|
|
46
|
+
private static moved;
|
|
47
|
+
/** What a management VDOM can't hold yet, or a config with one can't have. */
|
|
48
|
+
static gaps(device: Device): string[];
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
@@ -164,8 +164,14 @@ export declare class RouterOsRender {
|
|
|
164
164
|
*/
|
|
165
165
|
private addressLists;
|
|
166
166
|
private firewall;
|
|
167
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Untracked prefixes skip connection tracking in the raw table, both ways, before a connection is ever made.
|
|
169
|
+
*
|
|
170
|
+
* An address NAT uses inside one stays tracked, by an `accept` in each direction before every `notrack`: a reply to it that skipped tracking would never be translated back.
|
|
171
|
+
*/
|
|
168
172
|
private untracked;
|
|
173
|
+
/** Every address NAT translates to or from: each translation's target, and the address a destination translation matches. Once each, in rule order. */
|
|
174
|
+
static natAddresses(device: Device): string[];
|
|
169
175
|
/**
|
|
170
176
|
* The families a rule applies to. An address in the rule decides it; otherwise it applies to both.
|
|
171
177
|
*
|
|
@@ -2,7 +2,7 @@ import type { IP, Secret } from "./common.js";
|
|
|
2
2
|
import type { AclRule, Firewall } from "./firewall.js";
|
|
3
3
|
import type { Interface, Port, Vlan } from "./interfaces.js";
|
|
4
4
|
import type { PrefixSet, RoutePolicy } from "./policy.js";
|
|
5
|
-
import type { Routing } from "./routing.js";
|
|
5
|
+
import type { Routing, StaticRoute } from "./routing.js";
|
|
6
6
|
import type { Certificate, DhcpRelay, DhcpServer, FlowExport, Management, System, User } from "./system.js";
|
|
7
7
|
/** How the framework reaches the device. */
|
|
8
8
|
export type Connection<U extends string> = {
|
|
@@ -39,6 +39,24 @@ export type Console = {
|
|
|
39
39
|
/** The console asks only for a password, with no name first. Circuit then types no name; `connection.user` still picks the password. */
|
|
40
40
|
passwordOnly?: true;
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Virtual domains, on FortiOS: the device's own traffic kept apart from the traffic it routes. Everything is in `root` when absent.
|
|
44
|
+
*
|
|
45
|
+
* Multi-VDOM mode is turned on by hand, not by a plan: it moves the whole configuration into `root` and ends every session.
|
|
46
|
+
*/
|
|
47
|
+
export type Vdoms<I extends string> = {
|
|
48
|
+
/**
|
|
49
|
+
* A VDOM for the device's own traffic: FortiGuard, NTP, DNS and logging leave from it, by its own routes, so they never follow the routed traffic's default route. Everything not listed here stays in `root`.
|
|
50
|
+
*/
|
|
51
|
+
management: {
|
|
52
|
+
/** Its name, such as `mgmt`. Not `root`. */
|
|
53
|
+
name: string;
|
|
54
|
+
/** The ports and interfaces in it, by name. A LAG's member ports go with the LAG. */
|
|
55
|
+
interfaces: readonly I[];
|
|
56
|
+
/** Its static routes, such as its own default route. */
|
|
57
|
+
static?: readonly StaticRoute<I, never>[];
|
|
58
|
+
};
|
|
59
|
+
};
|
|
42
60
|
/** A separate routing table, with the interfaces that route in it. */
|
|
43
61
|
export type Vrf = {
|
|
44
62
|
/** What the VRF is for. */
|
|
@@ -103,4 +121,6 @@ export type Device<Pl extends string = string, M extends string = string, N exte
|
|
|
103
121
|
};
|
|
104
122
|
/** Forward in hardware where the platform can. An interface may override it. */
|
|
105
123
|
hardware_offload?: boolean;
|
|
124
|
+
/** Virtual domains, on FortiOS only: a management VDOM apart from the routed traffic. */
|
|
125
|
+
vdoms?: Vdoms<NoInfer<I | N>>;
|
|
106
126
|
};
|
|
@@ -96,7 +96,11 @@ export type Firewall<I extends string, AS extends string> = {
|
|
|
96
96
|
};
|
|
97
97
|
/** Helpers that run. None when absent. */
|
|
98
98
|
helpers?: readonly Helper[];
|
|
99
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Traffic to and from these prefixes passes without connection tracking. A flood toward them cannot fill the connection table, and rules see its packets in the state `untracked`. Everything is tracked when absent.
|
|
101
|
+
*
|
|
102
|
+
* Each address NAT uses inside one of these prefixes (a source translation's `to`, a destination translation's `to` or matched `dst`) stays tracked: Circuit writes two accept rules for it, one for traffic to it and one for traffic from it, before the untracked rules. Without them, a reply to that address would skip tracking and never be translated back.
|
|
103
|
+
*/
|
|
100
104
|
untracked?: readonly Prefix[];
|
|
101
105
|
};
|
|
102
106
|
/** A stateless rule evaluated in hardware where traffic arrives, before routing. */
|
|
@@ -78,15 +78,22 @@ export type Sets = {
|
|
|
78
78
|
blackhole?: true;
|
|
79
79
|
};
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* A rule without `action` changes the route and moves on to the next rule.
|
|
81
|
+
* Run another policy at this point. A route it accepts or rejects stops there; any other route goes on to the next rule. A call holds nothing else: to call a policy for some routes only, put the match inside that policy.
|
|
84
82
|
*/
|
|
85
|
-
export type
|
|
83
|
+
export type CallRule<P extends string> = {
|
|
86
84
|
/** Shown beside the rule on the device. */
|
|
87
85
|
description?: string;
|
|
88
|
-
/**
|
|
89
|
-
call
|
|
86
|
+
/** The policy to run. */
|
|
87
|
+
call: P;
|
|
88
|
+
match?: never;
|
|
89
|
+
set?: never;
|
|
90
|
+
action?: never;
|
|
91
|
+
};
|
|
92
|
+
/** Change and decide on the routes that match. A rule without `action` changes the route and moves on to the next rule. */
|
|
93
|
+
export type MatchRule<S extends string> = {
|
|
94
|
+
/** Shown beside the rule on the device. */
|
|
95
|
+
description?: string;
|
|
96
|
+
call?: never;
|
|
90
97
|
/** Which routes the rule applies to. Every route when absent. */
|
|
91
98
|
match?: Match<S>;
|
|
92
99
|
/** What to change on a matching route. */
|
|
@@ -94,5 +101,7 @@ export type PolicyRule<P extends string, S extends string> = {
|
|
|
94
101
|
/** Accept or reject a matching route, ending the policy for it. */
|
|
95
102
|
action?: "accept" | "reject";
|
|
96
103
|
};
|
|
104
|
+
/** One step of a route policy: a call to another policy, or a rule that matches routes, changes them and may decide on them. */
|
|
105
|
+
export type PolicyRule<P extends string, S extends string> = CallRule<P> | MatchRule<S>;
|
|
97
106
|
/** Rules evaluated in order, first decision wins. A route no rule accepts is rejected. */
|
|
98
107
|
export type RoutePolicy<P extends string, S extends string> = readonly PolicyRule<P, S>[];
|
package/package.json
CHANGED