@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,490 @@
1
+ # Mutual TLS (mTLS)
2
+
3
+ > Lamaste uses client certificates to authenticate admin access — your browser proves its identity at the TLS level before any HTTP request is sent.
4
+
5
+ ## In Plain English
6
+
7
+ When you visit a normal HTTPS website, your browser checks the server's certificate to verify you are talking to the real site. This is one-way trust: the browser trusts the server.
8
+
9
+ Mutual TLS (mTLS) adds a second direction. The server also checks _your_ certificate to verify you are an authorized admin. This is two-way trust: the browser trusts the server, and the server trusts the browser.
10
+
11
+ Think of it like a members-only club with ID checks at the door. The club has a sign outside proving it is legitimate (the server certificate). But to get in, you also need to show your membership card (the client certificate). If you do not have the card, the bouncer does not even let you through the door — you never reach the lobby.
12
+
13
+ This is exactly how Lamaste protects its admin panel. During installation, a digital "membership card" (a `.p12` file) is generated. You import it into your browser once, and from then on, every time you visit the panel, your browser automatically presents the certificate. No username, no password, no login page. If someone without the certificate tries to access the panel, nginx rejects the connection at the TLS layer — before any HTTP traffic is exchanged.
14
+
15
+ This approach is inspired by [LXD](https://documentation.ubuntu.com/lxd/en/latest/), which uses the same client certificate model for its management API.
16
+
17
+ ## For Users
18
+
19
+ ### When you encounter mTLS
20
+
21
+ You encounter mTLS during two moments:
22
+
23
+ 1. **After installation** — the installer prints a `.p12` file path and password. You download this file and import it into your browser or OS keychain.
24
+ 2. **Every time you visit the panel** — your browser silently presents the certificate. You see no login prompt; you are simply authenticated.
25
+
26
+ ### Importing the certificate
27
+
28
+ After running `npx @lamalibre/create-lamaste@4.0.0`, the installer prints something like:
29
+
30
+ ```
31
+ ╔══════════════════════════════════════════════════════════════╗
32
+ ║ Client certificate ready ║
33
+ ║ ║
34
+ ║ Download: scp root@203.0.113.42:/etc/lamalibre/lamaste/pki/client.p12 . ║
35
+ ║ Password: a7f3b2e1d9c8456e ║
36
+ ║ Panel URL: https://203.0.113.42:9292 ║
37
+ ╚══════════════════════════════════════════════════════════════╝
38
+ ```
39
+
40
+ You copy the `.p12` file to your local machine and import it.
41
+
42
+ #### macOS (Safari and Chrome)
43
+
44
+ 1. Double-click the `.p12` file
45
+ 2. Keychain Access opens — choose "login" keychain
46
+ 3. Enter the password printed by the installer
47
+ 4. The certificate appears under "My Certificates" in Keychain Access
48
+ 5. Visit `https://203.0.113.42:9292` — Safari/Chrome prompts you to select a certificate
49
+ 6. Select the "Lamaste Client" certificate and click OK
50
+
51
+ macOS remembers this selection. Future visits present the certificate automatically.
52
+
53
+ #### macOS (Firefox)
54
+
55
+ Firefox uses its own certificate store, separate from Keychain Access:
56
+
57
+ 1. Open Firefox → Settings → Privacy & Security → Certificates → View Certificates
58
+ 2. Click "Your Certificates" tab → "Import"
59
+ 3. Select the `.p12` file and enter the password
60
+ 4. Visit `https://203.0.113.42:9292` — Firefox prompts you to select a certificate
61
+
62
+ #### Windows (Chrome and Edge)
63
+
64
+ 1. Double-click the `.p12` file
65
+ 2. The Certificate Import Wizard opens
66
+ 3. Choose "Current User" and click Next
67
+ 4. Enter the password and click Next
68
+ 5. Choose "Automatically select the certificate store" and click Next
69
+ 6. Visit `https://203.0.113.42:9292`
70
+
71
+ #### Linux (Chrome)
72
+
73
+ 1. Open Chrome → Settings → Privacy and Security → Security → Manage certificates
74
+ 2. Click "Your certificates" → "Import"
75
+ 3. Select the `.p12` file and enter the password
76
+ 4. Visit `https://203.0.113.42:9292`
77
+
78
+ ### What happens without a certificate
79
+
80
+ If you visit the panel URL in a browser that does not have the client certificate imported, nginx rejects the TLS handshake. Instead of the panel, you see a certificate help page explaining what happened and how to import the certificate.
81
+
82
+ The rejection happens at the TLS level — no HTTP request ever reaches the panel server. This is a stronger security boundary than a login page, which at minimum requires the server to parse and respond to HTTP requests.
83
+
84
+ ### Zero-login access
85
+
86
+ Once the certificate is imported, you never see a login page for the admin panel. Your browser presents the certificate automatically on every HTTPS connection to the panel. This is what "zero-login" means — authentication is handled by the TLS layer, not by the application.
87
+
88
+ This does not affect the tunneled apps. Visitors to your tunneled apps authenticate through [Authelia TOTP 2FA](authentication.md), which is a separate system entirely.
89
+
90
+ ### Multiple devices
91
+
92
+ The `.p12` file can be imported into multiple browsers and devices. If you want to access the admin panel from both your laptop and your phone, import the same `.p12` file on each.
93
+
94
+ If you lose access to the certificate and need a new one, run `sudo lamaste-server reset-admin` on the server (SSH or your provider's console). It issues a new admin certificate and revokes the old one; the panel itself never issues admin certificates.
95
+
96
+ ### Agent certificates
97
+
98
+ When connecting a machine to Lamaste using `lamaste-agent`, you should **not** use the admin certificate. Instead, generate a scoped agent certificate from the panel:
99
+
100
+ 1. Open the Lamaste panel → Certificates → Agent Certificates
101
+ 2. Click "Generate" and enter a label (e.g., `macbook-pro`)
102
+ 3. Save the displayed password — it cannot be retrieved later
103
+ 4. Download the `.p12` file
104
+ 5. Share the `.p12` file and password with the agent machine through a secure channel, then install the agent there (`npm install -g @lamalibre/lamaste-agent@4.0.0`) and run `lamaste-agent setup` (an enrollment token avoids moving the key at all — see [Agent Setup](../02-guides/agent-setup.md))
105
+
106
+ Agent certificates have capability-based access. By default, a new agent can only read tunnel configuration. Admins can grant additional capabilities per-agent from the panel UI:
107
+
108
+ | Capability | What it grants |
109
+ | ---------------- | -------------------------------------------------------------------------------------- |
110
+ | `tunnels:read` | List the agent's own tunnels and fetch its Chisel config (always-on) |
111
+ | `tunnels:write` | Create, update and delete tunnels the agent carries (restricted access mode only) |
112
+ | `services:read` | View service status |
113
+ | `services:write` | Start/stop/restart services |
114
+ | `system:read` | View system stats (CPU, RAM, disk) |
115
+ | `sites:read` | List sites and browse files |
116
+ | `sites:write` | Upload and delete files on assigned sites |
117
+ | `panel:expose` | Expose agent management panel at `agent-<label>.<domain>` via mTLS-protected subdomain |
118
+ | `identity:read` | Parse Authelia identity headers on plugin routes |
119
+ | `identity:query` | Query panel for Authelia user metadata |
120
+
121
+ Capabilities are stored server-side, so changing what an agent can do does not require reissuing its certificate. Users, certificates, agent management, and logs always remain admin-only.
122
+
123
+ If a machine is compromised, the attacker is limited to whichever capabilities were assigned to that agent's certificate — and the admin can revoke or reduce capabilities at any time from the panel. The agent will immediately lose access.
124
+
125
+ ## For Developers
126
+
127
+ ### PKI hierarchy
128
+
129
+ Lamaste generates a minimal two-level PKI (Public Key Infrastructure) during installation:
130
+
131
+ ```
132
+ Lamaste CA (self-signed, 10-year validity)
133
+ │
134
+ ├── Admin certificate (CN=admin, signed by CA, 2-year validity)
135
+ │ └── Full panel access
136
+ │
137
+ └── Agent certificates (CN=agent:<label>, signed by CA, 2-year validity)
138
+ └── Capability-based access (one per agent)
139
+ ```
140
+
141
+ The CA (Certificate Authority) is self-signed because there is no need for external trust. The only party that needs to trust the CA is nginx on the same server. nginx is configured with the CA's public certificate and rejects any client certificate not signed by that CA.
142
+
143
+ All certificates — admin and agent — are signed by the same CA. The difference is the Common Name (CN): the mTLS middleware parses the CN to determine the role and restricts access accordingly.
144
+
145
+ ### Certificate generation
146
+
147
+ The installer (`packages/create-lamaste/src/tasks/mtls.js`) generates the PKI in five steps:
148
+
149
+ ```
150
+ 1. Generate CA private key (4096-bit RSA)
151
+ 2. Create self-signed CA cert (CN=Lamaste CA, 10-year, SHA-256)
152
+ 3. Generate client private key (4096-bit RSA)
153
+ 4. Create CSR and sign it (CN=admin, 2-year, SHA-256)
154
+ 5. Export PKCS12 bundle (client key + cert + CA cert)
155
+ ```
156
+
157
+ Each step uses OpenSSL CLI commands via `execa`:
158
+
159
+ ```javascript
160
+ // Step 1: CA key
161
+ await execa('openssl', ['genrsa', '-out', `${pkiDir}/ca.key`, '4096']);
162
+
163
+ // Step 2: CA certificate
164
+ await execa('openssl', [
165
+ 'req',
166
+ '-x509',
167
+ '-new',
168
+ '-nodes',
169
+ '-key',
170
+ `${pkiDir}/ca.key`,
171
+ '-sha256',
172
+ '-days',
173
+ '3650',
174
+ '-out',
175
+ `${pkiDir}/ca.crt`,
176
+ '-subj',
177
+ '/CN=Lamaste CA/O=Lamaste',
178
+ ]);
179
+
180
+ // Step 3: Client key
181
+ await execa('openssl', ['genrsa', '-out', `${pkiDir}/client.key`, '4096']);
182
+
183
+ // Step 4: CSR + sign
184
+ await execa('openssl', [
185
+ 'req',
186
+ '-new',
187
+ '-key',
188
+ `${pkiDir}/client.key`,
189
+ '-out',
190
+ `${pkiDir}/client.csr`,
191
+ '-subj',
192
+ '/CN=admin/O=Lamaste',
193
+ ]);
194
+ await execa('openssl', [
195
+ 'x509',
196
+ '-req',
197
+ '-in',
198
+ `${pkiDir}/client.csr`,
199
+ '-CA',
200
+ `${pkiDir}/ca.crt`,
201
+ '-CAkey',
202
+ `${pkiDir}/ca.key`,
203
+ '-CAcreateserial',
204
+ '-out',
205
+ `${pkiDir}/client.crt`,
206
+ '-days',
207
+ '730',
208
+ '-sha256',
209
+ ]);
210
+
211
+ // Step 5: PKCS12 bundle
212
+ await execa('openssl', [
213
+ 'pkcs12',
214
+ '-export',
215
+ '-keypbe',
216
+ 'PBE-SHA1-3DES',
217
+ '-certpbe',
218
+ 'PBE-SHA1-3DES',
219
+ '-macalg',
220
+ 'sha1',
221
+ '-out',
222
+ `${pkiDir}/client.p12`,
223
+ '-inkey',
224
+ `${pkiDir}/client.key`,
225
+ '-in',
226
+ `${pkiDir}/client.crt`,
227
+ '-certfile',
228
+ `${pkiDir}/ca.crt`,
229
+ '-passout',
230
+ `pass:${password}`,
231
+ ]);
232
+ ```
233
+
234
+ ### PKCS12 compatibility flags
235
+
236
+ The PKCS12 export uses legacy encryption algorithms (`PBE-SHA1-3DES`) and SHA-1 MAC. This is deliberate. Modern OpenSSL 3.x defaults to AES-256-CBC with SHA-256, which macOS Keychain Access cannot import. The legacy flags ensure compatibility across all platforms:
237
+
238
+ | Platform | Modern PKCS12 | Legacy PKCS12 (PBE-SHA1-3DES) |
239
+ | --------------------- | :-----------: | :---------------------------: |
240
+ | macOS Keychain Access | fails | works |
241
+ | Firefox | works | works |
242
+ | Chrome (Windows) | works | works |
243
+ | Chrome (Linux) | works | works |
244
+
245
+ ### nginx mTLS enforcement
246
+
247
+ The installer writes an nginx snippet at `/etc/nginx/snippets/lamalibre-lamaste-mtls.conf`:
248
+
249
+ ```nginx
250
+ ssl_client_certificate /etc/lamalibre/lamaste/pki/ca.crt;
251
+ ssl_verify_client optional;
252
+ ```
253
+
254
+ This snippet is included in every nginx vhost that should require mTLS. The `ssl_verify_client optional` directive enables client certificate verification at the TLS level while still allowing connections without a certificate (needed for public endpoints like `/api/enroll` and `/api/invite`). Protected locations enforce mTLS via `if ($ssl_client_verify != SUCCESS) { return 496; }` in each vhost's location blocks. If a client presents a certificate not signed by the specified CA, nginx returns a 495 or 496 error — before any application code runs.
255
+
256
+ The panel vhost includes this snippet and uses per-location `if` checks to enforce mTLS on protected locations while allowing public endpoints through:
257
+
258
+ ```nginx
259
+ map $http_upgrade $connection_upgrade {
260
+ default upgrade;
261
+ '' close;
262
+ }
263
+
264
+ server {
265
+ listen 9292 ssl;
266
+ server_name _;
267
+
268
+ ssl_certificate /etc/lamalibre/lamaste/pki/self-signed.pem;
269
+ ssl_certificate_key /etc/lamalibre/lamaste/pki/self-signed-key.pem;
270
+
271
+ include /etc/nginx/snippets/lamalibre-lamaste-mtls.conf;
272
+
273
+ # Show help page when client cert is missing
274
+ error_page 495 496 /cert-help.html;
275
+ location = /cert-help.html {
276
+ root /opt/lamalibre/lamaste/lamaste-server-ui;
277
+ internal;
278
+ }
279
+
280
+ # Protected locations — reject if client cert missing or invalid
281
+ location / {
282
+ if ($ssl_client_verify != SUCCESS) {
283
+ return 496;
284
+ }
285
+ proxy_pass http://127.0.0.1:3100;
286
+ proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
287
+ proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
288
+ proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
289
+ proxy_set_header Host $host;
290
+ proxy_set_header X-Real-IP $remote_addr;
291
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
292
+ proxy_set_header X-Forwarded-Proto $scheme;
293
+ }
294
+
295
+ # Public API paths — no mTLS check, cert headers cleared
296
+ location /api/enroll {
297
+ proxy_pass http://127.0.0.1:3100;
298
+ proxy_http_version 1.1;
299
+ proxy_set_header X-SSL-Client-Verify "";
300
+ proxy_set_header X-SSL-Client-DN "";
301
+ proxy_set_header X-SSL-Client-Serial "";
302
+ # ... standard proxy headers
303
+ }
304
+
305
+ location /api/invite {
306
+ proxy_pass http://127.0.0.1:3100;
307
+ proxy_http_version 1.1;
308
+ proxy_set_header X-SSL-Client-Verify "";
309
+ proxy_set_header X-SSL-Client-DN "";
310
+ proxy_set_header X-SSL-Client-Serial "";
311
+ # ... standard proxy headers
312
+ }
313
+
314
+ # API paths with WebSocket upgrade support (mTLS required)
315
+ location /api {
316
+ if ($ssl_client_verify != SUCCESS) {
317
+ return 496;
318
+ }
319
+ proxy_pass http://127.0.0.1:3100;
320
+ proxy_http_version 1.1;
321
+ proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
322
+ proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
323
+ proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
324
+ proxy_set_header Host $host;
325
+ proxy_set_header X-Real-IP $remote_addr;
326
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
327
+ proxy_set_header X-Forwarded-Proto $scheme;
328
+ proxy_set_header Upgrade $http_upgrade;
329
+ proxy_set_header Connection $connection_upgrade;
330
+ }
331
+ }
332
+ ```
333
+
334
+ The `error_page 495 496` directive shows a help page explaining how to import the certificate, rather than showing a cryptic TLS error. Public endpoints (`/api/enroll` for agent enrollment and `/api/invite` for user invitations) clear the client certificate headers so that the Fastify server treats them as unauthenticated requests, matching their registration in the `publicContext` (no mTLS middleware).
335
+
336
+ ### Certificate help page
337
+
338
+ When a visitor without a client certificate hits the panel, nginx serves a static HTML page from `/opt/lamalibre/lamaste/lamaste-server-ui/cert-help.html`. This page explains what mTLS is and provides step-by-step import instructions. The page is generated during installation with the VPS IP address embedded, so the SCP command is ready to copy.
339
+
340
+ ### Client certificate headers
341
+
342
+ After TLS negotiation succeeds, nginx forwards client certificate information to the panel server via headers:
343
+
344
+ ```nginx
345
+ proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
346
+ proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
347
+ proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
348
+ ```
349
+
350
+ The panel server's mTLS middleware checks these headers in order:
351
+
352
+ 1. **`X-SSL-Client-Verify`** — must be `SUCCESS` (valid client cert); any other value (`FAILED`, `NONE`) results in a 403 Forbidden response.
353
+ 2. **`X-SSL-Client-Serial`** — checked against the revocation list (`revoked.json`); if the serial is revoked, the request is rejected with 403.
354
+ 3. **`X-SSL-Client-DN`** — the CN is parsed to determine the role. `CN=admin` grants full access; `CN=agent:<label>` grants capability-based access with permissions looked up from the agent registry.
355
+
356
+ In development (`NODE_ENV=development`), all checks are skipped.
357
+
358
+ ### Certificate rotation
359
+
360
+ Client certificates expire after 2 years. The management panel's Certificates page shows the expiry date. The admin certificate is rotated on the server with `sudo lamaste-server reset-admin` (`packages/server/cli/src/commands/reset-admin.js`) — the panel runs unprivileged and signs only agent certificates, and its rotation endpoint answers `503` with that instruction:
361
+
362
+ ```
363
+ 1. Verify CA key exists
364
+ 2. Generate new client key (4096-bit RSA) ─┐
365
+ 3. Create CSR (CN=admin) and sign with the CA (2 yr) │ run as the lamaste user,
366
+ 4. Create new PKCS12 bundle with random password │ which owns the PKI directory
367
+ 5. Back up current client.key, client.crt, client.p12 │
368
+ 6. Move new files into place ─┘
369
+ 7. Revoke the old admin certificate, clear panel 2FA, set adminAuthMode to p12
370
+ 8. Restart the panel, reload nginx, print the new PKCS12 password
371
+ ```
372
+
373
+ The CA certificate is never rotated — it has a 10-year validity. Since nginx trusts the CA, and all client certificates are signed by the same CA, rotating the client cert does not require any nginx changes.
374
+
375
+ ### Idempotent installation
376
+
377
+ The mTLS generation task is idempotent. If `ca.key` and `client.p12` already exist (e.g., the installer is re-run), the entire generation is skipped. This prevents invalidating a previously imported client certificate:
378
+
379
+ ```javascript
380
+ const alreadyProvisioned = existsSync(`${pkiDir}/ca.key`) && existsSync(`${pkiDir}/client.p12`);
381
+
382
+ if (alreadyProvisioned) {
383
+ // Read existing p12 password for the summary display
384
+ ctx.p12Password = await readFile(`${pkiDir}/.p12-password`, 'utf8');
385
+ return; // Skip all generation steps
386
+ }
387
+ ```
388
+
389
+ ### File layout
390
+
391
+ ```
392
+ /etc/lamalibre/lamaste/pki/
393
+ ├── ca.key (600) CA private key — never leaves the server
394
+ ├── ca.crt (644) CA certificate — nginx uses this to verify clients
395
+ ├── client.key (600) Admin client private key — bundled into .p12
396
+ ├── client.crt (644) Admin client certificate — bundled into .p12
397
+ ├── client.p12 (600) Admin PKCS12 bundle — downloaded by admin
398
+ ├── .p12-password (600) Password for the admin .p12 file
399
+ ├── self-signed.pem (644) Self-signed TLS cert for IP access
400
+ ├── self-signed-key.pem (600) Key for self-signed TLS cert
401
+ ├── revoked.json (600) Revoked certificate serial numbers
402
+ └── agents/ Agent certificate storage
403
+ ├── registry.json (600) Metadata for all agent certs
404
+ └── <label>/ Per-agent directory
405
+ ├── client.key (600) Agent private key
406
+ ├── client.crt (644) Agent certificate (CN=agent:<label>)
407
+ └── client.p12 (600) Agent PKCS12 bundle
408
+ ```
409
+
410
+ ### Source files
411
+
412
+ | File | Purpose |
413
+ | --------------------------------------------------------- | -------------------------------------------------- |
414
+ | `packages/create-lamaste/src/tasks/mtls.js` | PKI generation during installation |
415
+ | `packages/create-lamaste/src/tasks/nginx.js` | mTLS snippet and panel vhost |
416
+ | `packages/lamaste-serverd/src/lib/mtls.js` | Certificate rotation, expiry checks, .p12 download |
417
+ | `packages/lamaste-serverd/src/middleware/mtls.js` | Request-level mTLS verification |
418
+ | `packages/lamaste-serverd/src/routes/management/certs.js` | Certificate management API endpoints |
419
+
420
+ ## Quick Reference
421
+
422
+ ### PKI hierarchy
423
+
424
+ | Certificate | Signed by | Validity | Key size | Purpose |
425
+ | ----------------------------------- | ----------- | -------- | ------------ | ------------------------------- |
426
+ | CA (`ca.crt`) | Self-signed | 10 years | 4096-bit RSA | Trust anchor for nginx |
427
+ | Admin (`client.crt`) | CA | 2 years | 4096-bit RSA | Full panel access (browser) |
428
+ | Agent (`agents/<label>/client.crt`) | CA | 2 years | 4096-bit RSA | Capability-based access (agent) |
429
+ | Self-signed TLS | Self-signed | 10 years | 2048-bit RSA | HTTPS for IP access |
430
+
431
+ ### File permissions
432
+
433
+ | File | Mode | Owner | Access |
434
+ | --------------- | ----- | ------- | ------------------- |
435
+ | `ca.key` | `600` | lamaste | CA signing only |
436
+ | `ca.crt` | `644` | lamaste | nginx reads this |
437
+ | `client.key` | `600` | lamaste | Bundled in .p12 |
438
+ | `client.crt` | `644` | lamaste | Bundled in .p12 |
439
+ | `client.p12` | `600` | lamaste | Downloaded by admin |
440
+ | `.p12-password` | `600` | lamaste | Installer summary |
441
+
442
+ The PKI directory (`0700`) belongs to the panel's `lamaste` user: the panel is the CA and signs agent certificates itself, unprivileged, refusing any CN that is not an agent or plugin-agent label. Admin certificates are issued only by `sudo lamaste-server reset-admin` on the server, which runs its `openssl` steps as `lamaste`.
443
+
444
+ ### nginx directives
445
+
446
+ | Directive | Value | Effect |
447
+ | ------------------------ | ----------------------------------- | ------------------------------------------------------------ |
448
+ | `ssl_client_certificate` | `/etc/lamalibre/lamaste/pki/ca.crt` | CA that signs valid client certs |
449
+ | `ssl_verify_client` | `optional` | Enable client cert verification (per-location `if` enforces) |
450
+ | `error_page 495 496` | `/cert-help.html` | Show help when cert is missing |
451
+
452
+ ### OpenSSL commands
453
+
454
+ ```bash
455
+ # View CA certificate details
456
+ openssl x509 -in /etc/lamalibre/lamaste/pki/ca.crt -text -noout
457
+
458
+ # View client certificate details
459
+ openssl x509 -in /etc/lamalibre/lamaste/pki/client.crt -text -noout
460
+
461
+ # Check client certificate expiry
462
+ openssl x509 -in /etc/lamalibre/lamaste/pki/client.crt -enddate -noout
463
+
464
+ # Verify client cert is signed by CA
465
+ openssl verify -CAfile /etc/lamalibre/lamaste/pki/ca.crt /etc/lamalibre/lamaste/pki/client.crt
466
+
467
+ # Inspect PKCS12 bundle contents
468
+ openssl pkcs12 -in /etc/lamalibre/lamaste/pki/client.p12 -info -nokeys
469
+ ```
470
+
471
+ ### API endpoints
472
+
473
+ | Method | Path | Description |
474
+ | ------ | --------------------------------------- | ------------------------------------ |
475
+ | POST | `/api/certs/mtls/rotate` | Rotate admin client certificate |
476
+ | GET | `/api/certs/mtls/download` | Download current admin `.p12` file |
477
+ | GET | `/api/certs` | List all certificates including mTLS |
478
+ | POST | `/api/certs/agent` | Generate agent-scoped certificate |
479
+ | GET | `/api/certs/agent` | List agent certificates |
480
+ | GET | `/api/certs/agent/:label/download` | Download agent `.p12` file |
481
+ | PATCH | `/api/certs/agent/:label/capabilities` | Update agent capabilities |
482
+ | PATCH | `/api/certs/agent/:label/allowed-sites` | Update agent site access |
483
+ | DELETE | `/api/certs/agent/:label` | Revoke agent certificate |
484
+
485
+ ### Related documentation
486
+
487
+ - [Authentication](authentication.md) — how tunneled apps use Authelia TOTP (separate from mTLS)
488
+ - [Certificates](certificates.md) — Let's Encrypt certificates for domains
489
+ - [Security Model](security-model.md) — mTLS as part of the defense-in-depth strategy
490
+ - [nginx Reverse Proxy](nginx-reverse-proxy.md) — how nginx enforces mTLS