@highstate/common 0.9.14 → 0.9.16
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-HZBJ6LLS.js +1057 -0
- package/dist/chunk-HZBJ6LLS.js.map +1 -0
- package/dist/highstate.manifest.json +9 -9
- package/dist/index.js +2 -50
- package/dist/index.js.map +1 -1
- package/dist/units/dns/record-set/index.js +4 -6
- package/dist/units/dns/record-set/index.js.map +1 -1
- package/dist/units/existing-server/index.js +7 -13
- package/dist/units/existing-server/index.js.map +1 -1
- package/dist/units/network/l3-endpoint/index.js +6 -9
- package/dist/units/network/l3-endpoint/index.js.map +1 -1
- package/dist/units/network/l4-endpoint/index.js +6 -9
- package/dist/units/network/l4-endpoint/index.js.map +1 -1
- package/dist/units/script/index.js +6 -9
- package/dist/units/script/index.js.map +1 -1
- package/dist/units/server-dns/index.js +7 -11
- package/dist/units/server-dns/index.js.map +1 -1
- package/dist/units/server-patch/index.js +7 -11
- package/dist/units/server-patch/index.js.map +1 -1
- package/dist/units/ssh/key-pair/index.js +18 -12
- package/dist/units/ssh/key-pair/index.js.map +1 -1
- package/package.json +22 -7
- package/src/shared/command.ts +19 -9
- package/src/shared/files.ts +730 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/network.ts +88 -1
- package/src/shared/ssh.ts +47 -19
- package/src/units/existing-server/index.ts +1 -1
- package/src/units/remote-folder/index.ts +0 -0
- package/src/units/server-dns/index.ts +1 -1
- package/src/units/server-patch/index.ts +1 -1
- package/src/units/ssh/key-pair/index.ts +12 -2
- package/dist/chunk-2JB6FMYR.js +0 -531
- package/dist/chunk-2JB6FMYR.js.map +0 -1
package/src/shared/index.ts
CHANGED
package/src/shared/network.ts
CHANGED
@@ -19,6 +19,13 @@ export type InputL3Endpoint = network.L3Endpoint | string
|
|
19
19
|
*/
|
20
20
|
export type InputL4Endpoint = network.L4Endpoint | string
|
21
21
|
|
22
|
+
/**
|
23
|
+
* The L7 endpoint for some service.
|
24
|
+
*
|
25
|
+
* The format is: `appProtocol://endpoint[:port][/resource]`
|
26
|
+
*/
|
27
|
+
export type InputL7Endpoint = network.L7Endpoint | string
|
28
|
+
|
22
29
|
/**
|
23
30
|
* Stringifies a L3 endpoint object into a string.
|
24
31
|
*
|
@@ -40,7 +47,6 @@ export function l3EndpointToString(l3Endpoint: network.L3Endpoint): string {
|
|
40
47
|
* Stringifies a L4 endpoint object into a string.
|
41
48
|
*
|
42
49
|
* @param l4Endpoint The L4 endpoint object to stringify.
|
43
|
-
*
|
44
50
|
* @returns The string representation of the L4 endpoint.
|
45
51
|
*/
|
46
52
|
export function l4EndpointToString(l4Endpoint: network.L4Endpoint): string {
|
@@ -51,6 +57,37 @@ export function l4EndpointToString(l4Endpoint: network.L4Endpoint): string {
|
|
51
57
|
return `${l3EndpointToString(l4Endpoint)}:${l4Endpoint.port}`
|
52
58
|
}
|
53
59
|
|
60
|
+
/**
|
61
|
+
* Stringifies a L4 endpoint object into a string with protocol.
|
62
|
+
*
|
63
|
+
* @param l4Endpoint The L4 endpoint object to stringify.
|
64
|
+
* @returns The string representation of the L4 endpoint with protocol.
|
65
|
+
*/
|
66
|
+
export function l4EndpointWithProtocolToString(l4Endpoint: network.L4Endpoint): string {
|
67
|
+
const protocol = `${l4Endpoint.protocol}://`
|
68
|
+
|
69
|
+
return `${protocol}${l4EndpointToString(l4Endpoint)}`
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Stringifies a L7 endpoint object into a string.
|
74
|
+
*
|
75
|
+
* The format is: `appProtocol://endpoint[:port][/resource]`
|
76
|
+
* @param l7Endpoint The L7 endpoint object to stringify.
|
77
|
+
* @returns The string representation of the L7 endpoint.
|
78
|
+
*/
|
79
|
+
export function l7EndpointToString(l7Endpoint: network.L7Endpoint): string {
|
80
|
+
const protocol = `${l7Endpoint.appProtocol}://`
|
81
|
+
|
82
|
+
let endpoint = l4EndpointToString(l7Endpoint)
|
83
|
+
|
84
|
+
if (l7Endpoint.resource) {
|
85
|
+
endpoint += `/${l7Endpoint.resource}`
|
86
|
+
}
|
87
|
+
|
88
|
+
return `${protocol}${endpoint}`
|
89
|
+
}
|
90
|
+
|
54
91
|
/**
|
55
92
|
* Stringifies a L3 or L4 endpoint object into a string.
|
56
93
|
*
|
@@ -68,6 +105,9 @@ export function l34EndpointToString(l34Endpoint: network.L34Endpoint): string {
|
|
68
105
|
const L34_ENDPOINT_RE =
|
69
106
|
/^(?:(?<protocol>[a-z]+):\/\/)?(?:(?:\[?(?<ipv6>[0-9A-Fa-f:]+)\]?)|(?<ipv4>(?:\d{1,3}\.){3}\d{1,3})|(?<hostname>[a-zA-Z0-9-*]+(?:\.[a-zA-Z0-9-*]+)*))(?::(?<port>\d{1,5}))?$/
|
70
107
|
|
108
|
+
const L7_ENDPOINT_RE =
|
109
|
+
/^(?<appProtocol>[a-z]+):\/\/(?:(?:\[?(?<ipv6>[0-9A-Fa-f:]+)\]?)|(?<ipv4>(?:\d{1,3}\.){3}\d{1,3})|(?<hostname>[a-zA-Z0-9-*]+(?:\.[a-zA-Z0-9-*]+)*))(?::(?<port>\d{1,5}))?(?:\/(?<resource>.*))?$/
|
110
|
+
|
71
111
|
/**
|
72
112
|
* Parses a L3 or L4 endpoint from a string.
|
73
113
|
*
|
@@ -275,6 +315,53 @@ export function l3EndpointToCidr(l3Endpoint: network.L3Endpoint): string {
|
|
275
315
|
}
|
276
316
|
}
|
277
317
|
|
318
|
+
const udpAppProtocols = ["dns", "dhcp"]
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Parses a L7 endpoint from a string.
|
322
|
+
*
|
323
|
+
* The format is: `appProtocol://endpoint[:port][/resource]`
|
324
|
+
*
|
325
|
+
* @param l7Endpoint The L7 endpoint string to parse.
|
326
|
+
* @returns The parsed L7 endpoint object.
|
327
|
+
*/
|
328
|
+
export function parseL7Endpoint(l7Endpoint: InputL7Endpoint): network.L7Endpoint {
|
329
|
+
if (typeof l7Endpoint === "object") {
|
330
|
+
return l7Endpoint
|
331
|
+
}
|
332
|
+
|
333
|
+
const match = l7Endpoint.match(L7_ENDPOINT_RE)
|
334
|
+
if (!match) {
|
335
|
+
throw new Error(`Invalid L7 endpoint: "${l7Endpoint}"`)
|
336
|
+
}
|
337
|
+
|
338
|
+
const { appProtocol, ipv6, ipv4, hostname, port, resource } = match.groups!
|
339
|
+
|
340
|
+
let visibility: network.EndpointVisibility = "public"
|
341
|
+
|
342
|
+
if (ipv4 && IPV4_PRIVATE_REGEX.test(ipv4)) {
|
343
|
+
visibility = "external"
|
344
|
+
} else if (ipv6 && IPV6_PRIVATE_REGEX.test(ipv6)) {
|
345
|
+
visibility = "external"
|
346
|
+
}
|
347
|
+
|
348
|
+
return {
|
349
|
+
type: ipv6 ? "ipv6" : ipv4 ? "ipv4" : "hostname",
|
350
|
+
visibility,
|
351
|
+
address: ipv6 || ipv4,
|
352
|
+
hostname: hostname,
|
353
|
+
|
354
|
+
// Default port for L7 endpoints (TODO: add more specific defaults for common protocols)
|
355
|
+
port: port ? parseInt(port, 10) : 443,
|
356
|
+
|
357
|
+
// L7 endpoints typically use TCP, but can also use UDP for specific protocols
|
358
|
+
protocol: udpAppProtocols.includes(appProtocol) ? "udp" : "tcp",
|
359
|
+
|
360
|
+
appProtocol,
|
361
|
+
resource: resource || "",
|
362
|
+
} as network.L7Endpoint
|
363
|
+
}
|
364
|
+
|
278
365
|
/**
|
279
366
|
* Updates the endpoints based on the given mode.
|
280
367
|
*
|
package/src/shared/ssh.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { common, network, ssh } from "@highstate/library"
|
2
2
|
import {
|
3
3
|
getOrCreateSecret,
|
4
|
-
getUnitInstanceName,
|
5
4
|
output,
|
6
5
|
Output,
|
7
6
|
secret,
|
@@ -11,7 +10,8 @@ import {
|
|
11
10
|
import getKeys, { PrivateExport } from "micro-key-producer/ssh.js"
|
12
11
|
import { randomBytes } from "micro-key-producer/utils.js"
|
13
12
|
import { local, remote } from "@pulumi/command"
|
14
|
-
import
|
13
|
+
import * as images from "../../assets/images.json"
|
14
|
+
import { l3EndpointToString, l3ToL4Endpoint } from "./network"
|
15
15
|
|
16
16
|
export function createSshTerminal(
|
17
17
|
credentials: Input<ssh.Credentials | undefined>,
|
@@ -40,25 +40,44 @@ export function createSshTerminal(
|
|
40
40
|
|
41
41
|
return {
|
42
42
|
name: "ssh",
|
43
|
-
title: `SSH: ${getUnitInstanceName()}`,
|
44
|
-
description: "Connect to the server via SSH",
|
45
|
-
image: "ghcr.io/exeteres/highstate/terminal-ssh",
|
46
|
-
command,
|
47
43
|
|
48
|
-
|
49
|
-
"
|
50
|
-
|
51
|
-
"
|
52
|
-
|
53
|
-
mode: 0o600,
|
54
|
-
},
|
44
|
+
meta: {
|
45
|
+
title: "Shell",
|
46
|
+
description: "Connect to the server via SSH",
|
47
|
+
icon: "gg:remote",
|
48
|
+
},
|
55
49
|
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
spec: {
|
51
|
+
image: images["terminal-ssh"].image,
|
52
|
+
command,
|
53
|
+
|
54
|
+
files: {
|
55
|
+
"/password": credentials.password,
|
56
|
+
|
57
|
+
"/private_key": credentials.keyPair?.privateKey && {
|
58
|
+
content: {
|
59
|
+
type: "embedded",
|
60
|
+
value: credentials.keyPair?.privateKey,
|
61
|
+
},
|
62
|
+
meta: {
|
63
|
+
name: "private_key",
|
64
|
+
mode: 0o600,
|
65
|
+
},
|
66
|
+
},
|
67
|
+
|
68
|
+
"/known_hosts": {
|
69
|
+
content: {
|
70
|
+
type: "embedded",
|
71
|
+
value: `${l3EndpointToString(endpoint)} ${credentials.hostKey}`,
|
72
|
+
},
|
73
|
+
meta: {
|
74
|
+
name: "known_hosts",
|
75
|
+
mode: 0o644,
|
76
|
+
},
|
77
|
+
},
|
59
78
|
},
|
60
79
|
},
|
61
|
-
}
|
80
|
+
} satisfies InstanceTerminal
|
62
81
|
})
|
63
82
|
}
|
64
83
|
|
@@ -75,13 +94,13 @@ export function privateKeyToKeyPair(privateKeyString: Input<string>): Output<ssh
|
|
75
94
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
76
95
|
const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array
|
77
96
|
|
78
|
-
const { fingerprint, publicKey
|
97
|
+
const { fingerprint, publicKey } = getKeys(privKey.slice(0, 32))
|
79
98
|
|
80
99
|
return output({
|
81
100
|
type: "ed25519" as const,
|
82
101
|
fingerprint,
|
83
102
|
publicKey,
|
84
|
-
privateKey: secret(
|
103
|
+
privateKey: secret(privateKeyString),
|
85
104
|
})
|
86
105
|
})
|
87
106
|
}
|
@@ -114,6 +133,7 @@ export function createServerEntity(
|
|
114
133
|
sshUser = "root",
|
115
134
|
sshPassword?: Input<string | undefined>,
|
116
135
|
sshPrivateKey?: Input<string>,
|
136
|
+
hasSsh = true,
|
117
137
|
): Output<common.Server> {
|
118
138
|
const connection = output({
|
119
139
|
host: l3EndpointToString(endpoint),
|
@@ -124,8 +144,16 @@ export function createServerEntity(
|
|
124
144
|
dialErrorLimit: 3,
|
125
145
|
})
|
126
146
|
|
147
|
+
if (!hasSsh) {
|
148
|
+
return output({
|
149
|
+
hostname: fallbackHostname,
|
150
|
+
endpoints: [endpoint],
|
151
|
+
})
|
152
|
+
}
|
153
|
+
|
127
154
|
const command = new local.Command("check-ssh", {
|
128
155
|
create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo "up" || echo "down"`,
|
156
|
+
triggers: [Date.now()],
|
129
157
|
})
|
130
158
|
|
131
159
|
return command.stdout.apply(result => {
|
File without changes
|
@@ -2,14 +2,24 @@ import { ssh } from "@highstate/library"
|
|
2
2
|
import { forUnit, getOrCreateSecret } from "@highstate/pulumi"
|
3
3
|
import { generatePrivateKey, privateKeyToKeyPair } from "../../../shared"
|
4
4
|
|
5
|
-
const { secrets, outputs } = forUnit(ssh.keyPair)
|
5
|
+
const { name, secrets, outputs } = forUnit(ssh.keyPair)
|
6
6
|
|
7
7
|
const privateKey = getOrCreateSecret(secrets, "privateKey", generatePrivateKey)
|
8
8
|
const keyPair = privateKeyToKeyPair(privateKey)
|
9
9
|
|
10
10
|
export default outputs({
|
11
11
|
keyPair: privateKeyToKeyPair(privateKey),
|
12
|
-
|
12
|
+
publicKeyFile: {
|
13
|
+
meta: {
|
14
|
+
name: `${name}.pub`,
|
15
|
+
mode: 0o644,
|
16
|
+
},
|
17
|
+
content: {
|
18
|
+
type: "embedded",
|
19
|
+
value: keyPair.publicKey,
|
20
|
+
},
|
21
|
+
},
|
22
|
+
$statusFields: {
|
13
23
|
fingerprint: keyPair.fingerprint,
|
14
24
|
publicKey: keyPair.publicKey,
|
15
25
|
},
|