@highstate/common 0.14.2 → 0.16.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/{chunk-WFWXDYUX.js → chunk-X5BK6JSN.js} +877 -194
- package/dist/chunk-X5BK6JSN.js.map +1 -0
- package/dist/highstate.manifest.json +12 -2
- package/dist/index.js +1 -1
- package/dist/units/databases/etcd-patch/index.js +20 -0
- package/dist/units/databases/etcd-patch/index.js.map +1 -0
- package/dist/units/databases/existing-etcd/index.js +14 -0
- package/dist/units/databases/existing-etcd/index.js.map +1 -0
- package/dist/units/databases/existing-mariadb/index.js +2 -2
- package/dist/units/databases/existing-mariadb/index.js.map +1 -1
- package/dist/units/databases/existing-mongodb/index.js +2 -2
- package/dist/units/databases/existing-mongodb/index.js.map +1 -1
- package/dist/units/databases/existing-postgresql/index.js +2 -2
- package/dist/units/databases/existing-postgresql/index.js.map +1 -1
- package/dist/units/databases/existing-redis/index.js +2 -2
- package/dist/units/databases/existing-redis/index.js.map +1 -1
- package/dist/units/databases/existing-s3/index.js +18 -0
- package/dist/units/databases/existing-s3/index.js.map +1 -0
- package/dist/units/databases/mariadb-patch/index.js +24 -0
- package/dist/units/databases/mariadb-patch/index.js.map +1 -0
- package/dist/units/databases/mongodb-patch/index.js +24 -0
- package/dist/units/databases/mongodb-patch/index.js.map +1 -0
- package/dist/units/databases/postgresql-patch/index.js +24 -0
- package/dist/units/databases/postgresql-patch/index.js.map +1 -0
- package/dist/units/databases/redis-patch/index.js +27 -0
- package/dist/units/databases/redis-patch/index.js.map +1 -0
- package/dist/units/databases/s3-patch/index.js +25 -0
- package/dist/units/databases/s3-patch/index.js.map +1 -0
- package/dist/units/dns/record-set/index.js +14 -20
- package/dist/units/dns/record-set/index.js.map +1 -1
- package/dist/units/existing-server/index.js +3 -4
- package/dist/units/existing-server/index.js.map +1 -1
- package/dist/units/network/address-space/index.js +20 -0
- package/dist/units/network/address-space/index.js.map +1 -0
- package/dist/units/network/endpoint-filter/index.js +15 -0
- package/dist/units/network/endpoint-filter/index.js.map +1 -0
- package/dist/units/network/l3-endpoint/index.js +2 -2
- package/dist/units/network/l3-endpoint/index.js.map +1 -1
- package/dist/units/network/l4-endpoint/index.js +2 -2
- package/dist/units/network/l4-endpoint/index.js.map +1 -1
- package/dist/units/network/l7-endpoint/index.js +12 -0
- package/dist/units/network/l7-endpoint/index.js.map +1 -0
- package/dist/units/script/index.js +1 -1
- package/dist/units/server-patch/index.js +9 -12
- package/dist/units/server-patch/index.js.map +1 -1
- package/dist/units/ssh/key-pair/index.js +1 -1
- package/package.json +64 -10
- package/src/shared/command.ts +1 -1
- package/src/shared/dns.ts +11 -93
- package/src/shared/files.ts +3 -3
- package/src/shared/impl-ref.ts +4 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/network/address-space.spec.ts +114 -0
- package/src/shared/network/address-space.ts +364 -0
- package/src/shared/network/address.spec.ts +109 -0
- package/src/shared/network/address.ts +119 -0
- package/src/shared/network/endpoints.spec.ts +249 -0
- package/src/shared/network/endpoints.ts +608 -0
- package/src/shared/network/index.ts +4 -0
- package/src/shared/network/ip.ts +236 -0
- package/src/shared/network/subnet.spec.ts +62 -0
- package/src/shared/network/subnet.ts +137 -0
- package/src/shared/ssh.ts +1 -1
- package/src/shared/tls.ts +21 -5
- package/src/shared/utils.ts +93 -0
- package/src/units/databases/etcd-patch/index.ts +23 -0
- package/src/units/databases/existing-etcd/index.ts +11 -0
- package/src/units/databases/existing-mariadb/index.ts +1 -1
- package/src/units/databases/existing-mongodb/index.ts +1 -1
- package/src/units/databases/existing-postgresql/index.ts +1 -1
- package/src/units/databases/existing-redis/index.ts +1 -1
- package/src/units/databases/existing-s3/index.ts +1 -1
- package/src/units/databases/mariadb-patch/index.ts +27 -0
- package/src/units/databases/mongodb-patch/index.ts +27 -0
- package/src/units/databases/postgresql-patch/index.ts +27 -0
- package/src/units/databases/redis-patch/index.ts +32 -0
- package/src/units/databases/s3-patch/index.ts +28 -0
- package/src/units/dns/record-set/index.ts +15 -20
- package/src/units/existing-server/index.ts +3 -4
- package/src/units/network/address-space/index.ts +20 -0
- package/src/units/network/endpoint-filter/index.ts +5 -5
- package/src/units/network/l3-endpoint/index.ts +2 -2
- package/src/units/network/l4-endpoint/index.ts +2 -2
- package/src/units/network/l7-endpoint/index.ts +2 -2
- package/src/units/remote-file/index.ts +12 -5
- package/src/units/server-patch/index.ts +10 -13
- package/dist/chunk-WFWXDYUX.js.map +0 -1
- package/dist/units/server-dns/index.js +0 -26
- package/dist/units/server-dns/index.js.map +0 -1
- package/src/shared/network.ts +0 -413
- package/src/units/server-dns/index.ts +0 -26
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { z, check, getOrCreate, HighstateSignature, stripNullish } from '@highstate/contract';
|
|
2
|
+
import { network, metadataSchema } from '@highstate/library';
|
|
1
3
|
import { ComponentResource, Resource, toPromise, getImportBaseUrl, output, interpolate, normalizeInputsAndMap, normalizeInputs, asset, fileFromString, secret } from '@highstate/pulumi';
|
|
2
|
-
import { uniqueBy,
|
|
4
|
+
import { pipe, filter, isNonNullish, map, uniqueBy, omit, flat } from 'remeda';
|
|
3
5
|
import { homedir, tmpdir } from 'node:os';
|
|
4
6
|
import { sha256 } from '@noble/hashes/sha2';
|
|
5
7
|
import { local, remote } from '@pulumi/command';
|
|
6
8
|
import { resolve } from 'import-meta-resolve';
|
|
7
|
-
import { z, getOrCreate, HighstateSignature, stripNullish } from '@highstate/contract';
|
|
8
|
-
import { network } from '@highstate/library';
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import { createReadStream } from 'node:fs';
|
|
11
11
|
import { mkdtemp, writeFile, cp, rm, stat, rename, mkdir } from 'node:fs/promises';
|
|
@@ -19,188 +19,629 @@ import { randomBytes, bytesToHex } from '@noble/hashes/utils.js';
|
|
|
19
19
|
import { secureMask } from 'micro-key-producer/password.js';
|
|
20
20
|
import getKeys, { PrivateExport } from 'micro-key-producer/ssh.js';
|
|
21
21
|
import { randomBytes as randomBytes$1 } from 'micro-key-producer/utils.js';
|
|
22
|
+
import { compile } from 'filter-expression';
|
|
22
23
|
|
|
23
|
-
// src/shared/network.ts
|
|
24
|
+
// src/shared/network/address.ts
|
|
25
|
+
|
|
26
|
+
// src/shared/network/ip.ts
|
|
27
|
+
function parseIp(value) {
|
|
28
|
+
if (value.includes(":")) {
|
|
29
|
+
return { type: "ipv6", value: parseIpv6(value) };
|
|
30
|
+
}
|
|
31
|
+
return { type: "ipv4", value: parseIpv4(value) };
|
|
32
|
+
}
|
|
33
|
+
function parseCidr(value) {
|
|
34
|
+
const [ipPart, prefixPart, ...rest] = value.split("/");
|
|
35
|
+
if (!ipPart || !prefixPart || rest.length > 0) {
|
|
36
|
+
throw new Error(`Invalid CIDR: "${value}"`);
|
|
37
|
+
}
|
|
38
|
+
const parsedIp = parseIp(ipPart.trim());
|
|
39
|
+
const prefix = parseInt(prefixPart.trim(), 10);
|
|
40
|
+
if (!Number.isFinite(prefix)) {
|
|
41
|
+
throw new Error(`Invalid CIDR prefix: "${value}"`);
|
|
42
|
+
}
|
|
43
|
+
const bits = parsedIp.type === "ipv4" ? 32 : 128;
|
|
44
|
+
if (prefix < 0 || prefix > bits) {
|
|
45
|
+
throw new Error(`Invalid CIDR prefix length: "${value}"`);
|
|
46
|
+
}
|
|
47
|
+
return { type: parsedIp.type, ip: parsedIp.value, prefixLength: prefix };
|
|
48
|
+
}
|
|
49
|
+
function subnetBaseFromCidr(parsed) {
|
|
50
|
+
const bits = parsed.type === "ipv4" ? 32 : 128;
|
|
51
|
+
if (parsed.prefixLength === 0) {
|
|
52
|
+
return 0n;
|
|
53
|
+
}
|
|
54
|
+
const mask = (1n << BigInt(parsed.prefixLength)) - 1n << BigInt(bits - parsed.prefixLength);
|
|
55
|
+
return parsed.ip & mask;
|
|
56
|
+
}
|
|
57
|
+
function cidrBlockSize(type, prefixLength) {
|
|
58
|
+
const bits = type === "ipv4" ? 32 : 128;
|
|
59
|
+
return 1n << BigInt(bits - prefixLength);
|
|
60
|
+
}
|
|
61
|
+
function ipToString(type, value) {
|
|
62
|
+
return type === "ipv4" ? ipv4ToString(value) : ipv6ToString(value);
|
|
63
|
+
}
|
|
64
|
+
function parseIpv4(value) {
|
|
65
|
+
const parts = value.trim().split(".");
|
|
66
|
+
if (parts.length !== 4) {
|
|
67
|
+
throw new Error(`Invalid IPv4 address: "${value}"`);
|
|
68
|
+
}
|
|
69
|
+
let result = 0n;
|
|
70
|
+
for (const part of parts) {
|
|
71
|
+
if (!part) {
|
|
72
|
+
throw new Error(`Invalid IPv4 address: "${value}"`);
|
|
73
|
+
}
|
|
74
|
+
const octet = parseInt(part, 10);
|
|
75
|
+
if (!Number.isInteger(octet) || octet < 0 || octet > 255) {
|
|
76
|
+
throw new Error(`Invalid IPv4 address: "${value}"`);
|
|
77
|
+
}
|
|
78
|
+
result = (result << 8n) + BigInt(octet);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
function parseIpv6(value) {
|
|
83
|
+
const input = value.trim().toLowerCase();
|
|
84
|
+
if (!input) {
|
|
85
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
86
|
+
}
|
|
87
|
+
let leftParts = [];
|
|
88
|
+
let rightParts = [];
|
|
89
|
+
const doubleColonIndex = input.indexOf("::");
|
|
90
|
+
if (doubleColonIndex >= 0) {
|
|
91
|
+
const [left, right] = input.split("::");
|
|
92
|
+
leftParts = left ? left.split(":") : [];
|
|
93
|
+
rightParts = right ? right.split(":") : [];
|
|
94
|
+
} else {
|
|
95
|
+
leftParts = input.split(":");
|
|
96
|
+
rightParts = [];
|
|
97
|
+
}
|
|
98
|
+
const expandIpv4Tail = (parts2) => {
|
|
99
|
+
if (parts2.length === 0) return parts2;
|
|
100
|
+
const last = parts2.at(-1);
|
|
101
|
+
if (!last.includes(".")) {
|
|
102
|
+
return parts2;
|
|
103
|
+
}
|
|
104
|
+
const ipv4Value = parseIpv4(last);
|
|
105
|
+
const high = Number(ipv4Value >> 16n & 0xffffn);
|
|
106
|
+
const low = Number(ipv4Value & 0xffffn);
|
|
107
|
+
return [...parts2.slice(0, -1), high.toString(16), low.toString(16)];
|
|
108
|
+
};
|
|
109
|
+
leftParts = leftParts.filter((p) => p.length > 0);
|
|
110
|
+
rightParts = rightParts.filter((p) => p.length > 0);
|
|
111
|
+
leftParts = expandIpv4Tail(leftParts);
|
|
112
|
+
rightParts = expandIpv4Tail(rightParts);
|
|
113
|
+
const totalParts = leftParts.length + rightParts.length;
|
|
114
|
+
if (doubleColonIndex < 0 && totalParts !== 8) {
|
|
115
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
116
|
+
}
|
|
117
|
+
if (totalParts > 8) {
|
|
118
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
119
|
+
}
|
|
120
|
+
const missing = 8 - totalParts;
|
|
121
|
+
const parts = doubleColonIndex >= 0 ? [...leftParts, ...Array.from({ length: missing }, () => "0"), ...rightParts] : leftParts;
|
|
122
|
+
if (parts.length !== 8) {
|
|
123
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
124
|
+
}
|
|
125
|
+
let result = 0n;
|
|
126
|
+
for (const part of parts) {
|
|
127
|
+
if (!part) {
|
|
128
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
129
|
+
}
|
|
130
|
+
const hextet = parseInt(part, 16);
|
|
131
|
+
if (!Number.isInteger(hextet) || hextet < 0 || hextet > 65535) {
|
|
132
|
+
throw new Error(`Invalid IPv6 address: "${value}"`);
|
|
133
|
+
}
|
|
134
|
+
result = (result << 16n) + BigInt(hextet);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
function ipv4ToString(value) {
|
|
139
|
+
const parts = [
|
|
140
|
+
Number(value >> 24n & 0xffn),
|
|
141
|
+
Number(value >> 16n & 0xffn),
|
|
142
|
+
Number(value >> 8n & 0xffn),
|
|
143
|
+
Number(value & 0xffn)
|
|
144
|
+
];
|
|
145
|
+
return parts.join(".");
|
|
146
|
+
}
|
|
147
|
+
function ipv6ToString(value) {
|
|
148
|
+
const hextets = [];
|
|
149
|
+
for (let i = 0; i < 8; i++) {
|
|
150
|
+
const shift = BigInt((7 - i) * 16);
|
|
151
|
+
hextets.push(Number(value >> shift & 0xffffn));
|
|
152
|
+
}
|
|
153
|
+
let bestStart = -1;
|
|
154
|
+
let bestLength = 0;
|
|
155
|
+
let currentStart = -1;
|
|
156
|
+
let currentLength = 0;
|
|
157
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
158
|
+
if (hextets[i] === 0) {
|
|
159
|
+
if (currentStart === -1) {
|
|
160
|
+
currentStart = i;
|
|
161
|
+
currentLength = 1;
|
|
162
|
+
} else {
|
|
163
|
+
currentLength++;
|
|
164
|
+
}
|
|
165
|
+
if (currentLength > bestLength) {
|
|
166
|
+
bestStart = currentStart;
|
|
167
|
+
bestLength = currentLength;
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
currentStart = -1;
|
|
171
|
+
currentLength = 0;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (bestLength < 2) {
|
|
175
|
+
bestStart = -1;
|
|
176
|
+
bestLength = 0;
|
|
177
|
+
}
|
|
178
|
+
const parts = [];
|
|
179
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
180
|
+
if (bestStart >= 0 && i >= bestStart && i < bestStart + bestLength) {
|
|
181
|
+
if (i === bestStart) {
|
|
182
|
+
parts.push("");
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
parts.push(hextets[i].toString(16));
|
|
187
|
+
}
|
|
188
|
+
let result = parts.join(":");
|
|
189
|
+
if (bestStart === 0) {
|
|
190
|
+
result = `:${result}`;
|
|
191
|
+
}
|
|
192
|
+
if (bestStart >= 0 && bestStart + bestLength === 8) {
|
|
193
|
+
result = `${result}:`;
|
|
194
|
+
}
|
|
195
|
+
if (result === "") {
|
|
196
|
+
return "::";
|
|
197
|
+
}
|
|
198
|
+
return result.replace(/:{3,}/g, "::");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/shared/network/address.ts
|
|
202
|
+
function parseAddress(address) {
|
|
203
|
+
if (check(network.addressEntity.schema, address)) {
|
|
204
|
+
return address;
|
|
205
|
+
}
|
|
206
|
+
const input = address.trim();
|
|
207
|
+
if (!input) {
|
|
208
|
+
throw new Error("Empty address string");
|
|
209
|
+
}
|
|
210
|
+
const parsed = input.includes("/") ? parseCidr(input) : parseCidrFromIp(input);
|
|
211
|
+
const canonicalAddress = ipToString(parsed.type, parsed.ip);
|
|
212
|
+
const subnetBase = subnetBaseFromCidr(parsed);
|
|
213
|
+
const subnetBaseAddress = ipToString(parsed.type, subnetBase);
|
|
214
|
+
const result = {
|
|
215
|
+
type: parsed.type,
|
|
216
|
+
value: canonicalAddress,
|
|
217
|
+
subnet: {
|
|
218
|
+
type: parsed.type,
|
|
219
|
+
baseAddress: subnetBaseAddress,
|
|
220
|
+
prefixLength: parsed.prefixLength
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const validated = network.addressEntity.schema.safeParse(result);
|
|
224
|
+
if (!validated.success) {
|
|
225
|
+
throw new Error(`Invalid address "${input}": ${validated.error.message}`);
|
|
226
|
+
}
|
|
227
|
+
return validated.data;
|
|
228
|
+
}
|
|
229
|
+
function addressToCidr(address) {
|
|
230
|
+
return `${address.value}/${address.subnet.prefixLength}`;
|
|
231
|
+
}
|
|
232
|
+
function doesAddressBelongToSubnet(address, subnet) {
|
|
233
|
+
if (address.type !== subnet.type) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const addressIp = parseIp(address.value);
|
|
237
|
+
const subnetBaseIp = parseIp(subnet.baseAddress);
|
|
238
|
+
if (addressIp.type !== subnet.type || subnetBaseIp.type !== subnet.type) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
const bits = subnet.type === "ipv4" ? 32 : 128;
|
|
242
|
+
if (subnet.prefixLength === 0) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
const mask = (1n << BigInt(subnet.prefixLength)) - 1n << BigInt(bits - subnet.prefixLength);
|
|
246
|
+
return (addressIp.value & mask) === (subnetBaseIp.value & mask);
|
|
247
|
+
}
|
|
248
|
+
function mergeAddresses(addresses) {
|
|
249
|
+
const mergedMap = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const address of addresses) {
|
|
251
|
+
mergedMap.set(addressToCidr(address), address);
|
|
252
|
+
}
|
|
253
|
+
return Array.from(mergedMap.values());
|
|
254
|
+
}
|
|
255
|
+
function parseCidrFromIp(value) {
|
|
256
|
+
const parsed = parseIp(value);
|
|
257
|
+
const prefixLength = parsed.type === "ipv4" ? 32 : 128;
|
|
258
|
+
return {
|
|
259
|
+
type: parsed.type,
|
|
260
|
+
ip: parsed.value,
|
|
261
|
+
prefixLength
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function parseSubnet(subnet) {
|
|
265
|
+
if (check(network.subnetEntity.schema, subnet)) {
|
|
266
|
+
return subnet;
|
|
267
|
+
}
|
|
268
|
+
if (check(network.addressEntity.schema, subnet)) {
|
|
269
|
+
const prefixLength = subnet.type === "ipv4" ? 32 : 128;
|
|
270
|
+
const result2 = {
|
|
271
|
+
type: subnet.type,
|
|
272
|
+
baseAddress: subnet.value,
|
|
273
|
+
prefixLength
|
|
274
|
+
};
|
|
275
|
+
const validated2 = network.subnetEntity.schema.safeParse(result2);
|
|
276
|
+
if (!validated2.success) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Invalid subnet "${subnet.value}/${prefixLength}": ${validated2.error.message}`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return validated2.data;
|
|
282
|
+
}
|
|
283
|
+
const input = subnet.trim();
|
|
284
|
+
if (!input) {
|
|
285
|
+
throw new Error("Empty subnet string");
|
|
286
|
+
}
|
|
287
|
+
let parsed;
|
|
288
|
+
if (input.includes("/")) {
|
|
289
|
+
parsed = parseCidr(input);
|
|
290
|
+
} else {
|
|
291
|
+
const ip = parseIp(input);
|
|
292
|
+
const prefixLength = ip.type === "ipv4" ? 32 : 128;
|
|
293
|
+
parsed = { type: ip.type, ip: ip.value, prefixLength };
|
|
294
|
+
}
|
|
295
|
+
const subnetBase = subnetBaseFromCidr(parsed);
|
|
296
|
+
const baseAddress = ipToString(parsed.type, subnetBase);
|
|
297
|
+
const result = {
|
|
298
|
+
type: parsed.type,
|
|
299
|
+
baseAddress,
|
|
300
|
+
prefixLength: parsed.prefixLength
|
|
301
|
+
};
|
|
302
|
+
const validated = network.subnetEntity.schema.safeParse(result);
|
|
303
|
+
if (!validated.success) {
|
|
304
|
+
throw new Error(`Invalid subnet "${input}": ${validated.error.message}`);
|
|
305
|
+
}
|
|
306
|
+
return validated.data;
|
|
307
|
+
}
|
|
308
|
+
var privateIpV4Subnets = [
|
|
309
|
+
parseSubnet("10.0.0.0/8"),
|
|
310
|
+
parseSubnet("127.0.0.0/8"),
|
|
311
|
+
parseSubnet("172.16.0.0/12"),
|
|
312
|
+
parseSubnet("192.168.0.0/16")
|
|
313
|
+
];
|
|
314
|
+
var privateIpV6Subnets = [
|
|
315
|
+
parseSubnet("fc00::/7"),
|
|
316
|
+
// IPv4-mapped private ranges.
|
|
317
|
+
parseSubnet("::ffff:10.0.0.0/104"),
|
|
318
|
+
parseSubnet("::ffff:127.0.0.0/104"),
|
|
319
|
+
parseSubnet("::ffff:172.16.0.0/108"),
|
|
320
|
+
parseSubnet("::ffff:192.168.0.0/112")
|
|
321
|
+
];
|
|
322
|
+
var privateSubnets = [...privateIpV4Subnets, ...privateIpV6Subnets];
|
|
323
|
+
function isPrivateAddress(address) {
|
|
324
|
+
for (const subnet of privateSubnets) {
|
|
325
|
+
if (doesAddressBelongToSubnet(address, subnet)) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
async function parseSubnets(subnets, inputSubnets) {
|
|
332
|
+
const resolvedInputSubnets = await toPromise(inputSubnets ?? []);
|
|
333
|
+
return pipe(
|
|
334
|
+
[...subnets ?? [], ...resolvedInputSubnets],
|
|
335
|
+
filter(isNonNullish),
|
|
336
|
+
map((subnet) => parseSubnet(subnet)),
|
|
337
|
+
uniqueBy((subnet) => subnetToString(subnet))
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
function subnetToString(subnet) {
|
|
341
|
+
return `${subnet.baseAddress}/${subnet.prefixLength}`;
|
|
342
|
+
}
|
|
24
343
|
function l3EndpointToString(l3Endpoint) {
|
|
25
344
|
switch (l3Endpoint.type) {
|
|
26
345
|
case "ipv4":
|
|
27
|
-
return l3Endpoint.address;
|
|
346
|
+
return l3Endpoint.address.value;
|
|
28
347
|
case "ipv6":
|
|
29
|
-
return l3Endpoint.address;
|
|
348
|
+
return l3Endpoint.address.value;
|
|
30
349
|
case "hostname":
|
|
31
350
|
return l3Endpoint.hostname;
|
|
32
351
|
}
|
|
33
352
|
}
|
|
34
353
|
function l4EndpointToString(l4Endpoint) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
return `${l3EndpointToString(l4Endpoint)}:${l4Endpoint.port}`;
|
|
354
|
+
const host = l3EndpointToString(l4Endpoint);
|
|
355
|
+
const wrappedHost = l4Endpoint.type === "ipv6" ? `[${host}]` : host;
|
|
356
|
+
return `${wrappedHost}:${l4Endpoint.port}`;
|
|
39
357
|
}
|
|
40
|
-
function
|
|
358
|
+
function l4EndpointToFullString(l4Endpoint) {
|
|
41
359
|
const protocol = `${l4Endpoint.protocol}://`;
|
|
42
360
|
return `${protocol}${l4EndpointToString(l4Endpoint)}`;
|
|
43
361
|
}
|
|
44
362
|
function l7EndpointToString(l7Endpoint) {
|
|
45
363
|
const protocol = `${l7Endpoint.appProtocol}://`;
|
|
46
364
|
let endpoint = l4EndpointToString(l7Endpoint);
|
|
47
|
-
if (l7Endpoint.
|
|
48
|
-
endpoint += `/${l7Endpoint.
|
|
365
|
+
if (l7Endpoint.path) {
|
|
366
|
+
endpoint += `/${l7Endpoint.path}`;
|
|
49
367
|
}
|
|
50
368
|
return `${protocol}${endpoint}`;
|
|
51
369
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
370
|
+
function endpointToString(endpoint) {
|
|
371
|
+
switch (endpoint.level) {
|
|
372
|
+
case 3:
|
|
373
|
+
return l3EndpointToString(endpoint);
|
|
374
|
+
case 4:
|
|
375
|
+
return l4EndpointToString(endpoint);
|
|
376
|
+
case 7:
|
|
377
|
+
return l7EndpointToString(endpoint);
|
|
55
378
|
}
|
|
56
|
-
return l3EndpointToString(l34Endpoint);
|
|
57
379
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function parseL34Endpoint(l34Endpoint) {
|
|
61
|
-
if (typeof l34Endpoint === "object") {
|
|
62
|
-
return l34Endpoint;
|
|
63
|
-
}
|
|
64
|
-
const match = l34Endpoint.match(L34_ENDPOINT_RE);
|
|
65
|
-
if (!match) {
|
|
66
|
-
throw new Error(`Invalid L3/L4 endpoint: "${l34Endpoint}"`);
|
|
67
|
-
}
|
|
68
|
-
const { protocol, ipv6, ipv4, hostname, port } = match.groups;
|
|
69
|
-
if (protocol && protocol !== "tcp" && protocol !== "udp") {
|
|
70
|
-
throw new Error(`Invalid L4 endpoint protocol: "${protocol}"`);
|
|
71
|
-
}
|
|
72
|
-
let visibility = "public";
|
|
73
|
-
if (ipv4 && IPV4_PRIVATE_REGEX.test(ipv4)) {
|
|
74
|
-
visibility = "external";
|
|
75
|
-
} else if (ipv6 && IPV6_PRIVATE_REGEX.test(ipv6)) {
|
|
76
|
-
visibility = "external";
|
|
77
|
-
}
|
|
78
|
-
const fallbackProtocol = port ? "tcp" : void 0;
|
|
79
|
-
return {
|
|
80
|
-
type: ipv6 ? "ipv6" : ipv4 ? "ipv4" : "hostname",
|
|
81
|
-
visibility,
|
|
82
|
-
address: ipv6 || ipv4,
|
|
83
|
-
hostname,
|
|
84
|
-
port: port ? parseInt(port, 10) : void 0,
|
|
85
|
-
protocol: protocol ? protocol : fallbackProtocol
|
|
86
|
-
};
|
|
380
|
+
function checkEndpointLevel(endpoint, minLevel) {
|
|
381
|
+
return endpoint.level >= minLevel;
|
|
87
382
|
}
|
|
88
|
-
function
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (parsed.port) {
|
|
94
|
-
throw new Error(`Port cannot be specified in L3 endpoint: "${l3Endpoint}"`);
|
|
383
|
+
function assertEndpointLevel(endpoint, minLevel) {
|
|
384
|
+
if (!checkEndpointLevel(endpoint, minLevel)) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`The endpoint "${endpointToString(endpoint)}" is L${endpoint.level}, but L${minLevel} is required`
|
|
387
|
+
);
|
|
95
388
|
}
|
|
96
|
-
return parsed;
|
|
97
389
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
390
|
+
function parseEndpoint(endpoint, minLevel = 3) {
|
|
391
|
+
function validateEndpoint(value) {
|
|
392
|
+
const schema = value.level === 7 ? network.l7EndpointEntity.schema : value.level === 4 ? network.l4EndpointEntity.schema : network.l3EndpointEntity.schema;
|
|
393
|
+
const result = schema.safeParse(value);
|
|
394
|
+
if (!result.success) {
|
|
395
|
+
throw new Error(`Invalid endpoint "${endpointToString(value)}": ${result.error.message}`);
|
|
396
|
+
}
|
|
397
|
+
return value;
|
|
105
398
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
399
|
+
function parseHostToL3(host) {
|
|
400
|
+
const trimmed = host.trim();
|
|
401
|
+
if (!trimmed) {
|
|
402
|
+
throw new Error("Empty endpoint host");
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const address = parseAddress(trimmed);
|
|
406
|
+
return {
|
|
407
|
+
type: address.type,
|
|
408
|
+
level: 3,
|
|
409
|
+
metadata: extractMetadata(address),
|
|
410
|
+
address,
|
|
411
|
+
subnet: address.subnet
|
|
412
|
+
};
|
|
413
|
+
} catch {
|
|
414
|
+
return {
|
|
415
|
+
type: "hostname",
|
|
416
|
+
level: 3,
|
|
417
|
+
hostname: trimmed,
|
|
418
|
+
metadata: {}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
113
421
|
}
|
|
114
|
-
|
|
115
|
-
|
|
422
|
+
function splitHostPort(input) {
|
|
423
|
+
const trimmed = input.trim();
|
|
424
|
+
if (!trimmed) {
|
|
425
|
+
throw new Error("Empty endpoint");
|
|
426
|
+
}
|
|
427
|
+
if (trimmed.startsWith("[")) {
|
|
428
|
+
const closingIndex = trimmed.indexOf("]");
|
|
429
|
+
if (closingIndex === -1) {
|
|
430
|
+
throw new Error(`Invalid endpoint: "${input}"`);
|
|
431
|
+
}
|
|
432
|
+
const host2 = trimmed.slice(1, closingIndex);
|
|
433
|
+
const remainder = trimmed.slice(closingIndex + 1);
|
|
434
|
+
if (!remainder) {
|
|
435
|
+
return { host: host2 };
|
|
436
|
+
}
|
|
437
|
+
if (!remainder.startsWith(":")) {
|
|
438
|
+
throw new Error(`Invalid endpoint: "${input}"`);
|
|
439
|
+
}
|
|
440
|
+
const portString2 = remainder.slice(1);
|
|
441
|
+
if (!/^\d{1,5}$/.test(portString2)) {
|
|
442
|
+
throw new Error(`Invalid endpoint port: "${portString2}"`);
|
|
443
|
+
}
|
|
444
|
+
return { host: host2, port: parseInt(portString2, 10) };
|
|
445
|
+
}
|
|
446
|
+
const lastColonIndex = trimmed.lastIndexOf(":");
|
|
447
|
+
if (lastColonIndex === -1) {
|
|
448
|
+
return { host: trimmed };
|
|
449
|
+
}
|
|
450
|
+
if (trimmed.includes(":")) {
|
|
451
|
+
const firstColonIndex = trimmed.indexOf(":");
|
|
452
|
+
if (firstColonIndex !== lastColonIndex) {
|
|
453
|
+
return { host: trimmed };
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const host = trimmed.slice(0, lastColonIndex);
|
|
457
|
+
const portString = trimmed.slice(lastColonIndex + 1);
|
|
458
|
+
if (!/^\d{1,5}$/.test(portString)) {
|
|
459
|
+
return { host: trimmed };
|
|
460
|
+
}
|
|
461
|
+
return { host, port: parseInt(portString, 10) };
|
|
462
|
+
}
|
|
463
|
+
if (check(network.l3EndpointEntity.schema, endpoint)) {
|
|
464
|
+
assertEndpointLevel(endpoint, minLevel);
|
|
465
|
+
return endpoint;
|
|
466
|
+
}
|
|
467
|
+
if (check(network.addressEntity.schema, endpoint)) {
|
|
468
|
+
const address = endpoint;
|
|
469
|
+
const built = {
|
|
470
|
+
type: address.type,
|
|
471
|
+
level: 3,
|
|
472
|
+
metadata: extractMetadata(address),
|
|
473
|
+
address,
|
|
474
|
+
subnet: address.subnet
|
|
475
|
+
};
|
|
476
|
+
const withDynamic2 = syncDynamic(built);
|
|
477
|
+
const validated2 = validateEndpoint(withDynamic2);
|
|
478
|
+
assertEndpointLevel(validated2, minLevel);
|
|
479
|
+
return validated2;
|
|
480
|
+
}
|
|
481
|
+
if (typeof endpoint !== "string") {
|
|
482
|
+
throw new Error("Invalid endpoint");
|
|
483
|
+
}
|
|
484
|
+
const endpointString = endpoint;
|
|
485
|
+
let builtEndpoint;
|
|
486
|
+
const schemeMatch = /^([a-z]+):\/\/(.*)$/.exec(endpointString);
|
|
487
|
+
if (schemeMatch) {
|
|
488
|
+
const appProtocol = schemeMatch[1];
|
|
489
|
+
const rest = schemeMatch[2];
|
|
490
|
+
const pathIndex = rest.indexOf("/");
|
|
491
|
+
const hostPortPart = pathIndex === -1 ? rest : rest.slice(0, pathIndex);
|
|
492
|
+
const path = pathIndex === -1 ? void 0 : rest.slice(pathIndex + 1);
|
|
493
|
+
const udpAppProtocols = ["dns", "dhcp"];
|
|
494
|
+
const { host, port } = splitHostPort(hostPortPart);
|
|
495
|
+
const l3Base = parseHostToL3(host);
|
|
496
|
+
const portNumber = port ?? 443;
|
|
497
|
+
const protocol = udpAppProtocols.includes(appProtocol) ? "udp" : "tcp";
|
|
498
|
+
builtEndpoint = l3Base.type === "hostname" ? {
|
|
499
|
+
...l3Base,
|
|
500
|
+
level: 7,
|
|
501
|
+
port: portNumber,
|
|
502
|
+
protocol,
|
|
503
|
+
appProtocol,
|
|
504
|
+
path: path || void 0
|
|
505
|
+
} : {
|
|
506
|
+
...l3Base,
|
|
507
|
+
level: 7,
|
|
508
|
+
port: portNumber,
|
|
509
|
+
protocol,
|
|
510
|
+
appProtocol,
|
|
511
|
+
path: path || void 0
|
|
512
|
+
};
|
|
513
|
+
} else {
|
|
514
|
+
const { host, port } = splitHostPort(endpointString);
|
|
515
|
+
const l3Base = parseHostToL3(host);
|
|
516
|
+
if (port !== void 0) {
|
|
517
|
+
builtEndpoint = l3Base.type === "hostname" ? {
|
|
518
|
+
...l3Base,
|
|
519
|
+
level: 4,
|
|
520
|
+
port,
|
|
521
|
+
protocol: "tcp"
|
|
522
|
+
} : {
|
|
523
|
+
...l3Base,
|
|
524
|
+
level: 4,
|
|
525
|
+
port,
|
|
526
|
+
protocol: "tcp"
|
|
527
|
+
};
|
|
528
|
+
} else {
|
|
529
|
+
builtEndpoint = l3Base;
|
|
530
|
+
}
|
|
116
531
|
}
|
|
117
|
-
|
|
532
|
+
const withDynamic = syncDynamic(builtEndpoint);
|
|
533
|
+
const validated = validateEndpoint(withDynamic);
|
|
534
|
+
assertEndpointLevel(validated, minLevel);
|
|
535
|
+
return validated;
|
|
118
536
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (inputEndpoint) {
|
|
124
|
-
return toPromise(inputEndpoint);
|
|
537
|
+
function parseL4Protocol(input) {
|
|
538
|
+
input = input.trim().toLowerCase();
|
|
539
|
+
if (input === "tcp" || input === "udp") {
|
|
540
|
+
return input;
|
|
125
541
|
}
|
|
126
|
-
throw new Error(
|
|
542
|
+
throw new Error(`Invalid L4 protocol: "${input}"`);
|
|
127
543
|
}
|
|
128
544
|
function l3EndpointToL4(l3Endpoint, port, protocol = "tcp") {
|
|
545
|
+
const parsed = parseEndpoint(l3Endpoint);
|
|
129
546
|
return {
|
|
130
|
-
...
|
|
547
|
+
...parsed,
|
|
548
|
+
level: 4,
|
|
131
549
|
port,
|
|
132
550
|
protocol
|
|
133
551
|
};
|
|
134
552
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (types?.length) {
|
|
144
|
-
endpoints = endpoints.filter((endpoint) => types.includes(endpoint.type));
|
|
145
|
-
}
|
|
146
|
-
return endpoints;
|
|
553
|
+
function l4EndpointToL7(l4Endpoint, appProtocol, path = "") {
|
|
554
|
+
const parsed = parseEndpoint(l4Endpoint, 4);
|
|
555
|
+
return {
|
|
556
|
+
...parsed,
|
|
557
|
+
level: 7,
|
|
558
|
+
appProtocol,
|
|
559
|
+
path
|
|
560
|
+
};
|
|
147
561
|
}
|
|
148
|
-
function l3EndpointToCidr(
|
|
149
|
-
switch (
|
|
562
|
+
function l3EndpointToCidr(endpoint) {
|
|
563
|
+
switch (endpoint.type) {
|
|
150
564
|
case "ipv4":
|
|
151
|
-
return `${
|
|
565
|
+
return `${endpoint.address.value}/32`;
|
|
152
566
|
case "ipv6":
|
|
153
|
-
return `${
|
|
567
|
+
return `${endpoint.address.value}/128`;
|
|
154
568
|
case "hostname":
|
|
155
569
|
throw new Error("Cannot convert hostname to CIDR");
|
|
156
570
|
}
|
|
157
571
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return l7Endpoint;
|
|
572
|
+
function extractMetadata(address) {
|
|
573
|
+
if (!address) {
|
|
574
|
+
return {};
|
|
162
575
|
}
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
165
|
-
|
|
576
|
+
const metadata = {};
|
|
577
|
+
if (privateSubnets.some((subnet) => doesAddressBelongToSubnet(address, subnet))) {
|
|
578
|
+
metadata["iana.scope"] = "private";
|
|
579
|
+
} else {
|
|
580
|
+
metadata["iana.scope"] = "global";
|
|
166
581
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
582
|
+
return metadata;
|
|
583
|
+
}
|
|
584
|
+
async function parseEndpoints(endpoints, inputEndpoints, minLevel = 3) {
|
|
585
|
+
const resolvedInputEndpoints = await toPromise(inputEndpoints ?? []);
|
|
586
|
+
return pipe(
|
|
587
|
+
[...endpoints ?? [], ...resolvedInputEndpoints],
|
|
588
|
+
filter(isNonNullish),
|
|
589
|
+
map((endpoint) => parseEndpoint(endpoint, minLevel)),
|
|
590
|
+
uniqueBy(endpointToString)
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
function addEndpointMetadata(endpoint, newMetadata) {
|
|
594
|
+
const parsedMetadata = metadataSchema.safeParse(newMetadata);
|
|
595
|
+
if (!parsedMetadata.success) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Invalid new metadata for endpoint "${endpointToString(endpoint)}": ${parsedMetadata.error.message}`
|
|
598
|
+
);
|
|
173
599
|
}
|
|
600
|
+
return syncDynamic({
|
|
601
|
+
...endpoint,
|
|
602
|
+
metadata: {
|
|
603
|
+
...endpoint.metadata,
|
|
604
|
+
...newMetadata
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
function mergeEndpoints(endpoints) {
|
|
609
|
+
const mergedMap = /* @__PURE__ */ new Map();
|
|
610
|
+
for (const endpoint of endpoints) {
|
|
611
|
+
const key = endpointToString(endpoint);
|
|
612
|
+
const existing = mergedMap.get(key);
|
|
613
|
+
if (existing) {
|
|
614
|
+
mergedMap.set(key, addEndpointMetadata(existing, endpoint.metadata ?? {}));
|
|
615
|
+
} else {
|
|
616
|
+
mergedMap.set(key, endpoint);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return Array.from(mergedMap.values());
|
|
620
|
+
}
|
|
621
|
+
function syncDynamic(endpoint) {
|
|
174
622
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
port: port ? parseInt(port, 10) : 443,
|
|
181
|
-
// L7 endpoints typically use TCP, but can also use UDP for specific protocols
|
|
182
|
-
protocol: udpAppProtocols.includes(appProtocol) ? "udp" : "tcp",
|
|
183
|
-
appProtocol,
|
|
184
|
-
resource: resource || ""
|
|
623
|
+
...endpoint,
|
|
624
|
+
dynamic: {
|
|
625
|
+
type: "static",
|
|
626
|
+
endpoint: omit(endpoint, ["dynamic"])
|
|
627
|
+
}
|
|
185
628
|
};
|
|
186
629
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
630
|
+
function replaceEndpointBase(endpoint, base) {
|
|
631
|
+
if (base.type === "hostname") {
|
|
632
|
+
return syncDynamic({
|
|
633
|
+
...omit(endpoint, ["type", "address", "subnet"]),
|
|
634
|
+
type: "hostname",
|
|
635
|
+
hostname: base.hostname
|
|
636
|
+
});
|
|
192
637
|
}
|
|
193
|
-
return
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return uniqueBy(
|
|
201
|
-
[...endpoints?.map(parseL34Endpoint) ?? [], ...resolvedInputEndpoints ?? []],
|
|
202
|
-
l34EndpointToString
|
|
203
|
-
);
|
|
638
|
+
return syncDynamic({
|
|
639
|
+
...omit(endpoint, ["type", "hostname", "metadata"]),
|
|
640
|
+
type: base.type,
|
|
641
|
+
address: base.address,
|
|
642
|
+
subnet: base.subnet,
|
|
643
|
+
metadata: base.metadata ?? extractMetadata(base.address)
|
|
644
|
+
});
|
|
204
645
|
}
|
|
205
646
|
function getServerConnection(ssh) {
|
|
206
647
|
return output(ssh).apply((ssh2) => ({
|
|
@@ -443,6 +884,9 @@ var ImplementationMediator = class {
|
|
|
443
884
|
return output(this.call(implRef, input));
|
|
444
885
|
}
|
|
445
886
|
};
|
|
887
|
+
function areImplRefsEqual(a, b) {
|
|
888
|
+
return a.package === b.package && JSON.stringify(a.data) === JSON.stringify(b.data);
|
|
889
|
+
}
|
|
446
890
|
var dnsRecordMediator = new ImplementationMediator(
|
|
447
891
|
"dns-record",
|
|
448
892
|
z.object({ name: z.string(), args: z.custom() }),
|
|
@@ -471,7 +915,7 @@ var DnsRecord = class extends ComponentResource {
|
|
|
471
915
|
waitCommands;
|
|
472
916
|
constructor(name, args, opts) {
|
|
473
917
|
super("highstate:common:DnsRecord", name, args, opts);
|
|
474
|
-
const l3Endpoint = output(args.value).apply((value) =>
|
|
918
|
+
const l3Endpoint = output(args.value).apply((value) => parseEndpoint(value));
|
|
475
919
|
const resolvedValue = l3Endpoint.apply(l3EndpointToString);
|
|
476
920
|
const resolvedType = args.type ? output(args.type) : l3Endpoint.apply(getTypeByEndpoint);
|
|
477
921
|
this.dnsRecord = output(args.provider).apply((provider) => {
|
|
@@ -547,7 +991,9 @@ var DnsRecordSet = class _DnsRecordSet extends ComponentResource {
|
|
|
547
991
|
providers: args.providers,
|
|
548
992
|
name: args.name ?? name
|
|
549
993
|
}).apply(({ providers, name: name2 }) => {
|
|
550
|
-
const matchedProviders2 = providers.filter(
|
|
994
|
+
const matchedProviders2 = providers.filter(
|
|
995
|
+
(provider) => provider.zones.some((zone) => name2.endsWith(zone))
|
|
996
|
+
);
|
|
551
997
|
if (matchedProviders2.length === 0) {
|
|
552
998
|
throw new Error(`No DNS provider matched the domain "${name2}"`);
|
|
553
999
|
}
|
|
@@ -559,7 +1005,7 @@ var DnsRecordSet = class _DnsRecordSet extends ComponentResource {
|
|
|
559
1005
|
providers: matchedProviders
|
|
560
1006
|
}).apply(({ name: name2, providers }) => {
|
|
561
1007
|
return providers.flatMap((provider) => {
|
|
562
|
-
const l3Endpoint =
|
|
1008
|
+
const l3Endpoint = parseEndpoint(value);
|
|
563
1009
|
return new DnsRecord(
|
|
564
1010
|
`${name2}.${provider.id}.${l3EndpointToString(l3Endpoint)}`,
|
|
565
1011
|
{
|
|
@@ -597,53 +1043,6 @@ var DnsRecordSet = class _DnsRecordSet extends ComponentResource {
|
|
|
597
1043
|
);
|
|
598
1044
|
}
|
|
599
1045
|
};
|
|
600
|
-
async function updateEndpointsWithFqdn(endpoints, fqdn, fqdnEndpointFilter, patchMode, dnsProviders, dnsSetName) {
|
|
601
|
-
const resolvedEndpoints = await toPromise(endpoints);
|
|
602
|
-
if (!fqdn) {
|
|
603
|
-
return {
|
|
604
|
-
endpoints: resolvedEndpoints,
|
|
605
|
-
dnsRecordSet: void 0
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
const filteredEndpoints = filterEndpoints(resolvedEndpoints, fqdnEndpointFilter);
|
|
609
|
-
const dnsRecordSet = new DnsRecordSet(dnsSetName ?? fqdn, {
|
|
610
|
-
name: fqdn,
|
|
611
|
-
providers: dnsProviders,
|
|
612
|
-
values: filteredEndpoints,
|
|
613
|
-
waitAt: "local"
|
|
614
|
-
});
|
|
615
|
-
const portProtocolGroups = groupBy(
|
|
616
|
-
filteredEndpoints,
|
|
617
|
-
(endpoint) => endpoint.port ? `${endpoint.port}-${endpoint.protocol}` : ""
|
|
618
|
-
);
|
|
619
|
-
const newEndpoints = [];
|
|
620
|
-
for (const group of Object.values(portProtocolGroups)) {
|
|
621
|
-
newEndpoints.unshift({
|
|
622
|
-
type: "hostname",
|
|
623
|
-
hostname: fqdn,
|
|
624
|
-
visibility: group[0].visibility,
|
|
625
|
-
port: group[0].port,
|
|
626
|
-
protocol: group[0].protocol
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
await toPromise(
|
|
630
|
-
dnsRecordSet.waitCommands.apply((waitCommands) => waitCommands.map((command) => command.stdout))
|
|
631
|
-
);
|
|
632
|
-
if (patchMode === "prepend") {
|
|
633
|
-
return {
|
|
634
|
-
endpoints: uniqueBy(
|
|
635
|
-
//
|
|
636
|
-
[...newEndpoints, ...resolvedEndpoints],
|
|
637
|
-
(endpoint) => l34EndpointToString(endpoint)
|
|
638
|
-
),
|
|
639
|
-
dnsRecordSet
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
return {
|
|
643
|
-
endpoints: newEndpoints,
|
|
644
|
-
dnsRecordSet
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
1046
|
var gatewayRouteMediator = new ImplementationMediator(
|
|
648
1047
|
"gateway-route",
|
|
649
1048
|
z.object({
|
|
@@ -701,18 +1100,29 @@ var TlsCertificate = class _TlsCertificate extends ComponentResource {
|
|
|
701
1100
|
dnsNames: args.dnsNames
|
|
702
1101
|
}).apply(async ({ issuers: issuers2, commonName, dnsNames }) => {
|
|
703
1102
|
const matchedIssuer = issuers2.find((issuer) => {
|
|
704
|
-
if (commonName && !commonName.endsWith(
|
|
1103
|
+
if (commonName && !issuer.zones.some((zone) => commonName.endsWith(zone))) {
|
|
705
1104
|
return false;
|
|
706
1105
|
}
|
|
707
|
-
if (dnsNames && !dnsNames.every((name2) => name2.endsWith(
|
|
1106
|
+
if (dnsNames && !dnsNames.every((name2) => issuer.zones.some((zone) => name2.endsWith(zone)))) {
|
|
708
1107
|
return false;
|
|
709
1108
|
}
|
|
710
1109
|
return true;
|
|
711
1110
|
});
|
|
712
1111
|
if (!matchedIssuer) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
1112
|
+
if (commonName && dnsNames && dnsNames.length > 0) {
|
|
1113
|
+
const dnsNameList = dnsNames.join(", ");
|
|
1114
|
+
throw new Error(
|
|
1115
|
+
`No TLS issuer matched the common name "${commonName}" and DNS names "${dnsNameList}"`
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (commonName) {
|
|
1119
|
+
throw new Error(`No TLS issuer matched the common name "${commonName}"`);
|
|
1120
|
+
}
|
|
1121
|
+
if (dnsNames && dnsNames.length > 0) {
|
|
1122
|
+
const dnsNameList = dnsNames.join(", ");
|
|
1123
|
+
throw new Error(`No TLS issuer matched the DNS names "${dnsNameList}"`);
|
|
1124
|
+
}
|
|
1125
|
+
throw new Error("No TLS issuer provided");
|
|
716
1126
|
}
|
|
717
1127
|
return await tlsCertificateMediator.call(matchedIssuer.implRef, {
|
|
718
1128
|
name,
|
|
@@ -1287,8 +1697,251 @@ async function fetchFileSize(endpoint) {
|
|
|
1287
1697
|
return size;
|
|
1288
1698
|
}
|
|
1289
1699
|
function getNameByEndpoint(endpoint) {
|
|
1290
|
-
const parsedEndpoint =
|
|
1291
|
-
return parsedEndpoint.
|
|
1700
|
+
const parsedEndpoint = parseEndpoint(endpoint, 7);
|
|
1701
|
+
return parsedEndpoint.path ? basename(parsedEndpoint.path) : "";
|
|
1702
|
+
}
|
|
1703
|
+
function createAddressSpace({
|
|
1704
|
+
included,
|
|
1705
|
+
excluded = []
|
|
1706
|
+
}) {
|
|
1707
|
+
const includedRanges = included.flatMap(resolveInputToRanges);
|
|
1708
|
+
const excludedRanges = excluded.flatMap(resolveInputToRanges);
|
|
1709
|
+
const normalized = normalizeRanges(includedRanges, excludedRanges);
|
|
1710
|
+
const subnets = rangesToCanonicalSubnets(normalized);
|
|
1711
|
+
return { subnets };
|
|
1712
|
+
}
|
|
1713
|
+
function resolveInputToRanges(input) {
|
|
1714
|
+
if (typeof input === "string") {
|
|
1715
|
+
return [rangeFromString(input)];
|
|
1716
|
+
}
|
|
1717
|
+
if (check(network.addressSpaceEntity.schema, input)) {
|
|
1718
|
+
return input.subnets.flatMap((subnet) => [rangeFromCidr(subnetToString(subnet))]);
|
|
1719
|
+
}
|
|
1720
|
+
if (check(network.subnetEntity.schema, input)) {
|
|
1721
|
+
return [rangeFromCidr(subnetToString(input))];
|
|
1722
|
+
}
|
|
1723
|
+
if (check(network.addressEntity.schema, input)) {
|
|
1724
|
+
return [rangeFromCidr(addressToCidr(input))];
|
|
1725
|
+
}
|
|
1726
|
+
if (check(network.l3EndpointEntity.schema, input)) {
|
|
1727
|
+
if (input.type === "hostname") {
|
|
1728
|
+
return [];
|
|
1729
|
+
}
|
|
1730
|
+
const cidr = input.cidr;
|
|
1731
|
+
if (typeof cidr === "string") {
|
|
1732
|
+
return [rangeFromCidr(cidr)];
|
|
1733
|
+
}
|
|
1734
|
+
const address = input.address;
|
|
1735
|
+
if (typeof address === "string") {
|
|
1736
|
+
const parsed = parseIp(address);
|
|
1737
|
+
const prefixLength = parsed.type === "ipv4" ? 32 : 128;
|
|
1738
|
+
return [
|
|
1739
|
+
rangeFromParsedCidr({ family: parsed.type, base: parsed.value, prefix: prefixLength })
|
|
1740
|
+
];
|
|
1741
|
+
}
|
|
1742
|
+
if (check(network.addressEntity.schema, address)) {
|
|
1743
|
+
return [rangeFromCidr(addressToCidr(address))];
|
|
1744
|
+
}
|
|
1745
|
+
throw new Error("Invalid L3 endpoint: missing address information");
|
|
1746
|
+
}
|
|
1747
|
+
throw new Error("Unsupported address space input");
|
|
1748
|
+
}
|
|
1749
|
+
function rangeFromString(value) {
|
|
1750
|
+
const trimmed = value.trim();
|
|
1751
|
+
if (!trimmed) {
|
|
1752
|
+
throw new Error("Empty address string");
|
|
1753
|
+
}
|
|
1754
|
+
if (trimmed.includes("-")) {
|
|
1755
|
+
const [left, right, ...rest] = trimmed.split("-");
|
|
1756
|
+
if (!left || !right || rest.length > 0) {
|
|
1757
|
+
throw new Error(`Invalid range: "${value}"`);
|
|
1758
|
+
}
|
|
1759
|
+
const start = parseIp(left.trim());
|
|
1760
|
+
const end = parseIp(right.trim());
|
|
1761
|
+
if (start.type !== end.type) {
|
|
1762
|
+
throw new Error(`Range must not mix IPv4 and IPv6: "${value}"`);
|
|
1763
|
+
}
|
|
1764
|
+
const min = start.value <= end.value ? start.value : end.value;
|
|
1765
|
+
const max = start.value <= end.value ? end.value : start.value;
|
|
1766
|
+
return {
|
|
1767
|
+
family: start.type,
|
|
1768
|
+
start: min,
|
|
1769
|
+
endExclusive: max + 1n
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
if (trimmed.includes("/")) {
|
|
1773
|
+
return rangeFromCidr(trimmed);
|
|
1774
|
+
}
|
|
1775
|
+
const parsed = parseIp(trimmed);
|
|
1776
|
+
return {
|
|
1777
|
+
family: parsed.type,
|
|
1778
|
+
start: parsed.value,
|
|
1779
|
+
endExclusive: parsed.value + 1n
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
function rangeFromCidr(cidr) {
|
|
1783
|
+
const parsed = parseCidr(cidr);
|
|
1784
|
+
return rangeFromParsedCidr({
|
|
1785
|
+
family: parsed.type,
|
|
1786
|
+
base: subnetBaseFromCidr(parsed),
|
|
1787
|
+
prefix: parsed.prefixLength
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
function rangeFromParsedCidr(parsed) {
|
|
1791
|
+
const size = cidrBlockSize(parsed.family, parsed.prefix);
|
|
1792
|
+
return {
|
|
1793
|
+
family: parsed.family,
|
|
1794
|
+
start: parsed.base,
|
|
1795
|
+
endExclusive: parsed.base + size
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
function normalizeRanges(included, excluded) {
|
|
1799
|
+
const includedByFamily = splitByFamily(included);
|
|
1800
|
+
const excludedByFamily = splitByFamily(excluded);
|
|
1801
|
+
const normalizedV4 = subtractMergedRanges(
|
|
1802
|
+
mergeRanges(includedByFamily.v4),
|
|
1803
|
+
mergeRanges(excludedByFamily.v4)
|
|
1804
|
+
);
|
|
1805
|
+
const normalizedV6 = subtractMergedRanges(
|
|
1806
|
+
mergeRanges(includedByFamily.v6),
|
|
1807
|
+
mergeRanges(excludedByFamily.v6)
|
|
1808
|
+
);
|
|
1809
|
+
return [...normalizedV4, ...normalizedV6];
|
|
1810
|
+
}
|
|
1811
|
+
function splitByFamily(ranges) {
|
|
1812
|
+
const v4 = [];
|
|
1813
|
+
const v6 = [];
|
|
1814
|
+
for (const range of ranges) {
|
|
1815
|
+
if (range.start >= range.endExclusive) continue;
|
|
1816
|
+
if (range.family === "ipv4") {
|
|
1817
|
+
v4.push(range);
|
|
1818
|
+
} else {
|
|
1819
|
+
v6.push(range);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
return { v4, v6 };
|
|
1823
|
+
}
|
|
1824
|
+
function mergeRanges(ranges) {
|
|
1825
|
+
const sorted = [...ranges].sort((a, b) => {
|
|
1826
|
+
if (a.start === b.start) {
|
|
1827
|
+
return a.endExclusive < b.endExclusive ? -1 : a.endExclusive > b.endExclusive ? 1 : 0;
|
|
1828
|
+
}
|
|
1829
|
+
return a.start < b.start ? -1 : 1;
|
|
1830
|
+
});
|
|
1831
|
+
const merged = [];
|
|
1832
|
+
for (const current of sorted) {
|
|
1833
|
+
const last = merged.at(-1);
|
|
1834
|
+
if (!last) {
|
|
1835
|
+
merged.push({ ...current });
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1838
|
+
if (current.start <= last.endExclusive) {
|
|
1839
|
+
if (current.endExclusive > last.endExclusive) {
|
|
1840
|
+
last.endExclusive = current.endExclusive;
|
|
1841
|
+
}
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
merged.push({ ...current });
|
|
1845
|
+
}
|
|
1846
|
+
return merged;
|
|
1847
|
+
}
|
|
1848
|
+
function subtractMergedRanges(included, excluded) {
|
|
1849
|
+
if (included.length === 0) return [];
|
|
1850
|
+
if (excluded.length === 0) return included;
|
|
1851
|
+
const result = [];
|
|
1852
|
+
let j = 0;
|
|
1853
|
+
for (const incOriginal of included) {
|
|
1854
|
+
const inc = { ...incOriginal };
|
|
1855
|
+
while (j < excluded.length && excluded[j].endExclusive <= inc.start) {
|
|
1856
|
+
j++;
|
|
1857
|
+
}
|
|
1858
|
+
while (j < excluded.length) {
|
|
1859
|
+
const exc = excluded[j];
|
|
1860
|
+
if (exc.start >= inc.endExclusive) {
|
|
1861
|
+
break;
|
|
1862
|
+
}
|
|
1863
|
+
if (exc.start <= inc.start) {
|
|
1864
|
+
inc.start = inc.start < exc.endExclusive ? exc.endExclusive : inc.start;
|
|
1865
|
+
if (inc.start >= inc.endExclusive) {
|
|
1866
|
+
break;
|
|
1867
|
+
}
|
|
1868
|
+
if (exc.endExclusive <= inc.start) {
|
|
1869
|
+
j++;
|
|
1870
|
+
}
|
|
1871
|
+
continue;
|
|
1872
|
+
}
|
|
1873
|
+
result.push({
|
|
1874
|
+
family: inc.family,
|
|
1875
|
+
start: inc.start,
|
|
1876
|
+
endExclusive: exc.start
|
|
1877
|
+
});
|
|
1878
|
+
inc.start = exc.endExclusive;
|
|
1879
|
+
if (inc.start >= inc.endExclusive) {
|
|
1880
|
+
break;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
if (inc.start < inc.endExclusive) {
|
|
1884
|
+
result.push(inc);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
return result;
|
|
1888
|
+
}
|
|
1889
|
+
function rangesToCanonicalSubnets(ranges) {
|
|
1890
|
+
const subnets = [];
|
|
1891
|
+
for (const range of ranges) {
|
|
1892
|
+
for (const cidr of rangeToCidrs(range)) {
|
|
1893
|
+
const baseAddress = ipToString(cidr.family, cidr.base);
|
|
1894
|
+
subnets.push({
|
|
1895
|
+
type: cidr.family,
|
|
1896
|
+
baseAddress,
|
|
1897
|
+
prefixLength: cidr.prefix
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return sortSubnetsCanonical(subnets);
|
|
1902
|
+
}
|
|
1903
|
+
function sortSubnetsCanonical(subnets) {
|
|
1904
|
+
return [...subnets].sort((a, b) => {
|
|
1905
|
+
const aFamily = a.type;
|
|
1906
|
+
const bFamily = b.type;
|
|
1907
|
+
if (aFamily !== bFamily) {
|
|
1908
|
+
return aFamily === "ipv4" ? -1 : 1;
|
|
1909
|
+
}
|
|
1910
|
+
const aBase = parseIp(a.baseAddress).value;
|
|
1911
|
+
const bBase = parseIp(b.baseAddress).value;
|
|
1912
|
+
if (aBase !== bBase) {
|
|
1913
|
+
return aBase < bBase ? -1 : 1;
|
|
1914
|
+
}
|
|
1915
|
+
return a.prefixLength - b.prefixLength;
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
function rangeToCidrs(range) {
|
|
1919
|
+
const bits = range.family === "ipv4" ? 32 : 128;
|
|
1920
|
+
const result = [];
|
|
1921
|
+
let current = range.start;
|
|
1922
|
+
while (current < range.endExclusive) {
|
|
1923
|
+
const remaining = range.endExclusive - current;
|
|
1924
|
+
const maxAligned = current === 0n ? 1n << BigInt(bits) : current & -current;
|
|
1925
|
+
const maxByRemaining = highestPowerOfTwoAtMost(remaining);
|
|
1926
|
+
const blockSize = maxAligned < maxByRemaining ? maxAligned : maxByRemaining;
|
|
1927
|
+
const prefix = bits - bitLength(blockSize) + 1;
|
|
1928
|
+
result.push({
|
|
1929
|
+
family: range.family,
|
|
1930
|
+
base: current,
|
|
1931
|
+
prefix
|
|
1932
|
+
});
|
|
1933
|
+
current += blockSize;
|
|
1934
|
+
}
|
|
1935
|
+
return result;
|
|
1936
|
+
}
|
|
1937
|
+
function highestPowerOfTwoAtMost(value) {
|
|
1938
|
+
if (value <= 0n) {
|
|
1939
|
+
throw new Error("Value must be positive");
|
|
1940
|
+
}
|
|
1941
|
+
return 1n << BigInt(bitLength(value) - 1);
|
|
1942
|
+
}
|
|
1943
|
+
function bitLength(value) {
|
|
1944
|
+
return value.toString(2).length;
|
|
1292
1945
|
}
|
|
1293
1946
|
function generatePassword() {
|
|
1294
1947
|
return secureMask.apply(randomBytes(32)).password;
|
|
@@ -1460,7 +2113,37 @@ async function createServerEntity({
|
|
|
1460
2113
|
}
|
|
1461
2114
|
});
|
|
1462
2115
|
}
|
|
2116
|
+
function filterByExpression(items, expression, getContext = (item) => item) {
|
|
2117
|
+
const { evaluate } = compile(expression);
|
|
2118
|
+
return items.filter((item) => evaluate(getContext(item)));
|
|
2119
|
+
}
|
|
2120
|
+
function filterWithMetadataByExpression(items, expression, getContext = (item) => item) {
|
|
2121
|
+
return filterByExpression(
|
|
2122
|
+
items,
|
|
2123
|
+
expression,
|
|
2124
|
+
(item) => getContext({ ...item, metadata: item.metadata ? flattenMetadata(item.metadata) : void 0 })
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
function flattenMetadata(metadata) {
|
|
2128
|
+
const result = {};
|
|
2129
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
2130
|
+
const path = key.split(".");
|
|
2131
|
+
let current = result;
|
|
2132
|
+
for (let i = 0; i < path.length; i++) {
|
|
2133
|
+
const segment = path[i];
|
|
2134
|
+
if (i === path.length - 1) {
|
|
2135
|
+
current[segment] = value;
|
|
2136
|
+
} else {
|
|
2137
|
+
if (!(segment in current)) {
|
|
2138
|
+
current[segment] = {};
|
|
2139
|
+
}
|
|
2140
|
+
current = current[segment];
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
return result;
|
|
2145
|
+
}
|
|
1463
2146
|
|
|
1464
|
-
export { AccessPointRoute, Command, DnsRecord, DnsRecordSet, GatewayRoute, ImplementationMediator, MaterializedFile, MaterializedFolder, TlsCertificate, archiveFromFolder, assetFromFile, createServerBundle, createServerEntity, createSshHostKeyFile, createSshTerminal, dnsRecordMediator, fetchFileSize,
|
|
1465
|
-
//# sourceMappingURL=chunk-
|
|
1466
|
-
//# sourceMappingURL=chunk-
|
|
2147
|
+
export { AccessPointRoute, Command, DnsRecord, DnsRecordSet, GatewayRoute, ImplementationMediator, MaterializedFile, MaterializedFolder, TlsCertificate, addEndpointMetadata, addressToCidr, archiveFromFolder, areImplRefsEqual, assertEndpointLevel, assetFromFile, checkEndpointLevel, createAddressSpace, createServerBundle, createServerEntity, createSshHostKeyFile, createSshTerminal, dnsRecordMediator, doesAddressBelongToSubnet, endpointToString, fetchFileSize, filterByExpression, filterWithMetadataByExpression, flattenMetadata, gatewayRouteMediator, generateKey, generatePassword, generateSshPrivateKey, getNameByEndpoint, getServerConnection, isPrivateAddress, l3EndpointToCidr, l3EndpointToL4, l3EndpointToString, l4EndpointToFullString, l4EndpointToL7, l4EndpointToString, l7EndpointToString, mergeAddresses, mergeEndpoints, parseAddress, parseEndpoint, parseEndpoints, parseL4Protocol, parseSubnet, parseSubnets, privateIpV4Subnets, privateIpV6Subnets, privateSubnets, replaceEndpointBase, sshPrivateKeyToKeyPair, subnetToString, tlsCertificateMediator };
|
|
2148
|
+
//# sourceMappingURL=chunk-X5BK6JSN.js.map
|
|
2149
|
+
//# sourceMappingURL=chunk-X5BK6JSN.js.map
|