@lamalibre/create-lamaste 4.6.0 → 4.7.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 (149) hide show
  1. package/package.json +2 -2
  2. package/scripts/bundle-vendor.js +20 -3
  3. package/scripts/lamaste-priv +22 -3
  4. package/scripts/pin-release.js +98 -0
  5. package/src/index.js +8 -1
  6. package/src/lib/env.js +19 -0
  7. package/src/lib/service-config.js +7 -0
  8. package/src/tasks/redeploy.js +8 -0
  9. package/vendor/docs/00-introduction/how-it-works.md +2 -2
  10. package/vendor/docs/00-introduction/quickstart.md +5 -5
  11. package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
  12. package/vendor/docs/01-concepts/authentication.md +6 -2
  13. package/vendor/docs/01-concepts/authorization.md +4 -0
  14. package/vendor/docs/01-concepts/certificates.md +3 -3
  15. package/vendor/docs/01-concepts/dns-and-domains.md +9 -0
  16. package/vendor/docs/01-concepts/mtls.md +2 -2
  17. package/vendor/docs/01-concepts/security-model.md +28 -3
  18. package/vendor/docs/01-concepts/tickets.md +2 -2
  19. package/vendor/docs/01-concepts/tunneling.md +4 -4
  20. package/vendor/docs/02-guides/agent-setup.md +5 -5
  21. package/vendor/docs/02-guides/certificate-management.md +2 -2
  22. package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
  23. package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
  24. package/vendor/docs/02-guides/installation.md +3 -3
  25. package/vendor/docs/02-guides/managing-users.md +5 -11
  26. package/vendor/docs/02-guides/onboarding.md +2 -2
  27. package/vendor/docs/02-guides/second-domain.md +82 -0
  28. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
  29. package/vendor/docs/03-architecture/installer-flow.md +2 -2
  30. package/vendor/docs/03-architecture/installer.md +4 -4
  31. package/vendor/docs/03-architecture/management-flow.md +1 -1
  32. package/vendor/docs/03-architecture/nginx-configuration.md +15 -5
  33. package/vendor/docs/03-architecture/overview.md +5 -5
  34. package/vendor/docs/03-architecture/panel-client.md +6 -6
  35. package/vendor/docs/03-architecture/panel-server.md +2 -2
  36. package/vendor/docs/03-architecture/state-management.md +3 -3
  37. package/vendor/docs/03-architecture/system-overview.md +1 -1
  38. package/vendor/docs/04-api-reference/agent-hosted-plugins.md +17 -13
  39. package/vendor/docs/04-api-reference/app-host.md +3 -3
  40. package/vendor/docs/04-api-reference/domains.md +192 -0
  41. package/vendor/docs/04-api-reference/gatekeeper.md +35 -7
  42. package/vendor/docs/04-api-reference/lifecycle-hooks.md +4 -4
  43. package/vendor/docs/04-api-reference/onboarding.md +1 -1
  44. package/vendor/docs/04-api-reference/overview.md +9 -0
  45. package/vendor/docs/04-api-reference/registry-and-oidc.md +26 -13
  46. package/vendor/docs/04-api-reference/sites.md +5 -0
  47. package/vendor/docs/04-api-reference/tunnels.md +15 -10
  48. package/vendor/docs/04-api-reference/users.md +41 -29
  49. package/vendor/docs/05-operations/backup-and-restore.md +1 -1
  50. package/vendor/docs/05-operations/uninstalling.md +1 -1
  51. package/vendor/docs/05-operations/upgrades.md +6 -6
  52. package/vendor/docs/06-reference/config-files.md +1 -1
  53. package/vendor/docs/06-reference/glossary.md +3 -3
  54. package/vendor/docs/06-reference/installer-flags.md +13 -13
  55. package/vendor/docs/06-reference/ports-and-services.md +1 -1
  56. package/vendor/docs/06-reference/troubleshooting.md +8 -8
  57. package/vendor/docs/_index.json +105 -19
  58. package/vendor/docs/external-members-design.md +816 -0
  59. package/vendor/docs/gatekeeper-design.md +8 -8
  60. package/vendor/docs/identity-directory-design.md +3 -3
  61. package/vendor/docs/multi-domain-relay-design.md +836 -0
  62. package/vendor/docs/package.json +1 -1
  63. package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
  64. package/vendor/gatekeeper/dist/cli/index.js +0 -0
  65. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
  66. package/vendor/gatekeeper/dist/lib/grants.js +20 -13
  67. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
  68. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0004_unconsume_access_grants.sql +8 -0
  69. package/vendor/gatekeeper/dist/lib/secret.d.ts +6 -0
  70. package/vendor/gatekeeper/dist/lib/secret.d.ts.map +1 -0
  71. package/vendor/gatekeeper/dist/lib/secret.js +12 -0
  72. package/vendor/gatekeeper/dist/lib/secret.js.map +1 -0
  73. package/vendor/gatekeeper/dist/lib/templates.d.ts +20 -0
  74. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -1
  75. package/vendor/gatekeeper/dist/lib/templates.js +80 -14
  76. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -1
  77. package/vendor/gatekeeper/dist/server/index.d.ts +5 -1
  78. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
  79. package/vendor/gatekeeper/dist/server/index.js +28 -2
  80. package/vendor/gatekeeper/dist/server/index.js.map +1 -1
  81. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
  82. package/vendor/gatekeeper/dist/server/routes/authz.js +225 -36
  83. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
  84. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
  85. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +5 -2
  86. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
  87. package/vendor/gatekeeper/package.json +2 -2
  88. package/vendor/server/bin/lamaste-server.js +7 -0
  89. package/vendor/server/package.json +2 -2
  90. package/vendor/server/src/commands/domains.js +214 -0
  91. package/vendor/server/src/commands/plugin-identity.js +2 -16
  92. package/vendor/server/src/commands/plugins.js +112 -8
  93. package/vendor/server/src/commands/registry-credential.js +10 -3
  94. package/vendor/server/src/commands/sites.js +3 -0
  95. package/vendor/server/src/commands/status.js +19 -0
  96. package/vendor/server/src/commands/tunnels.js +4 -1
  97. package/vendor/server/src/commands/users.js +8 -19
  98. package/vendor/server/src/domains.js +92 -0
  99. package/vendor/server/src/fail.js +21 -0
  100. package/vendor/server/src/oidc-client-grants.js +63 -0
  101. package/vendor/server/src/panel-api.js +15 -0
  102. package/vendor/server-ui/dist/assets/index-B8Tv_ocF.js +84 -0
  103. package/vendor/server-ui/dist/assets/{index-BZwQ_aQc.css → index-Cb-ADsbB.css} +1 -1
  104. package/vendor/server-ui/dist/index.html +2 -2
  105. package/vendor/serverd/package.json +2 -2
  106. package/vendor/serverd/src/index.js +42 -16
  107. package/vendor/serverd/src/lib/app-error.js +24 -0
  108. package/vendor/serverd/src/lib/audience.js +12 -3
  109. package/vendor/serverd/src/lib/authelia.js +341 -35
  110. package/vendor/serverd/src/lib/directory.js +67 -52
  111. package/vendor/serverd/src/lib/domains.js +1792 -0
  112. package/vendor/serverd/src/lib/gatekeeper-client.js +20 -4
  113. package/vendor/serverd/src/lib/identity-sync.js +35 -0
  114. package/vendor/serverd/src/lib/migrations/state/0016_domains.sql +53 -0
  115. package/vendor/serverd/src/lib/migrations/state/0017_membership_kind.sql +15 -0
  116. package/vendor/serverd/src/lib/migrations/state/0018_domain_group_keys.sql +23 -0
  117. package/vendor/serverd/src/lib/nginx.js +318 -140
  118. package/vendor/serverd/src/lib/owned-groups.js +326 -0
  119. package/vendor/serverd/src/lib/plugin-domain.js +86 -0
  120. package/vendor/serverd/src/lib/plugin-lifecycle.js +1 -0
  121. package/vendor/serverd/src/lib/plugin-oidc.js +144 -42
  122. package/vendor/serverd/src/lib/plugins.js +45 -5
  123. package/vendor/serverd/src/lib/registry-credential.js +39 -6
  124. package/vendor/serverd/src/lib/services.js +9 -1
  125. package/vendor/serverd/src/lib/state.js +11 -0
  126. package/vendor/serverd/src/lib/tunnel-deps.js +2 -4
  127. package/vendor/serverd/src/lib/user-surface.js +29 -4
  128. package/vendor/serverd/src/lib/users-lock.js +23 -0
  129. package/vendor/serverd/src/lib/vhost-reconcile.js +149 -41
  130. package/vendor/serverd/src/lib/web-surface.js +49 -32
  131. package/vendor/serverd/src/middleware/user-access-session.js +11 -4
  132. package/vendor/serverd/src/routes/invite.js +15 -5
  133. package/vendor/serverd/src/routes/management/domains.js +154 -0
  134. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +44 -8
  135. package/vendor/serverd/src/routes/management/identity.js +21 -7
  136. package/vendor/serverd/src/routes/management/invitations.js +33 -4
  137. package/vendor/serverd/src/routes/management/plugins.js +4 -2
  138. package/vendor/serverd/src/routes/management/registry-credential.js +86 -57
  139. package/vendor/serverd/src/routes/management/sites.js +50 -11
  140. package/vendor/serverd/src/routes/management/tunnels.js +29 -5
  141. package/vendor/serverd/src/routes/management/users.js +184 -64
  142. package/vendor/serverd/src/routes/management.js +2 -0
  143. package/vendor/serverd/src/routes/onboarding/dns.js +3 -17
  144. package/vendor/serverd/src/routes/onboarding/provision.js +22 -13
  145. package/vendor/serverd/src/routes/user-access.js +13 -1
  146. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +0 -7
  147. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +0 -14
  148. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +0 -9
  149. package/vendor/server-ui/dist/assets/index-BvEfCZYS.js +0 -84
@@ -231,17 +231,33 @@ function groupOf(raw) {
231
231
  * @param {string} resourceId
232
232
  * @returns {Promise<{ ok: true, grants: GatekeeperGrant[] } | Extract<GatekeeperResult, { ok: false }>>}
233
233
  */
234
- export async function listResourceGrants(resourceType, resourceId) {
235
- const qs = new URLSearchParams({ resourceType, resourceId }).toString();
234
+ export function listResourceGrants(resourceType, resourceId) {
235
+ return listGrantsMatching({ resourceType, resourceId });
236
+ }
237
+
238
+ /**
239
+ * Every grant on resources of `resourceType` (`tunnel`, `site`,
240
+ * `oidc-client`, …).
241
+ *
242
+ * @param {string} resourceType
243
+ * @returns {Promise<{ ok: true, grants: GatekeeperGrant[] } | Extract<GatekeeperResult, { ok: false }>>}
244
+ */
245
+ export function listGrantsOfType(resourceType) {
246
+ return listGrantsMatching({ resourceType });
247
+ }
248
+
249
+ /** The grants matching `filter` (its fields are Gatekeeper's query filters). */
250
+ async function listGrantsMatching(filter) {
251
+ const qs = new URLSearchParams(filter).toString();
236
252
  const result = await gatekeeperRequest('GET', `/api/grants?${qs}`);
237
253
  if (!result.ok) return result;
238
254
  if (!isRecord(result.data) || !Array.isArray(result.data.grants)) return malformed('its grants');
239
255
  const grants = result.data.grants.map(grantOf);
240
256
  if (grants.some((g) => g === null)) return malformed('its grants');
241
- // Gatekeeper filters by the query; a grant on anything else is not this resource's.
257
+ // Gatekeeper filters by the query; a grant on anything else is not one asked for.
242
258
  return {
243
259
  ok: true,
244
- grants: grants.filter((g) => g.resourceType === resourceType && g.resourceId === resourceId),
260
+ grants: grants.filter((g) => Object.entries(filter).every(([key, value]) => g[key] === value)),
245
261
  };
246
262
  }
247
263
 
@@ -77,6 +77,7 @@ import {
77
77
  } from './audience.js';
78
78
  import { getConfig } from './config.js';
79
79
  import { clearPendingPrincipals, getDirectoryId, reconcileDirectory } from './directory.js';
80
+ import { ensurePrimaryDomain, syncDomainAccess } from './domains.js';
80
81
  import { installedVersionApproved } from './plugin-hooks.js';
81
82
  import { findPlugin, findPluginByRoute, readPlugins } from './plugins.js';
82
83
  import { retainedIdentityPositions } from './retained-data.js';
@@ -859,6 +860,9 @@ async function syncDuePlugins(logger) {
859
860
  let tickTimer = null;
860
861
  let ticking = false;
861
862
 
863
+ /** The OIDC sync failure the tick last logged — logged again only when it changes. */
864
+ let lastOidcSyncError = null;
865
+
862
866
  /**
863
867
  * One tick: reconcile the directory with users.yml (once onboarding is
864
868
  * done), clear the Gatekeeper principals of removed users still pending,
@@ -875,6 +879,37 @@ export async function identityTick(logger) {
875
879
  } catch (err) {
876
880
  logger?.warn({ err: err.message }, 'Could not reconcile the user directory');
877
881
  }
882
+ // Who may reach each domain, as the directory now says: users adopted
883
+ // or removed outside Lamaste, and a rendering a failure left behind.
884
+ try {
885
+ await ensurePrimaryDomain({ logger });
886
+ await syncDomainAccess({ logger });
887
+ } catch (err) {
888
+ logger?.warn({ err: err.message }, 'Could not render domain access');
889
+ }
890
+ // users.yml touched once a minute: a reload Authelia skipped (its
891
+ // cooldown) or that a restart of the panel cut short is never missing
892
+ // for longer, and a hand edit gets Lamaste's groups back.
893
+ try {
894
+ const { touchUsersFile } = await import('./authelia.js');
895
+ await touchUsersFile();
896
+ } catch (err) {
897
+ logger?.warn({ err: err.message }, 'Could not touch users.yml');
898
+ }
899
+ // The OIDC provider too, as the plugins and the members now say: a
900
+ // plugin moved to another domain whose sync failed is healed here.
901
+ // Compare-first, so a steady relay writes nothing.
902
+ try {
903
+ const { syncOidcClients } = await import('./plugin-oidc.js');
904
+ await syncOidcClients(logger);
905
+ lastOidcSyncError = null;
906
+ } catch (err) {
907
+ // Once per distinct failure, not every minute.
908
+ if (err.message !== lastOidcSyncError) {
909
+ logger?.warn({ err: err.message }, 'Could not sync the OIDC clients');
910
+ }
911
+ lastOidcSyncError = err.message;
912
+ }
878
913
  }
879
914
  await clearPendingPrincipals({ logger });
880
915
  await recomputeAudiences({ logger });
@@ -0,0 +1,53 @@
1
+ -- The relay's base domains and who is a member of each (the multi-domain
2
+ -- relay, packages/core/docs/multi-domain-relay-design.md; lib/domains.js).
3
+ --
4
+ -- One domain is the primary: the panel's (panel.json `domain`), inserted at
5
+ -- the first start after onboarding together with every user the directory
6
+ -- holds as its members (lib/domains.js `ensurePrimaryDomain`) — an upgraded
7
+ -- single-domain relay keeps letting everyone in.
8
+
9
+ -- A base domain:
10
+ -- name lower case, two labels at least; domains never overlap
11
+ -- is_primary 1 for exactly one row: the panel's domain
12
+ -- status pending (added, auth.<domain> not yet issued) | active
13
+ -- (its sign-in, session cookie and members rendered)
14
+ CREATE TABLE domains (
15
+ name TEXT PRIMARY KEY,
16
+ is_primary INTEGER NOT NULL DEFAULT 0 CHECK (is_primary IN (0, 1)),
17
+ status TEXT NOT NULL CHECK (status IN ('pending', 'active')),
18
+ created_at TEXT NOT NULL,
19
+ activated_at TEXT,
20
+ CHECK (is_primary = 0 OR status = 'active'),
21
+ CHECK ((status = 'active') = (activated_at IS NOT NULL))
22
+ );
23
+ CREATE UNIQUE INDEX domains_one_primary ON domains (is_primary) WHERE is_primary = 1;
24
+
25
+ -- Who may reach a domain: one row per domain and user, by user id (a
26
+ -- re-created username is a new user with no membership). Rows go when the
27
+ -- user is deleted or the domain removed; a disabled user keeps theirs but
28
+ -- is never rendered as a member. Changes take the directory's store-wide
29
+ -- sequence, as plugin audiences do.
30
+ CREATE TABLE domain_members (
31
+ domain TEXT NOT NULL REFERENCES domains (name),
32
+ user_id TEXT NOT NULL REFERENCES directory_users (user_id),
33
+ added_at TEXT NOT NULL,
34
+ added_by TEXT NOT NULL,
35
+ seq INTEGER NOT NULL DEFAULT 0,
36
+ PRIMARY KEY (domain, user_id)
37
+ );
38
+ CREATE INDEX domain_members_user ON domain_members (user_id);
39
+
40
+ CREATE TRIGGER domain_members_stamp_insert AFTER INSERT ON domain_members
41
+ BEGIN
42
+ UPDATE directory_counter SET seq = seq + 1 WHERE id = 1;
43
+ UPDATE domain_members SET seq = (SELECT seq FROM directory_counter WHERE id = 1)
44
+ WHERE domain = NEW.domain AND user_id = NEW.user_id;
45
+ END;
46
+ CREATE TRIGGER domain_members_stamp_delete AFTER DELETE ON domain_members
47
+ BEGIN
48
+ UPDATE directory_counter SET seq = seq + 1 WHERE id = 1;
49
+ END;
50
+
51
+ -- The domain a plugin belongs to — its OIDC clients' `{domain}` and sign-in,
52
+ -- its user and web surfaces, its audience. NULL: the primary domain.
53
+ ALTER TABLE plugins ADD COLUMN domain TEXT;
@@ -0,0 +1,15 @@
1
+ -- The kind of a domain membership (packages/core/docs/external-members-design.md;
2
+ -- lib/domains.js): a `member` reaches what the domain gives its members, an
3
+ -- `external` member only what names them (a grant, a site's listed users, an
4
+ -- OIDC client granted to them). Every membership from before is a member.
5
+ ALTER TABLE domain_members ADD COLUMN kind TEXT NOT NULL DEFAULT 'member'
6
+ CHECK (kind IN ('member', 'external'));
7
+
8
+ -- A change of kind takes the directory's store-wide sequence, as an insert does.
9
+ CREATE TRIGGER domain_members_stamp_kind AFTER UPDATE OF kind ON domain_members
10
+ WHEN OLD.kind IS NOT NEW.kind
11
+ BEGIN
12
+ UPDATE directory_counter SET seq = seq + 1 WHERE id = 1;
13
+ UPDATE domain_members SET seq = (SELECT seq FROM directory_counter WHERE id = 1)
14
+ WHERE domain = NEW.domain AND user_id = NEW.user_id;
15
+ END;
@@ -0,0 +1,23 @@
1
+ -- Opaque names for the Authelia groups Lamaste owns
2
+ -- (packages/core/docs/external-members-design.md, security review): a
3
+ -- domain's members and external members are `lamaste:m:<key>` and
4
+ -- `lamaste:x:<key>`, with a random key per domain, so an OIDC relying party
5
+ -- that asks for the `groups` claim learns no domain's name. The panel gives
6
+ -- a new domain its key when it inserts it; the trigger is the fallback, and
7
+ -- existing domains get one here.
8
+ ALTER TABLE domains ADD COLUMN group_key TEXT;
9
+ UPDATE domains SET group_key = lower(hex(randomblob(8))) WHERE group_key IS NULL;
10
+ CREATE UNIQUE INDEX domains_group_key ON domains(group_key);
11
+ CREATE TRIGGER domains_group_key_fill AFTER INSERT ON domains
12
+ WHEN NEW.group_key IS NULL
13
+ BEGIN
14
+ UPDATE domains SET group_key = lower(hex(randomblob(8))) WHERE name = NEW.name;
15
+ END;
16
+
17
+ -- Domains removed from the relay: hostnames under them are still Lamaste's
18
+ -- groups (a host group left on a user is stripped at the next rendering,
19
+ -- never taken for an administrator's group).
20
+ CREATE TABLE retired_domains (
21
+ name TEXT PRIMARY KEY,
22
+ removed_at TEXT NOT NULL
23
+ );