@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,267 @@
1
+ # Services API
2
+
3
+ > Monitor and control the systemd services that make up the Lamaste stack, with live log streaming via WebSocket.
4
+
5
+ ## In Plain English
6
+
7
+ Lamaste runs several background services on the droplet: nginx (the reverse proxy), Chisel (the tunnel server), Authelia (the login system for tunneled apps), and the panel server itself. The services API lets you check whether each service is running, start or stop them, restart them, and watch their log output in real time.
8
+
9
+ Think of it as a simplified version of the `systemctl` and `journalctl` commands, accessible from your browser.
10
+
11
+ ## Authentication
12
+
13
+ All service endpoints require a valid mTLS client certificate and a completed onboarding. See the [API Overview](./overview.md) for details.
14
+
15
+ If onboarding is not complete, all endpoints return `503 Service Unavailable`.
16
+
17
+ In addition to admin certificates, agent certificates with the appropriate capabilities can access these endpoints:
18
+
19
+ - `services:read` — grants access to `GET /api/services` (list statuses)
20
+ - `services:write` — grants access to `POST /api/services/:name/:action` (control services)
21
+
22
+ ## Service Whitelist
23
+
24
+ Only these services can be managed through the API:
25
+
26
+ | Service Name | Description |
27
+ | --------------------------- | ------------------------------------------------ |
28
+ | `nginx` | Reverse proxy, TLS termination, mTLS enforcement |
29
+ | `chisel` | WebSocket tunnel server |
30
+ | `authelia` | TOTP two-factor authentication |
31
+ | `lamalibre-lamaste-serverd` | The panel server itself (Fastify) |
32
+
33
+ Requests for any other service name are rejected with a 400 error. This whitelist prevents arbitrary systemd service manipulation.
34
+
35
+ ## Safety Rules
36
+
37
+ - **Cannot stop the panel from the UI.** Calling `POST /api/services/lamalibre-lamaste-serverd/stop` returns a 400 error because stopping the panel would terminate the API session — you would lose the ability to restart it without SSH access.
38
+ - **All actions use sudo.** The panel runs as the `lamaste` user. Service control commands use `sudo systemctl` via sudoers rules that restrict which services and actions are permitted.
39
+
40
+ ## Endpoints
41
+
42
+ ### `GET /api/services`
43
+
44
+ Returns the status and uptime of all managed services. Statuses are queried in parallel from systemd.
45
+
46
+ **Request:**
47
+
48
+ No request body.
49
+
50
+ ```bash
51
+ curl -s --cert client.p12:password \
52
+ https://203.0.113.42:9292/api/services | jq
53
+ ```
54
+
55
+ **Response (200):**
56
+
57
+ ```json
58
+ {
59
+ "services": [
60
+ {
61
+ "name": "nginx",
62
+ "status": "active",
63
+ "uptime": "5d 3h 20m"
64
+ },
65
+ {
66
+ "name": "chisel",
67
+ "status": "active",
68
+ "uptime": "5d 3h 19m"
69
+ },
70
+ {
71
+ "name": "authelia",
72
+ "status": "active",
73
+ "uptime": "5d 3h 18m"
74
+ },
75
+ {
76
+ "name": "lamalibre-lamaste-serverd",
77
+ "status": "active",
78
+ "uptime": "2h 45m"
79
+ }
80
+ ]
81
+ }
82
+ ```
83
+
84
+ | Field | Type | Description |
85
+ | -------- | ---------------- | -------------------------------------------------------------------- |
86
+ | `name` | `string` | systemd service name |
87
+ | `status` | `string` | One of: `active`, `inactive`, `failed`, `unknown` |
88
+ | `uptime` | `string \| null` | Human-readable uptime (e.g., `"2d 5h 30m"`), or `null` if not active |
89
+
90
+ The uptime is calculated from the `ActiveEnterTimestamp` reported by systemd and formatted as a human-readable duration. If the timestamp cannot be read, uptime is `null` even for active services.
91
+
92
+ ---
93
+
94
+ ### `POST /api/services/:name/:action`
95
+
96
+ Executes a systemctl action on a managed service. The command runs with a 30-second timeout.
97
+
98
+ **URL parameters:**
99
+
100
+ | Parameter | Type | Validation | Description |
101
+ | --------- | -------- | -------------------------------------------- | ------------------ |
102
+ | `:name` | `string` | Must be in the service whitelist | Service to control |
103
+ | `:action` | `string` | One of: `start`, `stop`, `restart`, `reload` | Action to execute |
104
+
105
+ ```bash
106
+ curl -s --cert client.p12:password \
107
+ -X POST \
108
+ https://203.0.113.42:9292/api/services/nginx/restart | jq
109
+ ```
110
+
111
+ **Response (200):**
112
+
113
+ ```json
114
+ {
115
+ "ok": true,
116
+ "name": "nginx",
117
+ "action": "restart"
118
+ }
119
+ ```
120
+
121
+ **Errors:**
122
+
123
+ | Status | Body | When |
124
+ | ------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------- |
125
+ | 400 | `{"error":"Unknown service"}` | Service name not in whitelist |
126
+ | 400 | `{"error":"Invalid action"}` | Action not one of start/stop/restart/reload |
127
+ | 400 | `{"error":"Cannot stop the panel service from the UI — it would terminate this session"}` | Attempting to stop `lamalibre-lamaste-serverd` |
128
+ | 400 | `{"error":"chisel does not support reload — use restart"}` | `reload` for `chisel` or the panel service |
129
+ | 400 | `{"error":"Validation failed","details":{"issues":[...]}}` | Zod validation of params failed |
130
+ | 500 | `{"error":"Failed to restart nginx","details":"..."}` | systemctl command failed |
131
+
132
+ ### Available Actions
133
+
134
+ | Action | systemctl Command | Notes |
135
+ | --------- | ------------------------------- | --------------------------------------------------------------------------- |
136
+ | `start` | `sudo systemctl start <name>` | Start a stopped service |
137
+ | `stop` | `sudo systemctl stop <name>` | Stop a running service (blocked for `lamalibre-lamaste-serverd`) |
138
+ | `restart` | `sudo systemctl restart <name>` | Stop then start a service |
139
+ | `reload` | `sudo systemctl reload <name>` | Reload configuration without full restart (supported by nginx and authelia) |
140
+
141
+ ---
142
+
143
+ ### `WS /api/services/:name/logs`
144
+
145
+ WebSocket endpoint that streams live logs from journalctl for a specific service. The connection tails the last 100 log lines and then follows new output in real time.
146
+
147
+ **URL parameters:**
148
+
149
+ | Parameter | Type | Validation | Description |
150
+ | --------- | -------- | -------------------------------- | ------------------------ |
151
+ | `:name` | `string` | Must be in the service whitelist | Service to tail logs for |
152
+
153
+ If the service name is not in the whitelist, the WebSocket is closed immediately with code 1008 ("Policy Violation") and the reason "Unknown service".
154
+
155
+ **Connection:**
156
+
157
+ ```javascript
158
+ const ws = new WebSocket('wss://203.0.113.42:9292/api/services/nginx/logs');
159
+
160
+ ws.onmessage = (event) => {
161
+ const entry = JSON.parse(event.data);
162
+ console.log(`[${entry.timestamp}] ${entry.message}`);
163
+ };
164
+
165
+ ws.onclose = (event) => {
166
+ console.log(`Log stream closed: ${event.reason}`);
167
+ };
168
+ ```
169
+
170
+ **Message format:**
171
+
172
+ Each message is a JSON object with a timestamp and message:
173
+
174
+ ```json
175
+ {
176
+ "timestamp": "2026-03-13T14:30:45+0000",
177
+ "message": "hostname nginx[1234]: 203.0.113.42 - - [13/Mar/2026:14:30:45 +0000] \"GET /api/health HTTP/1.1\" 200 32"
178
+ }
179
+ ```
180
+
181
+ | Field | Type | Description |
182
+ | ----------- | -------- | -------------------------------------------------------------- |
183
+ | `timestamp` | `string` | ISO timestamp from journalctl `--output=short-iso` format |
184
+ | `message` | `string` | The rest of the log line (hostname, process, PID, and message) |
185
+
186
+ If the timestamp cannot be parsed from the log line, `timestamp` is an empty string and `message` contains the entire raw line.
187
+
188
+ **Special messages:**
189
+
190
+ ```json
191
+ {
192
+ "timestamp": "2026-03-13T14:30:45.000Z",
193
+ "message": "[Error: Failed to start log stream for nginx]"
194
+ }
195
+ ```
196
+
197
+ ```json
198
+ {
199
+ "timestamp": "2026-03-13T14:30:45.000Z",
200
+ "message": "[Log stream ended]"
201
+ }
202
+ ```
203
+
204
+ **Close codes:**
205
+
206
+ | Code | Reason | When |
207
+ | ---- | -------------------------- | -------------------------------------- |
208
+ | 1000 | Log stream ended | journalctl process exited normally |
209
+ | 1008 | Unknown service | Service name not in whitelist |
210
+ | 1011 | Failed to spawn journalctl | Could not start the journalctl process |
211
+
212
+ **Implementation details:**
213
+
214
+ - Uses `journalctl -u <name> -f -n 100 --output=short-iso`
215
+ - The `-n 100` flag sends the last 100 log lines on connect (backfill)
216
+ - The `-f` flag follows new output in real time
217
+ - The journalctl process is killed (SIGTERM) when the WebSocket closes
218
+ - All active journalctl processes are cleaned up on server shutdown
219
+
220
+ ## Quick Reference
221
+
222
+ | Method | Path | Description |
223
+ | ------ | ----------------------------- | ------------------------------------- |
224
+ | GET | `/api/services` | List all service statuses and uptimes |
225
+ | POST | `/api/services/:name/:action` | Execute start/stop/restart/reload |
226
+ | WS | `/api/services/:name/logs` | Stream live logs via WebSocket |
227
+
228
+ ### Service Status Object Shape
229
+
230
+ ```json
231
+ {
232
+ "name": "nginx",
233
+ "status": "active",
234
+ "uptime": "5d 3h 20m"
235
+ }
236
+ ```
237
+
238
+ ### Allowed Combinations
239
+
240
+ | Service | start | stop | restart | reload |
241
+ | --------------------------- | ----- | ------ | ------- | ------ |
242
+ | `nginx` | Yes | Yes | Yes | Yes |
243
+ | `chisel` | Yes | Yes | Yes | **No** |
244
+ | `authelia` | Yes | Yes | Yes | Yes |
245
+ | `lamalibre-lamaste-serverd` | Yes | **No** | Yes | **No** |
246
+
247
+ ### curl Cheat Sheet
248
+
249
+ ```bash
250
+ # List service statuses
251
+ curl -s --cert client.p12:password \
252
+ https://203.0.113.42:9292/api/services | jq
253
+
254
+ # Restart nginx
255
+ curl -s --cert client.p12:password \
256
+ -X POST \
257
+ https://203.0.113.42:9292/api/services/nginx/restart | jq
258
+
259
+ # Reload Authelia (after manual config changes)
260
+ curl -s --cert client.p12:password \
261
+ -X POST \
262
+ https://203.0.113.42:9292/api/services/authelia/reload | jq
263
+
264
+ # Stream nginx logs (requires wscat or similar)
265
+ wscat -c wss://203.0.113.42:9292/api/services/nginx/logs \
266
+ --cert client.pem --key client-key.pem
267
+ ```
@@ -0,0 +1,343 @@
1
+ # Settings API
2
+
3
+ > Two-factor authentication (2FA) configuration for the admin panel. All endpoints are admin-only and fall under the management route group.
4
+
5
+ ## In Plain English
6
+
7
+ The settings API lets you enable, configure, and manage TOTP-based two-factor authentication for the admin panel. When 2FA is enabled, every admin request must carry both a valid mTLS client certificate and a valid session cookie. The session cookie is issued after the admin verifies a TOTP code and lasts for 12 hours (or 2 hours of inactivity, whichever comes first).
8
+
9
+ The lifecycle is:
10
+
11
+ 1. **Check status** — `GET /api/settings/2fa` tells you whether 2FA is enabled.
12
+ 2. **Setup** — `POST /api/settings/2fa/setup` generates a TOTP secret and returns a `otpauth://` URI for scanning with an authenticator app.
13
+ 3. **Confirm** — `POST /api/settings/2fa/confirm` verifies the first code from the authenticator app, enabling 2FA and disabling the IP-based vhost.
14
+ 4. **Verify (ongoing)** — `POST /api/settings/2fa/verify` is called at the start of each admin session to exchange a TOTP code for a session cookie.
15
+ 5. **Disable** — `POST /api/settings/2fa/disable` turns off 2FA and re-enables the IP-based vhost. Requires a valid 2FA session and a TOTP code.
16
+
17
+ ## Authentication
18
+
19
+ All endpoints require a valid mTLS admin certificate. The **status** (`GET /api/settings/2fa`) and **verify** (`POST /api/settings/2fa/verify`) endpoints are exempt from the 2FA session requirement — they must be accessible before the admin has a session cookie. All other endpoints require a valid 2FA session when 2FA is enabled.
20
+
21
+ **Note:** Agent certificates bypass 2FA entirely — the 2FA session middleware skips all requests where `certRole` is `agent`. Only admin certificate holders are subject to the 2FA requirement.
22
+
23
+ ## Rate Limiting
24
+
25
+ The `/confirm`, `/verify`, and `/disable` endpoints enforce per-IP rate limiting:
26
+
27
+ - **5 attempts** per 2-minute sliding window
28
+ - **5-minute ban** once the limit is exceeded
29
+
30
+ Rate-limited requests receive:
31
+
32
+ **Response (429):**
33
+
34
+ ```json
35
+ {
36
+ "error": "Too many attempts. Try again later.",
37
+ "retryAfter": 300
38
+ }
39
+ ```
40
+
41
+ | Field | Type | Description |
42
+ | ------------ | -------- | ----------------------------- |
43
+ | `retryAfter` | `number` | Seconds until the ban expires |
44
+
45
+ ## Session Cookie
46
+
47
+ When `POST /api/settings/2fa/verify` succeeds, the server sets a `lamaste_2fa_session` cookie:
48
+
49
+ | Property | Value |
50
+ | ------------ | --------------------- |
51
+ | Name | `lamaste_2fa_session` |
52
+ | Signing | HMAC-SHA256 |
53
+ | HttpOnly | Yes |
54
+ | Secure | Yes |
55
+ | SameSite | Strict |
56
+ | Absolute TTL | 12 hours |
57
+ | Inactivity | 2 hours |
58
+
59
+ If a request arrives with a valid mTLS certificate but without a valid 2FA session cookie (when 2FA is enabled), the API returns:
60
+
61
+ **Response (401):**
62
+
63
+ ```json
64
+ {
65
+ "error": "2fa_required"
66
+ }
67
+ ```
68
+
69
+ ## Endpoints
70
+
71
+ ### `GET /api/settings/2fa`
72
+
73
+ Returns the current 2FA status. This endpoint is exempt from the 2FA session requirement, so the UI can check whether to show the verification prompt.
74
+
75
+ **Request:**
76
+
77
+ No request body.
78
+
79
+ ```bash
80
+ curl -s --cert client.p12:password \
81
+ https://203.0.113.42:9292/api/settings/2fa | jq
82
+ ```
83
+
84
+ **Response (200):**
85
+
86
+ ```json
87
+ {
88
+ "enabled": false,
89
+ "setupComplete": false
90
+ }
91
+ ```
92
+
93
+ | Field | Type | Description |
94
+ | --------------- | --------- | -------------------------------------------------- |
95
+ | `enabled` | `boolean` | Whether 2FA is currently active |
96
+ | `setupComplete` | `boolean` | Whether the initial setup and confirmation is done |
97
+
98
+ ---
99
+
100
+ ### `POST /api/settings/2fa/setup`
101
+
102
+ Generates a new TOTP secret and returns the provisioning URI for scanning with an authenticator app (Google Authenticator, 1Password, etc.) and a manual entry key for apps that do not support QR scanning.
103
+
104
+ Requires that a domain has been configured on the panel (enabling 2FA disables IP:9292 access, so a domain is required for continued access).
105
+
106
+ **Request:**
107
+
108
+ No request body.
109
+
110
+ ```bash
111
+ curl -s -X POST --cert client.p12:password \
112
+ https://203.0.113.42:9292/api/settings/2fa/setup | jq
113
+ ```
114
+
115
+ **Response (200):**
116
+
117
+ ```json
118
+ {
119
+ "uri": "otpauth://totp/Lamaste%20Panel:admin?secret=JBSWY3DPEHPK3PXP&issuer=Lamaste%20Panel",
120
+ "manualKey": "JBSWY3DPEHPK3PXP"
121
+ }
122
+ ```
123
+
124
+ | Field | Type | Description |
125
+ | ----------- | -------- | ------------------------------------------------------ |
126
+ | `uri` | `string` | `otpauth://` URI — encode as QR code for scanning |
127
+ | `manualKey` | `string` | Base32-encoded secret for manual entry in the auth app |
128
+
129
+ **Errors:**
130
+
131
+ | Status | Body | When |
132
+ | ------ | ---------------------------------------------------------------------------------------- | ------------------------------ |
133
+ | 400 | `{"error": "Domain must be configured before enabling 2FA", "details": {"hint": "..."}}` | No domain has been provisioned |
134
+ | 409 | `{"error": "2FA is already enabled"}` | 2FA is already active |
135
+
136
+ ---
137
+
138
+ ### `POST /api/settings/2fa/confirm`
139
+
140
+ Verifies the first TOTP code after setup, completing the 2FA activation. Once confirmed, 2FA is enabled and the IP-based vhost is disabled (all access goes through the domain vhost). This is a one-time operation during setup.
141
+
142
+ **Request:**
143
+
144
+ ```json
145
+ {
146
+ "code": "123456"
147
+ }
148
+ ```
149
+
150
+ | Field | Type | Required | Description |
151
+ | ------ | -------- | -------- | ---------------------------------------- |
152
+ | `code` | `string` | Yes | 6-digit TOTP code from authenticator app |
153
+
154
+ ```bash
155
+ curl -s -X POST --cert client.p12:password \
156
+ -H 'Content-Type: application/json' \
157
+ -d '{"code":"123456"}' \
158
+ https://203.0.113.42:9292/api/settings/2fa/confirm | jq
159
+ ```
160
+
161
+ **Response (200):**
162
+
163
+ ```json
164
+ {
165
+ "enabled": true
166
+ }
167
+ ```
168
+
169
+ The response also sets the `lamaste_2fa_session` cookie (see [Session Cookie](#session-cookie)), so no separate `/verify` call is needed for the initial session.
170
+
171
+ **Errors:**
172
+
173
+ | Status | Body | When |
174
+ | ------ | ----------------------------------------------------------------------------- | --------------------------------- |
175
+ | 400 | `{"error": "Code must be exactly 6 digits"}` | Code format invalid |
176
+ | 400 | `{"error": "No 2FA setup in progress. Call POST /settings/2fa/setup first."}` | No secret has been generated yet |
177
+ | 401 | `{"error": "Invalid TOTP code"}` | TOTP code is incorrect or expired |
178
+ | 409 | `{"error": "2FA is already enabled"}` | 2FA is already active |
179
+ | 429 | `{"error": "Too many attempts. Try again later."}` | Rate limit exceeded |
180
+ | 500 | `{"error": "Failed to disable IP vhost. 2FA was not enabled."}` | nginx config update failed |
181
+
182
+ ---
183
+
184
+ ### `POST /api/settings/2fa/verify`
185
+
186
+ Verifies a TOTP code and issues a session cookie. Called at the start of each admin session when 2FA is enabled. This endpoint is exempt from the 2FA session requirement.
187
+
188
+ **Request:**
189
+
190
+ ```json
191
+ {
192
+ "code": "123456"
193
+ }
194
+ ```
195
+
196
+ | Field | Type | Required | Description |
197
+ | ------ | -------- | -------- | ---------------------------------------- |
198
+ | `code` | `string` | Yes | 6-digit TOTP code from authenticator app |
199
+
200
+ ```bash
201
+ curl -s -X POST --cert client.p12:password \
202
+ -H 'Content-Type: application/json' \
203
+ -d '{"code":"123456"}' \
204
+ https://203.0.113.42:9292/api/settings/2fa/verify | jq
205
+ ```
206
+
207
+ **Response (200):**
208
+
209
+ ```json
210
+ {
211
+ "verified": true
212
+ }
213
+ ```
214
+
215
+ The response also sets the `lamaste_2fa_session` cookie (see [Session Cookie](#session-cookie) above).
216
+
217
+ **Errors:**
218
+
219
+ | Status | Body | When |
220
+ | ------ | -------------------------------------------------- | --------------------------------- |
221
+ | 400 | `{"error": "Code must be exactly 6 digits"}` | Code format invalid |
222
+ | 400 | `{"error": "2FA is not enabled"}` | 2FA has not been activated |
223
+ | 401 | `{"error": "Invalid TOTP code"}` | TOTP code is incorrect or expired |
224
+ | 429 | `{"error": "Too many attempts. Try again later."}` | Rate limit exceeded |
225
+
226
+ ---
227
+
228
+ ### `POST /api/settings/2fa/disable`
229
+
230
+ Disables 2FA. Requires both a valid 2FA session cookie and a TOTP code for confirmation. After disabling, the IP-based vhost is re-enabled and the session cookie requirement is removed.
231
+
232
+ **Note:** If re-enabling the IP vhost fails (e.g., nginx config error), 2FA is still disabled and the endpoint returns `200`. The failure is logged server-side. The admin would need to manually fix the nginx configuration or run `sudo lamaste-reset-admin` to restore the IP vhost.
233
+
234
+ **Request:**
235
+
236
+ ```json
237
+ {
238
+ "code": "123456"
239
+ }
240
+ ```
241
+
242
+ | Field | Type | Required | Description |
243
+ | ------ | -------- | -------- | ---------------------------------------- |
244
+ | `code` | `string` | Yes | 6-digit TOTP code from authenticator app |
245
+
246
+ ```bash
247
+ curl -s -X POST --cert client.p12:password \
248
+ -H 'Content-Type: application/json' \
249
+ -d '{"code":"123456"}' \
250
+ https://203.0.113.42:9292/api/settings/2fa/disable | jq
251
+ ```
252
+
253
+ **Response (200):**
254
+
255
+ ```json
256
+ {
257
+ "enabled": false
258
+ }
259
+ ```
260
+
261
+ **Errors:**
262
+
263
+ | Status | Body | When |
264
+ | ------ | -------------------------------------------------- | --------------------------------- |
265
+ | 400 | `{"error": "Code must be exactly 6 digits"}` | Code format invalid |
266
+ | 400 | `{"error": "2FA is not enabled"}` | 2FA was not active |
267
+ | 401 | `{"error": "Invalid TOTP code"}` | TOTP code is incorrect or expired |
268
+ | 401 | `{"error": "2fa_required"}` | No valid 2FA session cookie |
269
+ | 429 | `{"error": "Too many attempts. Try again later."}` | Rate limit exceeded |
270
+
271
+ ## Quick Reference
272
+
273
+ | Method | Path | Auth | Rate Limited | Description |
274
+ | ------ | --------------------------- | ------------------------------------ | ------------ | --------------------------------- |
275
+ | GET | `/api/settings/2fa` | mTLS (exempt from 2FA session) | No | Get 2FA status |
276
+ | POST | `/api/settings/2fa/setup` | mTLS (2FA not yet active during use) | No | Generate TOTP secret |
277
+ | POST | `/api/settings/2fa/confirm` | mTLS (2FA not yet active during use) | Yes | Confirm initial code, enable 2FA |
278
+ | POST | `/api/settings/2fa/verify` | mTLS (exempt from 2FA session) | Yes | Verify code, issue session cookie |
279
+ | POST | `/api/settings/2fa/disable` | mTLS + 2FA session | Yes | Disable 2FA |
280
+
281
+ ### Response Shapes
282
+
283
+ **Status:**
284
+
285
+ ```json
286
+ { "enabled": false, "setupComplete": false }
287
+ ```
288
+
289
+ **Setup:**
290
+
291
+ ```json
292
+ { "uri": "otpauth://totp/...", "manualKey": "JBSWY3DPEHPK3PXP" }
293
+ ```
294
+
295
+ **Confirm:**
296
+
297
+ ```json
298
+ { "enabled": true }
299
+ ```
300
+
301
+ **Verify:**
302
+
303
+ ```json
304
+ { "verified": true }
305
+ ```
306
+
307
+ **Disable:**
308
+
309
+ ```json
310
+ { "enabled": false }
311
+ ```
312
+
313
+ ### curl Cheat Sheet
314
+
315
+ ```bash
316
+ # Check 2FA status
317
+ curl -s --cert client.p12:password \
318
+ https://203.0.113.42:9292/api/settings/2fa | jq
319
+
320
+ # Start 2FA setup
321
+ curl -s -X POST --cert client.p12:password \
322
+ https://203.0.113.42:9292/api/settings/2fa/setup | jq
323
+
324
+ # Confirm setup with first code
325
+ curl -s -X POST --cert client.p12:password \
326
+ -H 'Content-Type: application/json' \
327
+ -d '{"code":"123456"}' \
328
+ https://203.0.113.42:9292/api/settings/2fa/confirm | jq
329
+
330
+ # Verify code to get session cookie
331
+ curl -s -X POST --cert client.p12:password \
332
+ -H 'Content-Type: application/json' \
333
+ -d '{"code":"123456"}' \
334
+ -c cookies.txt \
335
+ https://203.0.113.42:9292/api/settings/2fa/verify | jq
336
+
337
+ # Disable 2FA (requires session cookie)
338
+ curl -s -X POST --cert client.p12:password \
339
+ -H 'Content-Type: application/json' \
340
+ -d '{"code":"123456"}' \
341
+ -b cookies.txt \
342
+ https://203.0.113.42:9292/api/settings/2fa/disable | jq
343
+ ```