@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,265 @@
1
+ import { execa } from 'execa';
2
+ import { writeFile, unlink } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { ensureHttpRedirect } from '../lib/http-redirect.js';
5
+
6
+ /**
7
+ * nginx IP-based mTLS configuration subtasks.
8
+ *
9
+ * @param {object} ctx Shared installer context.
10
+ * @param {object} task Parent Listr2 task reference.
11
+ * @returns {import('listr2').ListrTask[]}
12
+ */
13
+ export function nginxTasks(ctx, task) {
14
+ const pkiDir = ctx.pkiDir;
15
+
16
+ return task.newListr([
17
+ {
18
+ title: 'Generating self-signed TLS certificate for IP access',
19
+ task: async (_ctx, subtask) => {
20
+ subtask.output = `Generating self-signed cert for IP ${ctx.ip}...`;
21
+ await execa('openssl', [
22
+ 'req',
23
+ '-x509',
24
+ '-nodes',
25
+ '-days',
26
+ '3650',
27
+ '-newkey',
28
+ 'rsa:2048',
29
+ '-keyout',
30
+ `${pkiDir}/self-signed-key.pem`,
31
+ '-out',
32
+ `${pkiDir}/self-signed.pem`,
33
+ '-subj',
34
+ `/CN=${ctx.ip}/O=Lamaste`,
35
+ '-addext',
36
+ `subjectAltName=IP:${ctx.ip}`,
37
+ ]);
38
+
39
+ await execa('chmod', ['600', `${pkiDir}/self-signed-key.pem`]);
40
+ await execa('chmod', ['644', `${pkiDir}/self-signed.pem`]);
41
+
42
+ subtask.output = 'Self-signed TLS certificate generated';
43
+ },
44
+ rendererOptions: { persistentOutput: true },
45
+ },
46
+ {
47
+ title: 'Writing mTLS snippet',
48
+ task: async (_ctx, subtask) => {
49
+ const snippetsDir = '/etc/nginx/snippets';
50
+ if (!existsSync(snippetsDir)) {
51
+ await execa('mkdir', ['-p', snippetsDir]);
52
+ }
53
+
54
+ const mtlsSnippet = `ssl_client_certificate ${pkiDir}/ca.crt;
55
+ ssl_verify_client optional;
56
+ `;
57
+ await writeFile('/etc/nginx/snippets/lamalibre-lamaste-mtls.conf', mtlsSnippet);
58
+
59
+ subtask.output = 'mTLS snippet written to /etc/nginx/snippets/lamalibre-lamaste-mtls.conf';
60
+ },
61
+ rendererOptions: { persistentOutput: true },
62
+ },
63
+ {
64
+ title: 'Writing IP-based panel vhost',
65
+ task: async (_ctx, subtask) => {
66
+ const vhostConfig = `# Rate limit zone for public enrollment endpoint (5 requests/minute per IP)
67
+ limit_req_zone $binary_remote_addr zone=enroll:1m rate=5r/m;
68
+
69
+ map $http_upgrade $connection_upgrade {
70
+ default upgrade;
71
+ '' close;
72
+ }
73
+
74
+ server {
75
+ listen 9292 ssl;
76
+ server_name _;
77
+
78
+ ssl_certificate ${pkiDir}/self-signed.pem;
79
+ ssl_certificate_key ${pkiDir}/self-signed-key.pem;
80
+
81
+ include /etc/nginx/snippets/lamalibre-lamaste-mtls.conf;
82
+
83
+ # SSL settings
84
+ ssl_protocols TLSv1.2 TLSv1.3;
85
+ ssl_ciphers HIGH:!aNULL:!MD5;
86
+ ssl_prefer_server_ciphers on;
87
+
88
+ # Show certificate help page when client cert is missing or invalid
89
+ error_page 495 496 /cert-help.html;
90
+ location = /cert-help.html {
91
+ root /opt/lamalibre/lamaste/lamaste-server-ui;
92
+ internal;
93
+ }
94
+
95
+ # Proxy to serverd (mTLS required — reject if cert missing or invalid)
96
+ location / {
97
+ if ($ssl_client_verify != SUCCESS) {
98
+ return 496;
99
+ }
100
+ proxy_pass http://127.0.0.1:3100;
101
+
102
+ # Client cert headers — set from nginx TLS variables, never passed through from client
103
+ proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
104
+ proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
105
+ proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
106
+
107
+ # Strip Authelia identity headers — not trusted on mTLS vhost
108
+ proxy_set_header Remote-User "";
109
+ proxy_set_header Remote-Groups "";
110
+ proxy_set_header Remote-Name "";
111
+ proxy_set_header Remote-Email "";
112
+
113
+ # Standard proxy headers
114
+ proxy_set_header Host $host;
115
+ proxy_set_header X-Real-IP $remote_addr;
116
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
117
+ proxy_set_header X-Forwarded-Proto $scheme;
118
+ }
119
+
120
+ # Public API paths (no mTLS verification check).
121
+ # ssl_verify_client is 'optional' at server level, so TLS handshake
122
+ # succeeds without a cert. These locations skip the $ssl_client_verify
123
+ # check and clear cert headers so the backend sees no client identity.
124
+ location /api/enroll {
125
+ limit_req zone=enroll burst=5 nodelay;
126
+ proxy_pass http://127.0.0.1:3100;
127
+ proxy_http_version 1.1;
128
+
129
+ # Clear cert headers so the backend sees no client cert
130
+ proxy_set_header X-SSL-Client-Verify "";
131
+ proxy_set_header X-SSL-Client-DN "";
132
+ proxy_set_header X-SSL-Client-Serial "";
133
+
134
+ # Strip Authelia identity headers — not trusted on public endpoint
135
+ proxy_set_header Remote-User "";
136
+ proxy_set_header Remote-Groups "";
137
+ proxy_set_header Remote-Name "";
138
+ proxy_set_header Remote-Email "";
139
+
140
+ proxy_set_header Host $host;
141
+ proxy_set_header X-Real-IP $remote_addr;
142
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
143
+ proxy_set_header X-Forwarded-Proto $scheme;
144
+ }
145
+
146
+ location /api/invite {
147
+ proxy_pass http://127.0.0.1:3100;
148
+ proxy_http_version 1.1;
149
+
150
+ proxy_set_header X-SSL-Client-Verify "";
151
+ proxy_set_header X-SSL-Client-DN "";
152
+ proxy_set_header X-SSL-Client-Serial "";
153
+
154
+ # Strip Authelia identity headers — not trusted on public endpoint
155
+ proxy_set_header Remote-User "";
156
+ proxy_set_header Remote-Groups "";
157
+ proxy_set_header Remote-Name "";
158
+ proxy_set_header Remote-Email "";
159
+
160
+ proxy_set_header Host $host;
161
+ proxy_set_header X-Real-IP $remote_addr;
162
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
163
+ proxy_set_header X-Forwarded-Proto $scheme;
164
+ }
165
+
166
+ # API paths with WebSocket upgrade support (mTLS required)
167
+ location /api {
168
+ if ($ssl_client_verify != SUCCESS) {
169
+ return 496;
170
+ }
171
+ proxy_pass http://127.0.0.1:3100;
172
+ proxy_http_version 1.1;
173
+
174
+ # Client cert headers — set from nginx TLS variables, never passed through from client
175
+ proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
176
+ proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
177
+ proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
178
+
179
+ # Strip Authelia identity headers — not trusted on mTLS vhost
180
+ proxy_set_header Remote-User "";
181
+ proxy_set_header Remote-Groups "";
182
+ proxy_set_header Remote-Name "";
183
+ proxy_set_header Remote-Email "";
184
+
185
+ # Standard proxy headers
186
+ proxy_set_header Host $host;
187
+ proxy_set_header X-Real-IP $remote_addr;
188
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
189
+ proxy_set_header X-Forwarded-Proto $scheme;
190
+
191
+ # WebSocket: only upgrade when client requests it
192
+ proxy_set_header Upgrade $http_upgrade;
193
+ proxy_set_header Connection $connection_upgrade;
194
+ }
195
+ }
196
+ `;
197
+ await writeFile('/etc/nginx/sites-available/lamalibre-lamaste-panel-ip', vhostConfig);
198
+
199
+ subtask.output = 'Vhost written to /etc/nginx/sites-available/lamalibre-lamaste-panel-ip';
200
+ },
201
+ rendererOptions: { persistentOutput: true },
202
+ },
203
+ {
204
+ title: 'Enabling site and cleaning up defaults',
205
+ task: async (_ctx, subtask) => {
206
+ // Create symlink
207
+ await execa('ln', [
208
+ '-sf',
209
+ '/etc/nginx/sites-available/lamalibre-lamaste-panel-ip',
210
+ '/etc/nginx/sites-enabled/lamalibre-lamaste-panel-ip',
211
+ ]);
212
+
213
+ // Remove only the default site from sites-enabled (preserve other existing sites)
214
+ const defaultSite = '/etc/nginx/sites-enabled/default';
215
+ if (existsSync(defaultSite)) {
216
+ await unlink(defaultSite).catch(() => {});
217
+ subtask.output = 'Site enabled, default removed';
218
+ } else {
219
+ subtask.output = 'Site enabled';
220
+ }
221
+ },
222
+ rendererOptions: { persistentOutput: true },
223
+ },
224
+ {
225
+ title: 'Redirecting plain HTTP to HTTPS',
226
+ task: async (_ctx, subtask) => {
227
+ // nginx is (re)started by the next task; no reload here. Not fatal:
228
+ // another site on this machine may already own port 80's default
229
+ // server, and the relay works without the redirect.
230
+ try {
231
+ await ensureHttpRedirect({ reload: false });
232
+ subtask.output = 'Port 80 answers every host with a redirect to https://';
233
+ } catch (err) {
234
+ subtask.skip(`Skipped — ${err.message}`);
235
+ }
236
+ },
237
+ rendererOptions: { persistentOutput: true },
238
+ },
239
+ {
240
+ title: 'Validating and starting nginx',
241
+ task: async (_ctx, subtask) => {
242
+ subtask.output = 'Validating nginx configuration...';
243
+ try {
244
+ await execa('nginx', ['-t']);
245
+ } catch (error) {
246
+ throw new Error(
247
+ `nginx configuration validation failed:\n${error.stderr || error.message}`,
248
+ );
249
+ }
250
+
251
+ subtask.output = 'Enabling and starting nginx...';
252
+ await execa('systemctl', ['enable', 'nginx']);
253
+ await execa('systemctl', ['restart', 'nginx']);
254
+
255
+ const { stdout: status } = await execa('systemctl', ['is-active', 'nginx']);
256
+ if (status.trim() !== 'active') {
257
+ throw new Error(`nginx failed to start. Status: ${status.trim()}`);
258
+ }
259
+
260
+ subtask.output = 'nginx is running and listening on port 9292';
261
+ },
262
+ rendererOptions: { persistentOutput: true },
263
+ },
264
+ ]);
265
+ }
@@ -0,0 +1,110 @@
1
+ import { execa } from 'execa';
2
+ import { unlink } from 'node:fs/promises';
3
+
4
+ const REQUIRED_MAJOR = 22;
5
+
6
+ /**
7
+ * Node.js 22 LTS installation subtasks.
8
+ *
9
+ * 22.5+ is the minimum because lamaste-serverd uses the built-in
10
+ * `node:sqlite` module (which requires 22.5+).
11
+ *
12
+ * @param {object} ctx Shared installer context.
13
+ * @param {object} task Parent Listr2 task reference.
14
+ * @returns {import('listr2').ListrTask[]}
15
+ */
16
+ export function nodeTasks(ctx, task) {
17
+ return task.newListr([
18
+ {
19
+ title: 'Checking existing Node.js installation',
20
+ task: async (_ctx, subtask) => {
21
+ try {
22
+ const { stdout } = await execa('node', ['--version']);
23
+ const version = stdout.trim();
24
+ const major = parseInt(version.replace(/^v/, ''), 10);
25
+
26
+ if (major >= REQUIRED_MAJOR) {
27
+ ctx.nodeAlreadyInstalled = true;
28
+ subtask.output = `Node.js ${version} found, skipping install`;
29
+ } else {
30
+ subtask.output = `Node.js ${version} found (below v${REQUIRED_MAJOR}), will upgrade`;
31
+ }
32
+ } catch {
33
+ subtask.output = `Node.js not found, installing v${REQUIRED_MAJOR} LTS`;
34
+ }
35
+ },
36
+ rendererOptions: { persistentOutput: true },
37
+ },
38
+ {
39
+ title: 'Installing NodeSource repository',
40
+ skip: () => ctx.nodeAlreadyInstalled && `Node.js ${REQUIRED_MAJOR}+ already installed`,
41
+ task: async (_ctx, subtask) => {
42
+ const setupScript = '/tmp/nodesource_setup.sh';
43
+
44
+ subtask.output = 'Downloading NodeSource setup script...';
45
+ try {
46
+ await execa('curl', [
47
+ '-fsSL',
48
+ `https://deb.nodesource.com/setup_${REQUIRED_MAJOR}.x`,
49
+ '-o',
50
+ setupScript,
51
+ ]);
52
+ } catch (error) {
53
+ throw new Error(
54
+ `Failed to download NodeSource setup script. Check your internet connection.\n${error.stderr || error.message}`,
55
+ );
56
+ }
57
+
58
+ subtask.output = 'Running NodeSource setup...';
59
+ try {
60
+ await execa('bash', [setupScript]);
61
+ } catch (error) {
62
+ throw new Error(`NodeSource setup script failed.\n${error.stderr || error.message}`);
63
+ }
64
+
65
+ await unlink(setupScript).catch(() => {});
66
+ subtask.output = 'NodeSource repository added';
67
+ },
68
+ rendererOptions: { persistentOutput: true },
69
+ },
70
+ {
71
+ title: `Installing Node.js ${REQUIRED_MAJOR} LTS`,
72
+ skip: () => ctx.nodeAlreadyInstalled && `Node.js ${REQUIRED_MAJOR}+ already installed`,
73
+ task: async (_ctx, subtask) => {
74
+ subtask.output = 'Running apt-get install nodejs...';
75
+ try {
76
+ await execa('apt-get', ['install', '-y', 'nodejs']);
77
+ } catch (error) {
78
+ throw new Error(
79
+ `Failed to install Node.js. Try running 'apt-get update' manually and retrying.\n${error.stderr || error.message}`,
80
+ );
81
+ }
82
+ subtask.output = 'Node.js installed';
83
+ },
84
+ rendererOptions: { persistentOutput: true },
85
+ },
86
+ {
87
+ title: 'Verifying Node.js installation',
88
+ task: async (_ctx, subtask) => {
89
+ const { stdout: nodeVersion } = await execa('node', ['--version']);
90
+ const { stdout: npmVersion } = await execa('npm', ['--version']);
91
+
92
+ const trimmedNode = nodeVersion.trim();
93
+ const trimmedNpm = npmVersion.trim();
94
+ const major = parseInt(trimmedNode.replace(/^v/, ''), 10);
95
+
96
+ if (major < REQUIRED_MAJOR) {
97
+ throw new Error(
98
+ `Expected Node.js v${REQUIRED_MAJOR}+, got ${trimmedNode}. Installation may have failed.`,
99
+ );
100
+ }
101
+
102
+ ctx.nodeVersion = trimmedNode;
103
+ ctx.npmVersion = trimmedNpm;
104
+
105
+ subtask.output = `Node.js ${trimmedNode}, npm ${trimmedNpm}`;
106
+ },
107
+ rendererOptions: { persistentOutput: true },
108
+ },
109
+ ]);
110
+ }