@marcusrbrown/infra 0.9.18 → 0.10.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/package.json +2 -1
- package/src/__snapshots__/cli.test.ts.snap +34 -1
- package/src/cli.ts +2 -0
- package/src/commands/mcp.test.ts +17 -2
- package/src/commands/mcp.ts +6 -0
- package/src/commands/status.test.ts +46 -0
- package/src/commands/status.ts +12 -3
- package/src/commands/vpn/client.test.ts +379 -0
- package/src/commands/vpn/client.ts +313 -0
- package/src/commands/vpn/deploy.test.ts +208 -0
- package/src/commands/vpn/deploy.ts +143 -0
- package/src/commands/vpn/host.test.ts +89 -0
- package/src/commands/vpn/host.ts +36 -0
- package/src/commands/vpn/index.ts +15 -0
- package/src/commands/vpn/logs.test.ts +234 -0
- package/src/commands/vpn/logs.ts +136 -0
- package/src/commands/vpn/shared.test.ts +334 -0
- package/src/commands/vpn/shared.ts +158 -0
- package/src/commands/vpn/status.test.ts +142 -0
- package/src/commands/vpn/status.ts +78 -0
- package/src/conventions.test.ts +12 -1
- package/src/resources/known_hosts +18 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {goke} from 'goke'
|
|
2
|
+
import type {ActionCtx} from '../../lib/action-ctx'
|
|
3
|
+
|
|
4
|
+
import {z} from 'zod'
|
|
5
|
+
|
|
6
|
+
import {validateVpnHost} from './host'
|
|
7
|
+
import {getVpnWgStatus, type SpawnFn} from './shared'
|
|
8
|
+
|
|
9
|
+
export {getVpnStatusSummary} from './shared'
|
|
10
|
+
|
|
11
|
+
declare const process: {
|
|
12
|
+
env: Record<string, string | undefined>
|
|
13
|
+
exitCode?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── Action (exported for direct testing) ────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export async function vpnStatusAction(
|
|
19
|
+
options: {key?: string | undefined},
|
|
20
|
+
ctx: ActionCtx,
|
|
21
|
+
spawn?: SpawnFn,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
const hostEnvKey = options.key ?? 'VPN_HOST'
|
|
24
|
+
const host = process.env[hostEnvKey]
|
|
25
|
+
|
|
26
|
+
if (!host) {
|
|
27
|
+
ctx.console.error(`VPN host not set. Export ${hostEnvKey} or pass --key <env-name> pointing to a set variable.`)
|
|
28
|
+
ctx.process.exit(1)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
validateVpnHost(host)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
ctx.console.error(error instanceof Error ? error.message : String(error))
|
|
36
|
+
ctx.process.exit(1)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ctx.console.log('VPN status')
|
|
41
|
+
ctx.console.log('')
|
|
42
|
+
|
|
43
|
+
const result = await getVpnWgStatus(host, spawn)
|
|
44
|
+
|
|
45
|
+
if (!result.ok && !result.wgShow) {
|
|
46
|
+
ctx.console.error(`Error: ${result.error ?? 'Unknown error'}`)
|
|
47
|
+
ctx.process.exit(1)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const wgShow = result.wgShow
|
|
52
|
+
|
|
53
|
+
if (wgShow?.serverPublicKey) {
|
|
54
|
+
ctx.console.log(`Server public key: ${wgShow.serverPublicKey}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ctx.console.log(`Peers: ${wgShow?.peerCount ?? 0}`)
|
|
58
|
+
ctx.console.log('')
|
|
59
|
+
|
|
60
|
+
if (result.ok && wgShow?.interfaceUp) {
|
|
61
|
+
ctx.console.log('Status: OK')
|
|
62
|
+
} else {
|
|
63
|
+
ctx.console.log('Status: DEGRADED (WireGuard interface not up)')
|
|
64
|
+
ctx.process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Command registration ─────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export function registerVpnStatus(cli: ReturnType<typeof goke>): void {
|
|
71
|
+
cli
|
|
72
|
+
.command('vpn status', 'Show operational health of the VPN box via SSH and wg show.')
|
|
73
|
+
.option(
|
|
74
|
+
'--key [key]',
|
|
75
|
+
z.string().describe('Environment variable name holding the SSH host. Falls back to VPN_HOST when omitted.'),
|
|
76
|
+
)
|
|
77
|
+
.action((options, ctx) => vpnStatusAction(options, ctx))
|
|
78
|
+
}
|
package/src/conventions.test.ts
CHANGED
|
@@ -11,7 +11,7 @@ const ALLOWED_SHELL_SCRIPTS = new Set(['apps/keeweb/deploy.sh'])
|
|
|
11
11
|
// MCP drift-guard: sensitive tool set (two-layer security model)
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
//
|
|
14
|
-
// These
|
|
14
|
+
// These commands are source-gated: they are NOT in MCP_ALLOWLIST and are
|
|
15
15
|
// therefore never registered as MCP tools. They remain CLI-only.
|
|
16
16
|
//
|
|
17
17
|
// WHY each is source-gated (primary layer — MCP_ALLOWLIST exclusion):
|
|
@@ -21,6 +21,11 @@ const ALLOWED_SHELL_SCRIPTS = new Set(['apps/keeweb/deploy.sh'])
|
|
|
21
21
|
// gateway backup — secret-bearing: writes CA private key material to a tarball
|
|
22
22
|
// cliproxy keys list — secret-disclosing: prints live bearer tokens in plaintext
|
|
23
23
|
// cliproxy config get — secret-disclosing: dumps management config incl. management key
|
|
24
|
+
// vpn deploy — mutating: deploys WireGuard config to live VPN box
|
|
25
|
+
// vpn logs — sensitive: streams journalctl logs that may reveal peer IPs/traffic
|
|
26
|
+
// vpn client add — mutating: generates keypair + appends peer + triggers redeploy
|
|
27
|
+
// vpn client list — sensitive: lists peer public keys and tunnel IPs
|
|
28
|
+
// vpn client remove — mutating: removes peer + triggers redeploy
|
|
24
29
|
//
|
|
25
30
|
// Defense-in-depth (secondary layer — opencode.jsonc `permission: deny`):
|
|
26
31
|
// Even if MCP_ALLOWLIST were mistakenly re-expanded, opencode's native tool
|
|
@@ -37,6 +42,12 @@ const SENSITIVE_MCP_COMMANDS: readonly string[] = [
|
|
|
37
42
|
'gateway backup',
|
|
38
43
|
'cliproxy keys list',
|
|
39
44
|
'cliproxy config get',
|
|
45
|
+
// VPN: mutating / sensitive / log-streaming — CLI-only
|
|
46
|
+
'vpn deploy',
|
|
47
|
+
'vpn logs',
|
|
48
|
+
'vpn client add',
|
|
49
|
+
'vpn client list',
|
|
50
|
+
'vpn client remove',
|
|
40
51
|
]
|
|
41
52
|
|
|
42
53
|
// Accepted version-comment forms on SHA-pinned `uses:` lines:
|
|
@@ -26,3 +26,21 @@ metrics.fro.bot ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy
|
|
|
26
26
|
|1|8o6qExp0klmE80pMU2CYvmbGqSQ=|064FdS/NWWEIU7EjmGBCwCek+DE= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICRCMPA99bZJ1w+QdZPrTzLK06LF7t91tb0GbKfBkA72
|
|
27
27
|
|1|1nwXP2iRwBq0GXyE94EXYJuaUCQ=|Wxoe6EvzjZbk02Hr4mx1YZOciUM= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCma/Y9qRPlZ8v1wnQQBfwghQ0XbtSMn7QHVgIFGBjbO+Xqim7Lv9EArn58HIVACsZKMD0e9T90r70TDXYTWuZ6+RQ2x2soE6DcG/T/2wyan40hVtXLSCg8hx9DNjLR3RBwkiJcbdrYCqpDmi4kOuJAJ+b5JxUhJAkdF5M9xhUko59HtUm5I/j1T2Df60+7CdKMGZKMrCRios/q4lvgImuWS3aZuNQNuJiv6rfhX7L9LqezlJUgw1AbOzaqPXbZ9xpzt8yklGZQNa5I/CS6pEgn4/SKiRsetpgd4SCB9UuCR7ia/M0VU8itNPMZY11Pi+YwJKfJVZDtS+OSFkFxea8B1MyxDZkvOwnzYVx9xG6W48Oi06x4pQgJcqq5V52xM4upSlVtws5gTLNm/3KaJZ3kb7IK4HnTEUQrz/UShtyP1crsqwj5eoDnvcUfaMHx/YC/7LlQFp8AO8W/KEYWzlycJpZLqPrO/gqoiR0AG9NMP0SSRaD7M2pcQ4tKM1xyopE=
|
|
28
28
|
|1|lwRlQiSSd0h7iti76fvRXTtbkSw=|D//yCCMd1iZftlbjJ0NhKCCf5/8= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOm6bQkB9dGaPJAVbk9IrVxZSM0KrkUikv8JgVrW0iJMKorJ233GaK03n2/5cuAJ/D3z6nN/pF6QGApWfm6s/JA=
|
|
29
|
+
|
|
30
|
+
# wg-egress static IP (52.208.116.13)
|
|
31
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
32
|
+
52.208.116.13 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILA4LGgnvBjq/yoOKLjzFefGzo9gAspxwfbY8aACj/3q
|
|
33
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
34
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
35
|
+
52.208.116.13 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCOSk8SjUJHwOzUbb/R0XWu2h/BmtPjDsbzkGZIryUZ00OBeSb2V70BHVagC41BH7XEfMT5jxFJKIAHx4iqcdjrjUfR+lTIJVdHjDpCHLxzKBRQsE9uLPkSoz+DR2Y9mg8tkBXWc2rol+SqjnOWkGsavJey7+bZwEadFPBCwJnCSkn+iIdu6BO2e0X8wvpLC+ThhWXPu8tUiPY5rPgDXtCzj5rROCL79Vh5HcWONmoOzTAnaH6WMlLedX37xM4Vayl62MKyFIQE7Pw/zg2l5StEzIvQjornMs09unFlZnWYla3+T3vwTaqh6B4sbdM332+Rxw0FoHb99xgWqHpk5BnZe6Dc+l6ZHVY6p8j1CSa3/8JMGzr62/OlYfnIOwW3HvHG8R6jmi++LlmfeGmUDOypl9yfAVNPRazEVXq+eWC1helxoRBvkabwpAAKg3aihQDxLPfEi1q8qYORzb/ZAU8z8HqBAfDD6wIjacq1HCUKhZA3QN+E2skjD+IdsVx4FF0=
|
|
36
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
37
|
+
52.208.116.13 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ5sx3iCboYm5TahbLThB4fWbnz391FDUFcRg+CfvXRCKHAuL8RTdkUaWK3OaFA4Xu1+37B0k3RYfbsBZ6vg6So=
|
|
38
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
39
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
40
|
+
|1|j1LnSfd4ntnFE7CxqDqu6aaQWLo=|A8bf0bH74R6KoEHVjeDDemArv9A= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ5sx3iCboYm5TahbLThB4fWbnz391FDUFcRg+CfvXRCKHAuL8RTdkUaWK3OaFA4Xu1+37B0k3RYfbsBZ6vg6So=
|
|
41
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
42
|
+
|1|4GGoI+hl2sBnBBkgwvuNYyDWsVs=|M6RYPIm/v9wCDInvvra6yLcQneY= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCOSk8SjUJHwOzUbb/R0XWu2h/BmtPjDsbzkGZIryUZ00OBeSb2V70BHVagC41BH7XEfMT5jxFJKIAHx4iqcdjrjUfR+lTIJVdHjDpCHLxzKBRQsE9uLPkSoz+DR2Y9mg8tkBXWc2rol+SqjnOWkGsavJey7+bZwEadFPBCwJnCSkn+iIdu6BO2e0X8wvpLC+ThhWXPu8tUiPY5rPgDXtCzj5rROCL79Vh5HcWONmoOzTAnaH6WMlLedX37xM4Vayl62MKyFIQE7Pw/zg2l5StEzIvQjornMs09unFlZnWYla3+T3vwTaqh6B4sbdM332+Rxw0FoHb99xgWqHpk5BnZe6Dc+l6ZHVY6p8j1CSa3/8JMGzr62/OlYfnIOwW3HvHG8R6jmi++LlmfeGmUDOypl9yfAVNPRazEVXq+eWC1helxoRBvkabwpAAKg3aihQDxLPfEi1q8qYORzb/ZAU8z8HqBAfDD6wIjacq1HCUKhZA3QN+E2skjD+IdsVx4FF0=
|
|
43
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
44
|
+
|1|6g8lttzHI3BCW6rIf1/Bteo1KcM=|bNbGP2QK2pUdPmhplw6eVzspe5g= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILA4LGgnvBjq/yoOKLjzFefGzo9gAspxwfbY8aACj/3q
|
|
45
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|
|
46
|
+
# 52.208.116.13:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15
|