@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,464 @@
1
+ # Authentication
2
+
3
+ > Lamaste uses two separate authentication systems: mTLS client certificates for admin access, and Authelia TOTP two-factor authentication for end-user access to tunneled apps.
4
+
5
+ ## In Plain English
6
+
7
+ Lamaste protects two different things, and each uses a different type of lock.
8
+
9
+ The **admin panel** is protected by a client certificate — like a digital ID card that your browser presents automatically. Only you, the server operator, have this ID card. No username, no password, no login page. This is [mTLS](mtls.md).
10
+
11
+ The **tunneled apps** (the web apps you expose to the internet) are protected by a separate system called Authelia. When a visitor tries to access your app, they see a login page where they enter a username, password, and a 6-digit code from an authenticator app on their phone (like Google Authenticator or Authy). This is TOTP two-factor authentication — something you know (password) plus something you have (your phone).
12
+
13
+ Think of it this way: the admin panel is your house's back door, opened only by a fingerprint scanner (the certificate). The tunneled apps are the front door, opened by a key and an alarm code (password plus TOTP).
14
+
15
+ These two systems are completely independent. Admin certificate holders do not automatically get access to tunneled apps, and Authelia users cannot access the admin panel.
16
+
17
+ There is also an **optional built-in 2FA layer** for the admin panel itself. When enabled, the admin must present their client certificate _and_ enter a TOTP code to access the panel. This adds a second factor on top of mTLS, protecting against scenarios where the certificate file is compromised. Agents are exempt from this requirement — they authenticate with mTLS only.
18
+
19
+ ## For Users
20
+
21
+ ### Who uses what
22
+
23
+ | Person | Accesses | Authentication method |
24
+ | --------------- | --------------------------------------- | ---------------------------------------------- |
25
+ | You (the admin) | Management panel at `https://<IP>:9292` | Client certificate (mTLS), optionally + TOTP |
26
+ | Your users | Tunneled apps at `myapp.example.com` | Username + password + TOTP code |
27
+ | Agents | Panel API | Client certificate (mTLS) — 2FA does not apply |
28
+
29
+ ### Managing users
30
+
31
+ From the Users page in the management panel, you can:
32
+
33
+ - **Create users** — set a username, display name, email, and password
34
+ - **Delete users** — remove a user (you cannot delete the last user)
35
+ - **Reset TOTP** — generate a new TOTP secret if a user loses their authenticator (separate step via `POST /api/users/:username/reset-totp`)
36
+ - **Update password** — change a user's password
37
+
38
+ ### Creating a user
39
+
40
+ 1. Navigate to the Users page in the management panel
41
+ 2. Click "Add User"
42
+ 3. Enter a username, display name, email, and password
43
+ 4. Lamaste creates the user in `users.yml` and restarts Authelia
44
+ 5. Share the credentials with the user — they enroll in TOTP on first login
45
+
46
+ ### TOTP enrollment flow
47
+
48
+ TOTP enrollment is a separate step from user creation. There are two paths:
49
+
50
+ **First-login enrollment:** When a new user visits a Lamaste-protected app for the first time, Authelia presents a QR code during their initial login. The user scans it with their authenticator app to complete enrollment.
51
+
52
+ **Admin-initiated reset:** If a user loses their authenticator, the admin clicks "Reset TOTP" on the Users page, which calls `POST /api/users/:username/reset-totp`. This writes the new TOTP secret to Authelia's SQLite database via the `authelia storage user totp generate` CLI command. A QR code is displayed for the user to scan.
53
+
54
+ ```
55
+ Admin creates user → User visits protected app
56
+ → Authelia prompts for TOTP enrollment on first login
57
+ → User scans QR code with authenticator app
58
+ → Authenticator generates 6-digit codes every 30 seconds
59
+ ```
60
+
61
+ The QR code encodes a standard `otpauth://` URI:
62
+
63
+ ```
64
+ otpauth://totp/Lamaste:username?secret=BASE32SECRET&issuer=Lamaste&algorithm=SHA1&digits=6&period=30
65
+ ```
66
+
67
+ ### How a user logs in
68
+
69
+ When a visitor browses to a tunneled app (e.g., `https://myapp.example.com`), nginx checks if they are authenticated via Authelia. If not:
70
+
71
+ 1. They are redirected to `https://auth.example.com` — the Authelia login portal
72
+ 2. They enter their username and password
73
+ 3. They enter the current 6-digit TOTP code from their authenticator app
74
+ 4. Authelia sets a session cookie and redirects them back to the app
75
+ 5. On subsequent visits (within the session), they are not prompted again
76
+
77
+ Session duration is 12 hours, with a 2-hour inactivity timeout. After that, the user must authenticate again.
78
+
79
+ ### What happens when a user loses their authenticator
80
+
81
+ If a user loses their phone or uninstalls their authenticator app, you (the admin) can reset their TOTP from the Users page. This generates a new TOTP secret and displays a new QR code for them to scan.
82
+
83
+ ### Optional panel 2FA (admin only)
84
+
85
+ You can enable a built-in TOTP 2FA layer for admin panel access. This is separate from Authelia (which protects tunneled apps) and adds a second factor on top of the mTLS client certificate.
86
+
87
+ **How it works:**
88
+
89
+ 1. Enable 2FA from the panel settings. A QR code is displayed for your authenticator app.
90
+ 2. On subsequent visits, after mTLS succeeds, a TOTP prompt appears. Enter the 6-digit code to proceed.
91
+ 3. A session cookie (`lamaste_2fa_session`) is set with a 12-hour absolute expiry and a 2-hour inactivity timeout. The cookie is HMAC-SHA256 signed using a `sessionSecret` stored in `panel.json`, and is marked `HttpOnly`, `Secure`, and `SameSite=Strict`.
92
+
93
+ **Important behavior changes when 2FA is enabled:**
94
+
95
+ - The IP-based vhost (`https://<IP>:9292`) is **disabled**. Panel access is domain-only. This prevents session cookies from being scoped to an IP address.
96
+ - Rate limiting applies: 5 failed TOTP attempts within 2 minutes trigger a 5-minute ban.
97
+ - Agents are **not affected** — they authenticate via mTLS only and bypass the 2FA check entirely.
98
+
99
+ **TOTP parameters:** RFC 6238, SHA-1, 30-second period, 6 digits, +/-1 step clock drift tolerance, replay protection (each code can only be used once).
100
+
101
+ **Configuration:** Panel 2FA state is stored in `panel.json` under `panel2fa: { enabled, secret, setupComplete }`. The session signing key is stored as `sessionSecret` in the same file, generated with `crypto.randomBytes` on first 2FA setup.
102
+
103
+ **Recovery:** If you lose your authenticator, run `sudo lamaste-reset-admin` on the server. This clears 2FA, re-enables the IP-based vhost, and reverts to standard mTLS-only admin access. See [Disaster Recovery](../02-guides/disaster-recovery.md) for details.
104
+
105
+ ## For Developers
106
+
107
+ ### Authelia overview
108
+
109
+ [Authelia](https://www.authelia.com/) is an open-source authentication and authorization server written in Go. Lamaste uses it specifically for TOTP two-factor authentication via the nginx forward-auth pattern.
110
+
111
+ Authelia runs as a systemd service on the VPS, binding to `127.0.0.1:9091`. It is never exposed directly to the internet — nginx proxies `auth.example.com` to it.
112
+
113
+ ### Why Authelia
114
+
115
+ Several factors led to choosing Authelia:
116
+
117
+ - **Single binary** — no runtime dependencies, simple deployment
118
+ - **Low memory** — ~25MB RAM at idle, fitting the 512MB budget
119
+ - **File-based users** — reads `users.yml` directly, no database required
120
+ - **TOTP built-in** — native support for time-based one-time passwords
121
+ - **nginx forward-auth** — designed for the `auth_request` integration pattern
122
+ - **bcrypt support** — critical for the RAM constraint (see below)
123
+
124
+ ### bcrypt vs argon2id — the RAM constraint
125
+
126
+ Authelia supports both bcrypt and argon2id for password hashing. Lamaste **must** use bcrypt.
127
+
128
+ Argon2id is generally considered the stronger algorithm, but it is memory-hard by design. Each hash operation allocates ~93MB of RAM. On a 512MB droplet running multiple services, a single authentication attempt with argon2id can trigger the Linux OOM killer, crashing the entire server.
129
+
130
+ Bcrypt uses minimal memory (~4KB per hash) and is configured with cost factor 12, which provides strong resistance to brute-force attacks without memory pressure:
131
+
132
+ ```yaml
133
+ # From Authelia configuration
134
+ authentication_backend:
135
+ file:
136
+ path: /etc/authelia/users.yml
137
+ password:
138
+ algorithm: bcrypt
139
+ bcrypt:
140
+ cost: 12
141
+ ```
142
+
143
+ ### Authelia configuration
144
+
145
+ The full configuration is written during onboarding provisioning (`packages/lamaste-serverd/src/lib/authelia.js`):
146
+
147
+ ```yaml
148
+ server:
149
+ address: 'tcp://127.0.0.1:9091/'
150
+
151
+ log:
152
+ level: info
153
+ file_path: /var/log/authelia/authelia.log
154
+
155
+ identity_validation:
156
+ reset_password:
157
+ jwt_secret: <random-64-byte-hex>
158
+
159
+ authentication_backend:
160
+ file:
161
+ path: /etc/authelia/users.yml
162
+ password:
163
+ algorithm: bcrypt
164
+ bcrypt:
165
+ cost: 12
166
+
167
+ access_control:
168
+ default_policy: two_factor
169
+
170
+ session:
171
+ name: lamaste_session
172
+ secret: <random-64-byte-hex>
173
+ cookies:
174
+ - domain: example.com
175
+ authelia_url: https://auth.example.com
176
+ default_redirection_url: https://example.com
177
+ expiration: 12h
178
+ inactivity: 2h
179
+
180
+ regulation:
181
+ max_retries: 5
182
+ find_time: 2m
183
+ ban_time: 5m
184
+
185
+ storage:
186
+ encryption_key: <random-64-byte-hex>
187
+ local:
188
+ path: /etc/authelia/db.sqlite3
189
+
190
+ notifier:
191
+ filesystem:
192
+ filename: /etc/authelia/notifications.txt
193
+
194
+ totp:
195
+ issuer: Lamaste
196
+ period: 30
197
+ digits: 6
198
+ ```
199
+
200
+ Key configuration choices:
201
+
202
+ - **`server.address: 'tcp://127.0.0.1:9091/'`** — binds to localhost only; nginx handles public access
203
+ - **`default_policy: two_factor`** — requires password + TOTP for all authenticated users
204
+ - **`session.cookies`** — array format (Authelia v4.38+) specifying the domain and Authelia URL for session cookies
205
+ - **`notifier.filesystem`** — writes notifications to a file instead of sending email (suitable for small-scale use)
206
+ - **`totp.period: 30`** — standard 30-second TOTP window
207
+
208
+ ### Users file format
209
+
210
+ Authelia reads user data from `/etc/authelia/users.yml`:
211
+
212
+ ```yaml
213
+ users:
214
+ alice:
215
+ displayname: alice
216
+ password: $2b$12$xxxxx... # bcrypt hash
217
+ email: alice@lamaste.local
218
+ groups:
219
+ - admins
220
+ ```
221
+
222
+ Lamaste manages this file through the panel server API. All writes are atomic: content is written to a temporary file first, then moved into place. This prevents Authelia from reading a partially written file.
223
+
224
+ After every write to `users.yml`, the panel server reloads Authelia via `systemctl restart authelia` so the changes take effect.
225
+
226
+ ### Creating users programmatically
227
+
228
+ The panel server creates users by hashing the password with bcrypt and writing to `users.yml`:
229
+
230
+ ```javascript
231
+ // From packages/lamaste-serverd/src/lib/authelia.js
232
+ export async function createUser(username, password) {
233
+ // Hash password using bcrypt with cost factor 12
234
+ const hash = await bcrypt.hash(password, 12);
235
+
236
+ // Read existing users or start fresh
237
+ let usersData = { users: {} };
238
+ try {
239
+ const { stdout } = await execa('sudo', ['cat', AUTHELIA_USERS]);
240
+ const parsed = yaml.load(stdout);
241
+ if (parsed && parsed.users) usersData = parsed;
242
+ } catch {
243
+ // File doesn't exist — start fresh
244
+ }
245
+
246
+ usersData.users[username] = {
247
+ displayname: username,
248
+ password: hash,
249
+ email: `${username}@lamaste.local`,
250
+ groups: ['admins'],
251
+ };
252
+
253
+ await writeUsers(usersData);
254
+ return { username, created: true };
255
+ }
256
+ ```
257
+
258
+ ### TOTP secret generation
259
+
260
+ TOTP secrets are generated using `crypto.randomBytes` and encoded as base32:
261
+
262
+ ```javascript
263
+ // From packages/lamaste-serverd/src/lib/authelia.js
264
+ export function generateTotpSecret(username, opts) {
265
+ const issuer = opts?.issuer ?? 'Lamaste';
266
+ const secretBytes = crypto.randomBytes(20);
267
+ const secret = base32Encode(secretBytes);
268
+ const uri =
269
+ `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(username)}?` +
270
+ `secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=SHA1&digits=6&period=30`;
271
+ return { secret, uri };
272
+ }
273
+ ```
274
+
275
+ This function is also reused by the panel 2FA feature (`lib/totp.js`) with `{ issuer: 'Lamaste Panel' }` to generate a distinct TOTP entry for admin panel access.
276
+
277
+ The `otpauth://` URI follows the [Google Authenticator Key URI format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format). This URI is rendered as a QR code in the panel client.
278
+
279
+ ### nginx forward-auth pattern
280
+
281
+ The forward-auth pattern is how nginx delegates authentication decisions to Authelia. For each request to a protected app, nginx sends a sub-request to Authelia asking "is this user authenticated?"
282
+
283
+ Here is the nginx configuration for a protected app vhost:
284
+
285
+ ```nginx
286
+ # Internal location — subrequest to Authelia
287
+ location /authelia {
288
+ internal;
289
+ proxy_pass http://127.0.0.1:9091/api/verify?rd=https://auth.example.com/;
290
+ proxy_pass_request_body off;
291
+ proxy_set_header Content-Length "";
292
+ proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
293
+ proxy_set_header X-Forwarded-Method $request_method;
294
+ proxy_set_header X-Forwarded-Proto $scheme;
295
+ proxy_set_header X-Forwarded-Host $http_host;
296
+ proxy_set_header X-Forwarded-Uri $request_uri;
297
+ proxy_set_header X-Forwarded-For $remote_addr;
298
+ }
299
+
300
+ # Main location — protected by auth_request
301
+ location / {
302
+ auth_request /authelia;
303
+ auth_request_set $user $upstream_http_remote_user;
304
+ auth_request_set $groups $upstream_http_remote_groups;
305
+ auth_request_set $name $upstream_http_remote_name;
306
+ auth_request_set $email $upstream_http_remote_email;
307
+
308
+ proxy_set_header Remote-User $user;
309
+ proxy_set_header Remote-Groups $groups;
310
+ proxy_set_header Remote-Name $name;
311
+ proxy_set_header Remote-Email $email;
312
+
313
+ proxy_pass http://127.0.0.1:PORT;
314
+ }
315
+
316
+ # Unauthenticated users are redirected to Authelia
317
+ error_page 401 =302 https://auth.example.com/?rd=$scheme://$http_host$request_uri;
318
+ ```
319
+
320
+ The flow works like this:
321
+
322
+ ```
323
+ 1. Visitor requests myapp.example.com
324
+ 2. nginx sends subrequest to Authelia's /api/verify endpoint
325
+ 3. Authelia checks for a valid session cookie:
326
+ a. Cookie present and valid → returns 200 with user headers
327
+ b. No cookie or expired → returns 401
328
+ 4. If 200: nginx proxies to the app with Remote-User/Groups/Name/Email headers
329
+ 5. If 401: nginx redirects to auth.example.com with the original URL as ?rd= parameter
330
+ 6. User authenticates on Authelia portal
331
+ 7. Authelia sets session cookie and redirects back to the original URL
332
+ 8. On the redirect, step 3a succeeds and the user sees the app
333
+ ```
334
+
335
+ ### User headers
336
+
337
+ When authentication succeeds, Authelia returns user information in response headers. nginx captures these with `auth_request_set` and forwards them to the proxied app:
338
+
339
+ | Header | Content | Example |
340
+ | --------------- | ---------------------- | --------------------- |
341
+ | `Remote-User` | Username | `alice` |
342
+ | `Remote-Groups` | Comma-separated groups | `admins` |
343
+ | `Remote-Name` | Display name | `alice` |
344
+ | `Remote-Email` | Email address | `alice@lamaste.local` |
345
+
346
+ Your tunneled app can read these headers to identify the authenticated user without implementing its own authentication.
347
+
348
+ ### Atomic YAML writes
349
+
350
+ The `users.yml` file is read live by Authelia. To prevent Authelia from reading a partially written file, writes use atomic operations:
351
+
352
+ ```javascript
353
+ export async function writeUsers(usersData) {
354
+ const yamlContent = yaml.dump(usersData, { lineWidth: -1 });
355
+ // 0660: Authelia rewrites the file in place when a user changes a password.
356
+ await writeAutheliaFile(AUTHELIA_USERS, yamlContent, 0o660);
357
+ }
358
+ ```
359
+
360
+ The `writeAutheliaFile` helper writes a random temp file in `/etc/authelia/`, `fsync`s it and renames it over the target. Since a rename on the same filesystem is atomic at the kernel level, Authelia never sees a half-written file. No sudo is involved: `/etc/authelia` belongs to the panel's `lamaste` user with group `lamaste-authelia` and the setgid bit, so new files belong to Authelia's group. Authelia itself runs as the unprivileged `lamaste-authelia` account, not root.
361
+
362
+ Storing a user's TOTP secret is the one Authelia operation the panel cannot do itself — the secret lives in Authelia's database, which is that account's file. The panel runs `sudo lamaste-priv authelia-totp <username>` with the secret on stdin (never on a command line), and the root helper runs `authelia storage user totp generate` as `lamaste-authelia`.
363
+
364
+ ### Secrets management
365
+
366
+ Authelia requires three secrets, all generated with `crypto.randomBytes`:
367
+
368
+ | Secret | Purpose | Storage |
369
+ | ---------------------- | ------------------------ | --------------------------------- |
370
+ | `jwtSecret` | Signs JWT tokens | `/etc/authelia/configuration.yml` |
371
+ | `sessionSecret` | Encrypts session cookies | `/etc/authelia/configuration.yml` |
372
+ | `storageEncryptionKey` | Encrypts SQLite database | `/etc/authelia/configuration.yml` |
373
+
374
+ A copy is also stored in `/etc/authelia/.secrets.json` (mode `600`) for reference. The configuration file itself is mode `600` to prevent unauthorized reading of these secrets.
375
+
376
+ ### Safety rule: never delete the last user
377
+
378
+ The panel server API prevents deleting the last Authelia user. If only one user exists and you try to delete them, the API returns an error. This prevents a lockout scenario where no one can authenticate to reach the tunneled apps.
379
+
380
+ ### Source files
381
+
382
+ | File | Purpose |
383
+ | ------------------------------------------------------------ | ---------------------------------------------------- |
384
+ | `packages/lamaste-serverd/src/lib/authelia.js` | Install, configure, user CRUD, TOTP generation |
385
+ | `packages/lamaste-serverd/src/routes/management/users.js` | User management API endpoints |
386
+ | `packages/lamaste-serverd/src/lib/nginx.js` | App vhost with Authelia forward-auth block |
387
+ | `packages/lamaste-serverd/src/lib/certbot.js` | TLS cert for `auth.example.com` subdomain |
388
+ | `packages/lamaste-serverd/src/lib/totp.js` | Panel 2FA TOTP verification and replay protection |
389
+ | `packages/lamaste-serverd/src/lib/session.js` | HMAC-SHA256 session cookie signing and validation |
390
+ | `packages/lamaste-serverd/src/middleware/twofa-session.js` | Fastify plugin enforcing 2FA session for admin certs |
391
+ | `packages/lamaste-serverd/src/routes/management/settings.js` | Panel 2FA setup, verify, and disable endpoints |
392
+
393
+ ## Quick Reference
394
+
395
+ ### Two authentication systems
396
+
397
+ | System | Protects | Method | Session |
398
+ | --------- | ------------- | -------------------------------- | --------------------------------------------------- |
399
+ | mTLS | Admin panel | Client certificate | Permanent (cert-based) |
400
+ | Panel 2FA | Admin panel | mTLS + TOTP (opt-in, admin only) | 12h absolute, 2h inactivity (`lamaste_2fa_session`) |
401
+ | Authelia | Tunneled apps | Password + TOTP | 12h expiry, 2h inactivity |
402
+
403
+ ### Authelia service
404
+
405
+ | Property | Value |
406
+ | ------------ | --------------------------------- |
407
+ | Binary | `/usr/local/bin/authelia` |
408
+ | Config | `/etc/authelia/configuration.yml` |
409
+ | Users | `/etc/authelia/users.yml` |
410
+ | Database | `/etc/authelia/db.sqlite3` |
411
+ | Log | `/var/log/authelia/authelia.log` |
412
+ | Listen | `127.0.0.1:9091` |
413
+ | Systemd unit | `authelia.service` |
414
+ | RAM usage | ~25MB |
415
+
416
+ ### TOTP parameters
417
+
418
+ | Parameter | Value |
419
+ | ------------- | ------------------- |
420
+ | Algorithm | SHA-1 |
421
+ | Digits | 6 |
422
+ | Period | 30 seconds |
423
+ | Issuer | `Lamaste` |
424
+ | Secret length | 20 bytes (160 bits) |
425
+ | Encoding | Base32 |
426
+
427
+ ### Systemd commands
428
+
429
+ ```bash
430
+ # Check Authelia status
431
+ systemctl status authelia
432
+
433
+ # View recent logs
434
+ journalctl -u authelia -n 50 --no-pager
435
+
436
+ # Restart after users.yml changes
437
+ sudo systemctl restart authelia
438
+ ```
439
+
440
+ ### API endpoints
441
+
442
+ | Method | Path | Description |
443
+ | ------ | --------------------------------- | ----------------------------------------------------------- |
444
+ | GET | `/api/users` | List all users (without password hashes) |
445
+ | POST | `/api/users` | Create user with username, displayname, email, and password |
446
+ | PUT | `/api/users/:username` | Update user password or display name |
447
+ | DELETE | `/api/users/:username` | Delete user (not the last one) |
448
+ | POST | `/api/users/:username/reset-totp` | Generate new TOTP secret |
449
+
450
+ ### Password hashing
451
+
452
+ | Property | Value |
453
+ | ---------------- | --------------------------------------- |
454
+ | Algorithm | bcrypt |
455
+ | Cost factor | 12 |
456
+ | Memory per hash | ~4KB |
457
+ | Why not argon2id | ~93MB per hash, causes OOM on 512MB VPS |
458
+
459
+ ### Related documentation
460
+
461
+ - [mTLS](mtls.md) — client certificate authentication for the admin panel
462
+ - [nginx Reverse Proxy](nginx-reverse-proxy.md) — forward-auth configuration details
463
+ - [Security Model](security-model.md) — authentication as part of defense-in-depth
464
+ - [Tunneling](tunneling.md) — how tunneled apps reach the internet