@lamalibre/create-lamaste 4.0.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.
Files changed (270) hide show
  1. package/LICENSE.md +118 -0
  2. package/README.md +114 -0
  3. package/bin/create-lamaste.js +15 -0
  4. package/package.json +54 -0
  5. package/scripts/bundle-vendor.js +110 -0
  6. package/scripts/lamaste-cert-info +62 -0
  7. package/scripts/lamaste-certbot +96 -0
  8. package/scripts/lamaste-priv +693 -0
  9. package/src/index.js +646 -0
  10. package/src/lib/accounts.js +86 -0
  11. package/src/lib/binaries.js +137 -0
  12. package/src/lib/cert-help-page.js +160 -0
  13. package/src/lib/env.js +132 -0
  14. package/src/lib/http-redirect.js +38 -0
  15. package/src/lib/ownership.js +231 -0
  16. package/src/lib/relay-services.js +103 -0
  17. package/src/lib/secrets.js +19 -0
  18. package/src/lib/service-config.js +311 -0
  19. package/src/lib/summary.js +131 -0
  20. package/src/lib/wrappers.js +63 -0
  21. package/src/tasks/gatekeeper.js +233 -0
  22. package/src/tasks/harden.js +293 -0
  23. package/src/tasks/mtls.js +193 -0
  24. package/src/tasks/nginx.js +265 -0
  25. package/src/tasks/node.js +110 -0
  26. package/src/tasks/panel.js +408 -0
  27. package/src/tasks/redeploy.js +438 -0
  28. package/vendor/docs/00-introduction/how-it-works.md +447 -0
  29. package/vendor/docs/00-introduction/quickstart.md +472 -0
  30. package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
  31. package/vendor/docs/01-concepts/authentication.md +464 -0
  32. package/vendor/docs/01-concepts/authorization.md +242 -0
  33. package/vendor/docs/01-concepts/certificates.md +452 -0
  34. package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
  35. package/vendor/docs/01-concepts/mtls.md +490 -0
  36. package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
  37. package/vendor/docs/01-concepts/security-model.md +600 -0
  38. package/vendor/docs/01-concepts/tickets.md +414 -0
  39. package/vendor/docs/01-concepts/tunneling.md +367 -0
  40. package/vendor/docs/02-guides/agent-setup.md +194 -0
  41. package/vendor/docs/02-guides/certificate-management.md +341 -0
  42. package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
  43. package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
  44. package/vendor/docs/02-guides/disaster-recovery.md +464 -0
  45. package/vendor/docs/02-guides/first-tunnel.md +230 -0
  46. package/vendor/docs/02-guides/installation.md +393 -0
  47. package/vendor/docs/02-guides/managing-users.md +255 -0
  48. package/vendor/docs/02-guides/onboarding.md +244 -0
  49. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
  50. package/vendor/docs/02-guides/static-sites.md +364 -0
  51. package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
  52. package/vendor/docs/03-architecture/installer-flow.md +151 -0
  53. package/vendor/docs/03-architecture/installer.md +445 -0
  54. package/vendor/docs/03-architecture/management-flow.md +288 -0
  55. package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
  56. package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
  57. package/vendor/docs/03-architecture/overview.md +541 -0
  58. package/vendor/docs/03-architecture/panel-client.md +416 -0
  59. package/vendor/docs/03-architecture/panel-server.md +695 -0
  60. package/vendor/docs/03-architecture/state-management.md +529 -0
  61. package/vendor/docs/03-architecture/system-overview.md +167 -0
  62. package/vendor/docs/04-api-reference/certificates.md +1141 -0
  63. package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
  64. package/vendor/docs/04-api-reference/onboarding.md +473 -0
  65. package/vendor/docs/04-api-reference/overview.md +404 -0
  66. package/vendor/docs/04-api-reference/services.md +267 -0
  67. package/vendor/docs/04-api-reference/settings.md +343 -0
  68. package/vendor/docs/04-api-reference/sites.md +716 -0
  69. package/vendor/docs/04-api-reference/system.md +268 -0
  70. package/vendor/docs/04-api-reference/tickets.md +618 -0
  71. package/vendor/docs/04-api-reference/tunnels.md +417 -0
  72. package/vendor/docs/04-api-reference/users.md +333 -0
  73. package/vendor/docs/05-operations/backup-and-restore.md +342 -0
  74. package/vendor/docs/05-operations/monitoring.md +285 -0
  75. package/vendor/docs/05-operations/uninstalling.md +344 -0
  76. package/vendor/docs/05-operations/upgrades.md +270 -0
  77. package/vendor/docs/06-reference/config-files.md +1080 -0
  78. package/vendor/docs/06-reference/glossary.md +528 -0
  79. package/vendor/docs/06-reference/installer-flags.md +260 -0
  80. package/vendor/docs/06-reference/ports-and-services.md +244 -0
  81. package/vendor/docs/06-reference/troubleshooting.md +665 -0
  82. package/vendor/docs/_index.json +233 -0
  83. package/vendor/docs/gatekeeper-design.md +953 -0
  84. package/vendor/docs/package.json +15 -0
  85. package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
  86. package/vendor/gatekeeper/dist/boot.d.ts +1 -0
  87. package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
  88. package/vendor/gatekeeper/dist/boot.js +17 -0
  89. package/vendor/gatekeeper/dist/boot.js.map +1 -0
  90. package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
  91. package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
  92. package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
  93. package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
  94. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
  95. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
  96. package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
  97. package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
  98. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
  99. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
  100. package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
  101. package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
  102. package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
  103. package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
  104. package/vendor/gatekeeper/dist/cli/index.js +45 -0
  105. package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
  106. package/vendor/gatekeeper/dist/index.d.ts +9 -0
  107. package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
  108. package/vendor/gatekeeper/dist/index.js +13 -0
  109. package/vendor/gatekeeper/dist/index.js.map +1 -0
  110. package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
  111. package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
  112. package/vendor/gatekeeper/dist/lib/authz.js +42 -0
  113. package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
  114. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
  115. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
  116. package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
  117. package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
  118. package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
  119. package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
  120. package/vendor/gatekeeper/dist/lib/constants.js +79 -0
  121. package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
  122. package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
  123. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
  124. package/vendor/gatekeeper/dist/lib/grants.js +314 -0
  125. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
  126. package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
  127. package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
  128. package/vendor/gatekeeper/dist/lib/groups.js +247 -0
  129. package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
  130. package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
  131. package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
  132. package/vendor/gatekeeper/dist/lib/migration.js +79 -0
  133. package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
  134. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
  135. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
  136. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  137. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
  138. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
  139. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  140. package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
  141. package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
  142. package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
  143. package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
  144. package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
  145. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
  146. package/vendor/gatekeeper/dist/lib/templates.js +151 -0
  147. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
  148. package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
  149. package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
  150. package/vendor/gatekeeper/dist/lib/types.js +2 -0
  151. package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
  152. package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
  153. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
  154. package/vendor/gatekeeper/dist/server/index.js +251 -0
  155. package/vendor/gatekeeper/dist/server/index.js.map +1 -0
  156. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
  157. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
  158. package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
  159. package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
  160. package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
  161. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
  162. package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
  163. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
  164. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
  165. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
  166. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
  167. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
  168. package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
  169. package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
  170. package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
  171. package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
  172. package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
  173. package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
  174. package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
  175. package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
  176. package/vendor/gatekeeper/package.json +49 -0
  177. package/vendor/server/bin/lamaste-server.js +204 -0
  178. package/vendor/server/package.json +31 -0
  179. package/vendor/server/src/commands/certs.js +224 -0
  180. package/vendor/server/src/commands/chisel.js +132 -0
  181. package/vendor/server/src/commands/logs.js +49 -0
  182. package/vendor/server/src/commands/plugins.js +321 -0
  183. package/vendor/server/src/commands/reset-admin.js +478 -0
  184. package/vendor/server/src/commands/restart.js +94 -0
  185. package/vendor/server/src/commands/sites.js +257 -0
  186. package/vendor/server/src/commands/status.js +158 -0
  187. package/vendor/server/src/commands/tunnels.js +365 -0
  188. package/vendor/server/src/commands/uninstall.js +326 -0
  189. package/vendor/server/src/config.js +38 -0
  190. package/vendor/server/src/exec.js +26 -0
  191. package/vendor/server/src/logger.js +39 -0
  192. package/vendor/server/src/ndjson.js +56 -0
  193. package/vendor/server/src/panel-api.js +85 -0
  194. package/vendor/server/src/state.js +45 -0
  195. package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
  196. package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
  197. package/vendor/server-ui/dist/index.html +13 -0
  198. package/vendor/serverd/package.json +44 -0
  199. package/vendor/serverd/src/boot.js +13 -0
  200. package/vendor/serverd/src/index.js +191 -0
  201. package/vendor/serverd/src/lib/app-error.js +14 -0
  202. package/vendor/serverd/src/lib/authelia.js +85 -0
  203. package/vendor/serverd/src/lib/certbot.js +74 -0
  204. package/vendor/serverd/src/lib/chisel-args.js +6 -0
  205. package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
  206. package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
  207. package/vendor/serverd/src/lib/chisel-users.js +109 -0
  208. package/vendor/serverd/src/lib/chisel.js +81 -0
  209. package/vendor/serverd/src/lib/config.js +190 -0
  210. package/vendor/serverd/src/lib/constants.js +8 -0
  211. package/vendor/serverd/src/lib/csr-signing.js +331 -0
  212. package/vendor/serverd/src/lib/enrollment.js +302 -0
  213. package/vendor/serverd/src/lib/files.js +19 -0
  214. package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
  215. package/vendor/serverd/src/lib/invite-page.js +281 -0
  216. package/vendor/serverd/src/lib/ip-utils.js +79 -0
  217. package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
  218. package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
  219. package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
  220. package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
  221. package/vendor/serverd/src/lib/mtls.js +795 -0
  222. package/vendor/serverd/src/lib/nginx.js +1090 -0
  223. package/vendor/serverd/src/lib/pki-paths.js +41 -0
  224. package/vendor/serverd/src/lib/pki-sign.js +103 -0
  225. package/vendor/serverd/src/lib/plugins.js +538 -0
  226. package/vendor/serverd/src/lib/push-install.js +390 -0
  227. package/vendor/serverd/src/lib/revocation.js +112 -0
  228. package/vendor/serverd/src/lib/services.js +131 -0
  229. package/vendor/serverd/src/lib/session.js +151 -0
  230. package/vendor/serverd/src/lib/state-db.js +130 -0
  231. package/vendor/serverd/src/lib/state.js +140 -0
  232. package/vendor/serverd/src/lib/storage.js +63 -0
  233. package/vendor/serverd/src/lib/system-stats.js +58 -0
  234. package/vendor/serverd/src/lib/tickets.js +1556 -0
  235. package/vendor/serverd/src/lib/totp.js +76 -0
  236. package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
  237. package/vendor/serverd/src/lib/user-access-session.js +142 -0
  238. package/vendor/serverd/src/lib/user-access.js +52 -0
  239. package/vendor/serverd/src/middleware/errors.js +63 -0
  240. package/vendor/serverd/src/middleware/mtls.js +120 -0
  241. package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
  242. package/vendor/serverd/src/middleware/role-guard.js +58 -0
  243. package/vendor/serverd/src/middleware/twofa-session.js +65 -0
  244. package/vendor/serverd/src/middleware/user-access-session.js +52 -0
  245. package/vendor/serverd/src/routes/enrollment.js +120 -0
  246. package/vendor/serverd/src/routes/health.js +22 -0
  247. package/vendor/serverd/src/routes/invite.js +163 -0
  248. package/vendor/serverd/src/routes/management/agents.js +182 -0
  249. package/vendor/serverd/src/routes/management/certs.js +911 -0
  250. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
  251. package/vendor/serverd/src/routes/management/identity.js +107 -0
  252. package/vendor/serverd/src/routes/management/invitations.js +166 -0
  253. package/vendor/serverd/src/routes/management/logs.js +218 -0
  254. package/vendor/serverd/src/routes/management/plugins.js +560 -0
  255. package/vendor/serverd/src/routes/management/services.js +63 -0
  256. package/vendor/serverd/src/routes/management/settings.js +325 -0
  257. package/vendor/serverd/src/routes/management/sites.js +522 -0
  258. package/vendor/serverd/src/routes/management/storage.js +192 -0
  259. package/vendor/serverd/src/routes/management/system.js +201 -0
  260. package/vendor/serverd/src/routes/management/tickets.js +457 -0
  261. package/vendor/serverd/src/routes/management/tunnels.js +709 -0
  262. package/vendor/serverd/src/routes/management/users.js +377 -0
  263. package/vendor/serverd/src/routes/management.js +36 -0
  264. package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
  265. package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
  266. package/vendor/serverd/src/routes/onboarding/index.js +18 -0
  267. package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
  268. package/vendor/serverd/src/routes/onboarding/status.js +12 -0
  269. package/vendor/serverd/src/routes/plugin-router.js +79 -0
  270. package/vendor/serverd/src/routes/user-access.js +389 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * lamaste-server certs — Certificate status and renewal.
3
+ *
4
+ * Subcommands:
5
+ * status Show certificate expiry and renewal status
6
+ * renew Force certificate renewal via certbot
7
+ * agents List agent certificates
8
+ */
9
+
10
+ import chalk from 'chalk';
11
+ import { execa } from 'execa';
12
+ import { getMtlsCerts, listAgentCerts } from '@lamalibre/lamaste/server';
13
+ import { PKI_DIR } from '../config.js';
14
+ import { exec } from '../exec.js';
15
+ import { emit, emitStep, emitError, emitComplete } from '../ndjson.js';
16
+
17
+ /**
18
+ * @param {string[]} args
19
+ * @param {{ json: boolean }} options
20
+ */
21
+ export async function runCerts(args, { json }) {
22
+ const sub = args[0] || 'status';
23
+
24
+ switch (sub) {
25
+ case 'status':
26
+ return certStatus({ json });
27
+ case 'renew':
28
+ return certRenew({ json });
29
+ case 'agents':
30
+ return agentCerts({ json });
31
+ default:
32
+ printCertUsage();
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ function printCertUsage() {
38
+ const b = chalk.bold;
39
+ const c = chalk.cyan;
40
+ console.log(`
41
+ ${b('Usage:')} lamaste-server certs <subcommand>
42
+
43
+ ${b('Subcommands:')}
44
+ ${c('status')} Show mTLS and Let's Encrypt certificate status (default)
45
+ ${c('renew')} Force Let's Encrypt certificate renewal
46
+ ${c('agents')} List agent certificates with expiry info
47
+ `);
48
+ }
49
+
50
+ /**
51
+ * @param {{ json: boolean }} options
52
+ */
53
+ async function certStatus({ json }) {
54
+ // mTLS certs
55
+ const mtlsCerts = await getMtlsCerts(PKI_DIR, exec);
56
+
57
+ // Let's Encrypt certs
58
+ let letsencryptCerts = [];
59
+ try {
60
+ const { stdout } = await execa('sudo', ['certbot', 'certificates', '--non-interactive'], {
61
+ timeout: 30000,
62
+ });
63
+ letsencryptCerts = parseCertbotOutput(stdout);
64
+ } catch {
65
+ // certbot may not be installed or may have no certs
66
+ }
67
+
68
+ if (json) {
69
+ emit({ mtls: mtlsCerts, letsencrypt: letsencryptCerts });
70
+ return;
71
+ }
72
+
73
+ const b = chalk.bold;
74
+ const c = chalk.cyan;
75
+ const g = chalk.green;
76
+ const y = chalk.yellow;
77
+ const r = chalk.red;
78
+ const d = chalk.dim;
79
+
80
+ console.log('');
81
+ console.log(b(' mTLS Certificates'));
82
+ console.log(d(' ' + '\u2500'.repeat(40)));
83
+
84
+ if (mtlsCerts.length === 0) {
85
+ console.log(` ${d('No mTLS certificates found.')}`);
86
+ }
87
+
88
+ for (const cert of mtlsCerts) {
89
+ const label = cert.type === 'mtls-ca' ? 'CA' : 'Client';
90
+ const status = cert.expiringSoon ? y('expiring soon') : g('valid');
91
+ console.log(
92
+ ` ${b(label + ':')} ${status} ${d(`expires in ${cert.daysUntilExpiry} days`)} ${d(cert.expiresAt)}`,
93
+ );
94
+ }
95
+
96
+ if (letsencryptCerts.length > 0) {
97
+ console.log('');
98
+ console.log(b(" Let's Encrypt Certificates"));
99
+ console.log(d(' ' + '\u2500'.repeat(40)));
100
+
101
+ for (const cert of letsencryptCerts) {
102
+ const daysLeft = cert.daysUntilExpiry;
103
+ const status = daysLeft <= 7 ? r('critical') : daysLeft <= 30 ? y('expiring') : g('valid');
104
+ console.log(` ${c(cert.domain)} ${status} ${d(`expires in ${daysLeft} days`)}`);
105
+ }
106
+ }
107
+
108
+ console.log('');
109
+ }
110
+
111
+ /**
112
+ * @param {{ json: boolean }} options
113
+ */
114
+ async function certRenew({ json }) {
115
+ if (json) emitStep('renew', 'running', 'Forcing certificate renewal');
116
+ else process.stderr.write(' Forcing certificate renewal...');
117
+
118
+ try {
119
+ await execa(
120
+ 'sudo',
121
+ ['certbot', 'renew', '--force-renewal', '--no-random-sleep-on-renew', '--non-interactive'],
122
+ {
123
+ timeout: 120000,
124
+ },
125
+ );
126
+
127
+ // Reload nginx to pick up new certs
128
+ await execa('sudo', ['nginx', '-t']);
129
+ await execa('sudo', ['systemctl', 'reload', 'nginx']);
130
+
131
+ if (json) {
132
+ emitStep('renew', 'complete');
133
+ emitComplete({ renewed: true });
134
+ } else {
135
+ console.log(` ${chalk.green('ok')}`);
136
+ console.log(' Certificates renewed and nginx reloaded.\n');
137
+ }
138
+ } catch (err) {
139
+ const msg = err.stderr || err.message;
140
+ if (json) {
141
+ emitStep('renew', 'failed', msg);
142
+ emitError(msg);
143
+ } else {
144
+ console.log(` ${chalk.red('failed')}`);
145
+ console.error(` ${chalk.red(msg)}\n`);
146
+ }
147
+ process.exit(1);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * @param {{ json: boolean }} options
153
+ */
154
+ async function agentCerts({ json }) {
155
+ const agents = await listAgentCerts(PKI_DIR);
156
+
157
+ if (json) {
158
+ emit({ agents });
159
+ return;
160
+ }
161
+
162
+ const b = chalk.bold;
163
+ const c = chalk.cyan;
164
+ const r = chalk.red;
165
+ const y = chalk.yellow;
166
+ const d = chalk.dim;
167
+
168
+ console.log('');
169
+ console.log(b(' Agent Certificates'));
170
+ console.log(d(' ' + '\u2500'.repeat(50)));
171
+
172
+ if (agents.length === 0) {
173
+ console.log(` ${d('No agent certificates.')}`);
174
+ console.log('');
175
+ return;
176
+ }
177
+
178
+ for (const agent of agents) {
179
+ const revoked = agent.revoked ? r(' [REVOKED]') : '';
180
+ const expiring = agent.expiringSoon ? y(' [EXPIRING]') : '';
181
+ const method = agent.enrollmentMethod === 'hardware-bound' ? d(' (hw-bound)') : '';
182
+ const certType = agent.certType === 'plugin-agent' ? d(' (plugin)') : '';
183
+ console.log(` ${c(agent.label)}${certType}${method} ${d(agent.serial)}${revoked}${expiring}`);
184
+
185
+ if (agent.capabilities.length > 0) {
186
+ console.log(` ${d('capabilities:')} ${agent.capabilities.join(', ')}`);
187
+ }
188
+
189
+ if (agent.expiresAt) {
190
+ console.log(` ${d(`expires: ${agent.expiresAt}`)}`);
191
+ }
192
+ }
193
+ console.log('');
194
+ }
195
+
196
+ /**
197
+ * Parse certbot certificates output into structured data.
198
+ * @param {string} output
199
+ * @returns {Array<{ domain: string, daysUntilExpiry: number, expiresAt: string }>}
200
+ */
201
+ function parseCertbotOutput(output) {
202
+ const certs = [];
203
+ const blocks = output.split('Certificate Name:').slice(1);
204
+
205
+ for (const block of blocks) {
206
+ const domainMatch = block.match(/Domains:\s+(.+)/);
207
+ const expiryMatch = block.match(/Expiry Date:\s+(\S+ \S+ \S+ \S+ \S+)/);
208
+
209
+ if (domainMatch && expiryMatch) {
210
+ const domain = domainMatch[1].trim().split(/\s+/)[0];
211
+ const expiryDate = new Date(expiryMatch[1]);
212
+ const daysUntilExpiry = Math.floor(
213
+ (expiryDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24),
214
+ );
215
+ certs.push({
216
+ domain,
217
+ daysUntilExpiry,
218
+ expiresAt: expiryDate.toISOString(),
219
+ });
220
+ }
221
+ }
222
+
223
+ return certs;
224
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * lamaste-server chisel — manage chisel tunnel-server credentials.
3
+ *
4
+ * Subcommands:
5
+ * rotate-credential --label <label> Mint a fresh chisel password for an
6
+ * existing agent and restart the
7
+ * chisel service. The agent must
8
+ * re-fetch via
9
+ * `lamaste-agent chisel refresh-credential`.
10
+ */
11
+
12
+ import chalk from 'chalk';
13
+ import { panelRequest } from '../panel-api.js';
14
+ import { emit, emitError, emitComplete } from '../ndjson.js';
15
+
16
+ /**
17
+ * @param {string[]} args
18
+ * @param {{ json: boolean }} options
19
+ */
20
+ export async function runChisel(args, { json }) {
21
+ const sub = args[0];
22
+
23
+ switch (sub) {
24
+ case 'rotate-credential':
25
+ return rotateCredential(args.slice(1), { json });
26
+ default:
27
+ printChiselUsage();
28
+ process.exit(sub ? 1 : 0);
29
+ }
30
+ }
31
+
32
+ function printChiselUsage() {
33
+ const b = chalk.bold;
34
+ const c = chalk.cyan;
35
+ console.log(`
36
+ ${b('Usage:')} lamaste-server chisel <subcommand>
37
+
38
+ ${b('Subcommands:')}
39
+ ${c('rotate-credential')} --label <label> Mint a new chisel credential for an agent
40
+
41
+ ${b('Notes:')}
42
+ After rotating, the affected agent must run
43
+ ${c('lamaste-agent chisel refresh-credential --label <label>')} to pick up
44
+ the new credential. Until then its tunnels will fail to authenticate.
45
+ `);
46
+ }
47
+
48
+ /**
49
+ * @param {string[]} args
50
+ * @param {{ json: boolean }} options
51
+ */
52
+ async function rotateCredential(args, { json }) {
53
+ const label = getArg(args, 'label');
54
+ if (!label) {
55
+ const msg = 'Error: --label is required';
56
+ if (json) emitError(msg);
57
+ else console.error(`\n ${msg}\n`);
58
+ process.exit(1);
59
+ }
60
+
61
+ if (!json) {
62
+ process.stderr.write(` Rotating chisel credential for ${chalk.cyan(label)}...`);
63
+ }
64
+
65
+ try {
66
+ // The panel API does the heavy lifting: writes the new credential, regenerates
67
+ // the chisel-users authfile, and restarts the chisel service.
68
+ const result = await panelRequest(
69
+ 'POST',
70
+ `/api/agents/${encodeURIComponent(label)}/chisel-credential/rotate`,
71
+ );
72
+
73
+ if (json) {
74
+ emitComplete({
75
+ label: result.label,
76
+ user: result.user,
77
+ password: result.password,
78
+ restartOk: result.restartOk,
79
+ restartError: result.restartError || null,
80
+ });
81
+ return;
82
+ }
83
+
84
+ console.log(` ${chalk.green('ok')}`);
85
+ console.log('');
86
+ console.log(` ${chalk.bold('user:')} ${chalk.cyan(result.user)}`);
87
+ // SECURITY: the rotated chisel password is NEVER written to a log sink
88
+ // (stdout/stderr/console). It is persisted server-side in the chisel
89
+ // credentials store (mode 0600). Scripted consumers must use the
90
+ // non-interactive JSON mode (`--json`), which emits the password in
91
+ // the NDJSON `complete` event for direct programmatic capture.
92
+ console.log(
93
+ ` ${chalk.bold('password:')} ${chalk.dim('*** (redacted — use --json to retrieve)')}`,
94
+ );
95
+ if (!result.restartOk) {
96
+ console.log('');
97
+ console.log(
98
+ ` ${chalk.yellow('warning:')} chisel service restart failed: ${result.restartError}`,
99
+ );
100
+ console.log(
101
+ ` ${chalk.dim('Run `sudo systemctl restart chisel` manually before any tunnels reconnect.')}`,
102
+ );
103
+ }
104
+ console.log('');
105
+ console.log(
106
+ ` ${chalk.dim('On the agent host, run:')} ${chalk.cyan(`lamaste-agent chisel refresh-credential --label ${label}`)}`,
107
+ );
108
+ console.log('');
109
+ } catch (err) {
110
+ if (json) {
111
+ emitError(err.message);
112
+ } else {
113
+ console.log(` ${chalk.red('failed')}`);
114
+ console.error(` ${chalk.red(err.message)}\n`);
115
+ }
116
+ process.exit(1);
117
+ }
118
+
119
+ // Silence linter for unused emit
120
+ void emit;
121
+ }
122
+
123
+ /**
124
+ * @param {string[]} args
125
+ * @param {string} name
126
+ * @returns {string | null}
127
+ */
128
+ function getArg(args, name) {
129
+ const idx = args.indexOf(`--${name}`);
130
+ if (idx === -1 || idx + 1 >= args.length) return null;
131
+ return args[idx + 1];
132
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * lamaste-server logs — View server log output.
3
+ *
4
+ * Wraps journalctl for the panel service. Supports --follow and --lines.
5
+ */
6
+
7
+ import { execa } from 'execa';
8
+ import { PANEL_SERVICE, GATEKEEPER_SERVICE } from '../config.js';
9
+
10
+ /**
11
+ * @param {string[]} args Remaining CLI arguments after 'logs'
12
+ * @param {{ json: boolean }} options
13
+ */
14
+ export async function runLogs(args, { json }) {
15
+ const follow = args.includes('--follow') || args.includes('-f');
16
+ const service = args.includes('--gatekeeper') ? GATEKEEPER_SERVICE : PANEL_SERVICE;
17
+
18
+ let lines = '50';
19
+ for (let i = 0; i < args.length; i++) {
20
+ if ((args[i] === '--lines' || args[i] === '-n') && args[i + 1]) {
21
+ lines = args[i + 1];
22
+ }
23
+ }
24
+
25
+ const journalArgs = ['-u', service, '-n', lines, '--no-pager'];
26
+
27
+ if (follow) {
28
+ journalArgs.push('-f');
29
+ }
30
+
31
+ if (json) {
32
+ journalArgs.push('-o', 'json');
33
+ }
34
+
35
+ // For --follow we need to stream output to the user's terminal.
36
+ // For non-follow we capture and print.
37
+ if (follow) {
38
+ const proc = execa('journalctl', journalArgs, { stdio: 'inherit' });
39
+ // Let the user Ctrl+C to stop
40
+ await proc.catch((err) => {
41
+ // SIGINT/SIGTERM are expected when tailing
42
+ if (err.signal === 'SIGINT' || err.signal === 'SIGTERM') return;
43
+ throw err;
44
+ });
45
+ } else {
46
+ const { stdout } = await execa('journalctl', journalArgs);
47
+ process.stdout.write(stdout + '\n');
48
+ }
49
+ }