@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.
- package/package.json +2 -2
- package/scripts/bundle-vendor.js +20 -3
- package/scripts/lamaste-priv +22 -3
- package/scripts/pin-release.js +98 -0
- package/src/index.js +8 -1
- package/src/lib/env.js +19 -0
- package/src/lib/service-config.js +7 -0
- package/src/tasks/redeploy.js +8 -0
- package/vendor/docs/00-introduction/how-it-works.md +2 -2
- package/vendor/docs/00-introduction/quickstart.md +5 -5
- package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
- package/vendor/docs/01-concepts/authentication.md +6 -2
- package/vendor/docs/01-concepts/authorization.md +4 -0
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/dns-and-domains.md +9 -0
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/security-model.md +28 -3
- package/vendor/docs/01-concepts/tickets.md +2 -2
- package/vendor/docs/01-concepts/tunneling.md +4 -4
- package/vendor/docs/02-guides/agent-setup.md +5 -5
- package/vendor/docs/02-guides/certificate-management.md +2 -2
- package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
- package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
- package/vendor/docs/02-guides/installation.md +3 -3
- package/vendor/docs/02-guides/managing-users.md +5 -11
- package/vendor/docs/02-guides/onboarding.md +2 -2
- package/vendor/docs/02-guides/second-domain.md +82 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
- package/vendor/docs/03-architecture/installer-flow.md +2 -2
- package/vendor/docs/03-architecture/installer.md +4 -4
- package/vendor/docs/03-architecture/management-flow.md +1 -1
- package/vendor/docs/03-architecture/nginx-configuration.md +15 -5
- package/vendor/docs/03-architecture/overview.md +5 -5
- package/vendor/docs/03-architecture/panel-client.md +6 -6
- package/vendor/docs/03-architecture/panel-server.md +2 -2
- package/vendor/docs/03-architecture/state-management.md +3 -3
- package/vendor/docs/03-architecture/system-overview.md +1 -1
- package/vendor/docs/04-api-reference/agent-hosted-plugins.md +17 -13
- package/vendor/docs/04-api-reference/app-host.md +3 -3
- package/vendor/docs/04-api-reference/domains.md +192 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +35 -7
- package/vendor/docs/04-api-reference/lifecycle-hooks.md +4 -4
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/overview.md +9 -0
- package/vendor/docs/04-api-reference/registry-and-oidc.md +26 -13
- package/vendor/docs/04-api-reference/sites.md +5 -0
- package/vendor/docs/04-api-reference/tunnels.md +15 -10
- package/vendor/docs/04-api-reference/users.md +41 -29
- package/vendor/docs/05-operations/backup-and-restore.md +1 -1
- package/vendor/docs/05-operations/uninstalling.md +1 -1
- package/vendor/docs/05-operations/upgrades.md +6 -6
- package/vendor/docs/06-reference/config-files.md +1 -1
- package/vendor/docs/06-reference/glossary.md +3 -3
- package/vendor/docs/06-reference/installer-flags.md +13 -13
- package/vendor/docs/06-reference/ports-and-services.md +1 -1
- package/vendor/docs/06-reference/troubleshooting.md +8 -8
- package/vendor/docs/_index.json +105 -19
- package/vendor/docs/external-members-design.md +816 -0
- package/vendor/docs/gatekeeper-design.md +8 -8
- package/vendor/docs/identity-directory-design.md +3 -3
- package/vendor/docs/multi-domain-relay-design.md +836 -0
- package/vendor/docs/package.json +1 -1
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
- package/vendor/gatekeeper/dist/cli/index.js +0 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/lib/grants.js +20 -13
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0004_unconsume_access_grants.sql +8 -0
- package/vendor/gatekeeper/dist/lib/secret.d.ts +6 -0
- package/vendor/gatekeeper/dist/lib/secret.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/secret.js +12 -0
- package/vendor/gatekeeper/dist/lib/secret.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +20 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/lib/templates.js +80 -14
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -1
- package/vendor/gatekeeper/dist/server/index.d.ts +5 -1
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.js +28 -2
- package/vendor/gatekeeper/dist/server/index.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.js +225 -36
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +5 -2
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
- package/vendor/gatekeeper/package.json +2 -2
- package/vendor/server/bin/lamaste-server.js +7 -0
- package/vendor/server/package.json +2 -2
- package/vendor/server/src/commands/domains.js +214 -0
- package/vendor/server/src/commands/plugin-identity.js +2 -16
- package/vendor/server/src/commands/plugins.js +112 -8
- package/vendor/server/src/commands/registry-credential.js +10 -3
- package/vendor/server/src/commands/sites.js +3 -0
- package/vendor/server/src/commands/status.js +19 -0
- package/vendor/server/src/commands/tunnels.js +4 -1
- package/vendor/server/src/commands/users.js +8 -19
- package/vendor/server/src/domains.js +92 -0
- package/vendor/server/src/fail.js +21 -0
- package/vendor/server/src/oidc-client-grants.js +63 -0
- package/vendor/server/src/panel-api.js +15 -0
- package/vendor/server-ui/dist/assets/index-B8Tv_ocF.js +84 -0
- package/vendor/server-ui/dist/assets/{index-BZwQ_aQc.css → index-Cb-ADsbB.css} +1 -1
- package/vendor/server-ui/dist/index.html +2 -2
- package/vendor/serverd/package.json +2 -2
- package/vendor/serverd/src/index.js +42 -16
- package/vendor/serverd/src/lib/app-error.js +24 -0
- package/vendor/serverd/src/lib/audience.js +12 -3
- package/vendor/serverd/src/lib/authelia.js +341 -35
- package/vendor/serverd/src/lib/directory.js +67 -52
- package/vendor/serverd/src/lib/domains.js +1792 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +20 -4
- package/vendor/serverd/src/lib/identity-sync.js +35 -0
- package/vendor/serverd/src/lib/migrations/state/0016_domains.sql +53 -0
- package/vendor/serverd/src/lib/migrations/state/0017_membership_kind.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0018_domain_group_keys.sql +23 -0
- package/vendor/serverd/src/lib/nginx.js +318 -140
- package/vendor/serverd/src/lib/owned-groups.js +326 -0
- package/vendor/serverd/src/lib/plugin-domain.js +86 -0
- package/vendor/serverd/src/lib/plugin-lifecycle.js +1 -0
- package/vendor/serverd/src/lib/plugin-oidc.js +144 -42
- package/vendor/serverd/src/lib/plugins.js +45 -5
- package/vendor/serverd/src/lib/registry-credential.js +39 -6
- package/vendor/serverd/src/lib/services.js +9 -1
- package/vendor/serverd/src/lib/state.js +11 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +2 -4
- package/vendor/serverd/src/lib/user-surface.js +29 -4
- package/vendor/serverd/src/lib/users-lock.js +23 -0
- package/vendor/serverd/src/lib/vhost-reconcile.js +149 -41
- package/vendor/serverd/src/lib/web-surface.js +49 -32
- package/vendor/serverd/src/middleware/user-access-session.js +11 -4
- package/vendor/serverd/src/routes/invite.js +15 -5
- package/vendor/serverd/src/routes/management/domains.js +154 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +44 -8
- package/vendor/serverd/src/routes/management/identity.js +21 -7
- package/vendor/serverd/src/routes/management/invitations.js +33 -4
- package/vendor/serverd/src/routes/management/plugins.js +4 -2
- package/vendor/serverd/src/routes/management/registry-credential.js +86 -57
- package/vendor/serverd/src/routes/management/sites.js +50 -11
- package/vendor/serverd/src/routes/management/tunnels.js +29 -5
- package/vendor/serverd/src/routes/management/users.js +184 -64
- package/vendor/serverd/src/routes/management.js +2 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +3 -17
- package/vendor/serverd/src/routes/onboarding/provision.js +22 -13
- package/vendor/serverd/src/routes/user-access.js +13 -1
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +0 -7
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +0 -14
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +0 -9
- package/vendor/server-ui/dist/assets/index-BvEfCZYS.js +0 -84
|
@@ -319,6 +319,15 @@ There are two exceptions:
|
|
|
319
319
|
| PUT | `/api/users/:username` | Management | Update user |
|
|
320
320
|
| DELETE | `/api/users/:username` | Management | Delete user |
|
|
321
321
|
| POST | `/api/users/:username/reset-totp` | Management | Reset TOTP secret |
|
|
322
|
+
| GET | `/api/domains` | Management | List domains |
|
|
323
|
+
| POST | `/api/domains` | Management | Add a domain |
|
|
324
|
+
| POST | `/api/domains/:name/verify-dns` | Management | Check a domain's DNS |
|
|
325
|
+
| POST | `/api/domains/:name/activate` | Management | Activate a domain |
|
|
326
|
+
| DELETE | `/api/domains/:name` | Management | Remove a domain |
|
|
327
|
+
| GET | `/api/domains/:name/members` | Management | List a domain's members |
|
|
328
|
+
| PUT | `/api/domains/:name/members/:user` | Management | Add a member |
|
|
329
|
+
| DELETE | `/api/domains/:name/members/:user` | Management | End a membership |
|
|
330
|
+
| PUT | `/api/plugins/:name/domain` | Management | Move a plugin to a domain |
|
|
322
331
|
| GET | `/api/certs` | Management | List certificates |
|
|
323
332
|
| GET | `/api/certs/auto-renew-status` | Management | Auto-renew timer status |
|
|
324
333
|
| POST | `/api/certs/:domain/renew` | Management | Force-renew certificate |
|
|
@@ -37,19 +37,21 @@ Plugins use the same Authelia to let people and programs log in. A plugin's mani
|
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
| Field | Default | Meaning
|
|
41
|
-
| ------------------------------ | ----------------- |
|
|
42
|
-
| `id` | — | Client id. Must be the plugin's route (`feria` for `@lamalibre/feria-server`) or `<route>-<suffix>`; one plugin cannot claim another's client
|
|
43
|
-
| `name` | `<plugin> (<id>)` | Shown on Authelia's consent screen
|
|
44
|
-
| `grantTypes` | — | Any of `urn:ietf:params:oauth:grant-type:device_code`, `authorization_code`, `
|
|
45
|
-
| `scopes` | `["openid"]` | Scopes the client may request (lowercase letters and underscores)
|
|
46
|
-
| `audience` | `[]` | Audiences the client may request; `{domain}` becomes the relay domain. Clients must request an audience explicitly (`requested_audience_mode: explicit`)
|
|
47
|
-
| `redirectUris` | `[]` | For `authorization_code`; `{domain}` is expanded
|
|
48
|
-
| `accessTokenSignedResponseAlg` | `RS256` | `RS256` makes access tokens JWTs (RFC 9068) a resource server verifies against Authelia's JWKS; `none` keeps them opaque
|
|
49
|
-
| `accessTokenClaims` | `[]` | Claims copied into the JWT access token through a per-client claims policy: `groups` (needs the `groups` scope), `preferred_username` and `name` (`profile`), `email` (`email`). Without a policy Authelia puts these claims only in the UserInfo response
|
|
50
|
-
| `authorizationPolicy` | `two_factor` | `one_factor` or `two_factor` for the user-facing grants. A client with the `client_credentials` grant has no end user: Authelia requires `one_factor` there, so it defaults to `one_factor` and `two_factor` is rejected
|
|
51
|
-
| `requirePkce` | `false` | Require PKCE on authorization-code requests
|
|
52
|
-
| `confidential` | `false` | A confidential client authenticates to the token endpoint with a secret (`client_secret_basic`). Lamaste generates the secret
|
|
40
|
+
| Field | Default | Meaning |
|
|
41
|
+
| ------------------------------ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
42
|
+
| `id` | — | Client id. Must be the plugin's route (`feria` for `@lamalibre/feria-server`) or `<route>-<suffix>`; one plugin cannot claim another's client |
|
|
43
|
+
| `name` | `<plugin> (<id>)` | Shown on Authelia's consent screen |
|
|
44
|
+
| `grantTypes` | — | Any of `urn:ietf:params:oauth:grant-type:device_code`, `authorization_code`, `client_credentials` — never `refresh_token` (below). `client_credentials` requires `confidential: true`; `authorization_code` requires `redirectUris`. A client whose only grant is `client_credentials` may not have `openid` or `offline_access` |
|
|
45
|
+
| `scopes` | `["openid"]` | Scopes the client may request (lowercase letters and underscores) |
|
|
46
|
+
| `audience` | `[]` | Audiences the client may request; `{domain}` becomes the relay domain. Clients must request an audience explicitly (`requested_audience_mode: explicit`) |
|
|
47
|
+
| `redirectUris` | `[]` | For `authorization_code`; `{domain}` is expanded |
|
|
48
|
+
| `accessTokenSignedResponseAlg` | `RS256` | `RS256` makes access tokens JWTs (RFC 9068) a resource server verifies against Authelia's JWKS; `none` keeps them opaque |
|
|
49
|
+
| `accessTokenClaims` | `[]` | Claims copied into the JWT access token through a per-client claims policy: `groups` (needs the `groups` scope), `preferred_username` and `name` (`profile`), `email` (`email`). Without a policy Authelia puts these claims only in the UserInfo response |
|
|
50
|
+
| `authorizationPolicy` | `two_factor` | `one_factor` or `two_factor` for the user-facing grants. A client with the `client_credentials` grant has no end user: Authelia requires `one_factor` there, so it defaults to `one_factor` and `two_factor` is rejected |
|
|
51
|
+
| `requirePkce` | `false` | Require PKCE on authorization-code requests |
|
|
52
|
+
| `confidential` | `false` | A confidential client authenticates to the token endpoint with a secret (`client_secret_basic`). Lamaste generates the secret |
|
|
53
|
+
|
|
54
|
+
**No refresh tokens.** A manifest declaring the `refresh_token` grant, or the `offline` / `offline_access` scope, is refused at install and update: Authelia checks membership of the client's domain only when a user authorizes a client, so a removed member could keep refreshing. A plugin installed before is served without them (`validateInstalledManifest`).
|
|
53
55
|
|
|
54
56
|
What the panel writes for a public client: `public: true`, `token_endpoint_auth_method: none`, `consent_mode: explicit`. For a confidential client: `client_secret` as a pbkdf2-sha512 digest (`$pbkdf2-sha512$310000$<salt>$<key>`), `token_endpoint_auth_method: client_secret_basic`, and no consent mode when the only grant is `client_credentials`. Any client with the `client_credentials` grant is rendered with `authorization_policy: one_factor` and may not carry the `openid`, `offline` or `offline_access` scopes — Authelia 4.39 refuses the configuration otherwise (`validator/identity_providers.go`). A client with `accessTokenClaims` gets `claims_policy: <id>` and a policy of the same name under `identity_providers.oidc.claims_policies` with `access_token: [...]`.
|
|
55
57
|
|
|
@@ -162,6 +164,7 @@ Admin only, 10 per minute. Verifies the whole chain against the registry, then s
|
|
|
162
164
|
|
|
163
165
|
```json
|
|
164
166
|
{ "registryUrl": "https://feria.example.com", "auth": { "kind": "oidc" } }
|
|
167
|
+
{ "registryUrl": "https://feria.codelama.com.tr", "auth": { "kind": "oidc", "issuerDomain": "codelama.com.tr" } }
|
|
165
168
|
{ "registryUrl": "https://feria.example.com", "auth": { "kind": "token", "token": "feria_…" }, "scope": "@lamalibre" }
|
|
166
169
|
```
|
|
167
170
|
|
|
@@ -206,6 +209,16 @@ CLI: `lamaste-server registry-credential show | set --url <registry> (--oidc | -
|
|
|
206
209
|
|
|
207
210
|
When a user loses a `feria-*` group, or is deleted, the panel asks the registry to revoke that user's tokens (`DELETE /-/feria/tokens?owner=user:<name>`) with the same credential. This is best effort — the user operation reports `warnings` rather than failing. The identity change feed planned for the identity directory (see [Identity directory design](../identity-directory-design.md)) will supersede this hook: plugins will subscribe to membership changes instead of the panel calling each registry.
|
|
208
211
|
|
|
212
|
+
## Several domains: one issuer each
|
|
213
|
+
|
|
214
|
+
Authelia takes the issuer from the request's host, so every active domain is an issuer of its own: `https://auth.<domain>`. Every client is known to every issuer — Authelia has no per-host client list — but a token carries the issuer it was obtained at, and a relying party that trusts one issuer (Feria) refuses the others.
|
|
215
|
+
|
|
216
|
+
- **A plugin's clients belong to its domain** ([`PUT /api/plugins/:name/domain`](domains.md#put-apipluginsnamedomain); the primary until moved): `{domain}` in `audience` and `redirectUris` expands to it, and each user-facing client has an authorization policy of its own, `lamaste_client_<id>` under `identity_providers.oidc.authorization_policies` (`default_policy: deny`): the domain's members (group `lamaste:m:<key>`, the domain's random group key) with the client's factor, and its external members only with a grant on the client (groups `lamaste:x:<key>` and `lamaste:oidc@<id>`). Authelia reads a user's groups afresh on the authorization-code path but not on the device-code path, so a change that takes a device-code client away from someone restarts Authelia. Uninstalling a plugin revokes the grants on its clients. A user's registry tokens are revoked when none of the registry's own sign-in clients (user-facing clients whose audience is the registry) admits them any more. Authelia's `access_control` does not apply to OIDC authorization; these policies are how `feria login` admits only members — and the external members granted it. A pure `client_credentials` client (no user) stays `one_factor`.
|
|
217
|
+
- **Granting a client to external members**: a Gatekeeper grant with `resourceType: "oidc-client"` and the client id as `resourceId`, to a user or a Lamaste group (`POST /api/gatekeeper/grants`; the panel refuses a client no installed plugin declares, `unknown_oidc_client`, and a service client, `service_oidc_client`). `GET /api/plugins/:name/oidc-clients` lists each user-facing client's `grants` (`userFacing: true`; `grants: null` when Gatekeeper could not be read); the plugin card grants and revokes; CLI `lamaste-server plugins oidc-clients <name> grant|revoke <clientId> --user <u>|--group <g>`. A grant or a revocation is a `users.yml` change: it applies at the user's next authorization, without restarting Authelia. Members need no grant.
|
|
218
|
+
- **Token lifetime.** Access and ID tokens of user-facing clients live 15 minutes (a custom lifespan the clients name): what an authorization already issued survives a revocation at most that long. Registry tokens Feria minted from a login are revoked by the panel when the user no longer reaches the registry's sign-in.
|
|
219
|
+
- **The registry credential names its issuer**: `auth.issuerDomain` (an active domain; absent = the primary) is the domain whose issuer the panel's tokens carry (`X-Forwarded-Host: auth.<issuerDomain>`); the owner Feria records is then `ci:auth.<issuerDomain>/lamaste-server`. CLI: `lamaste-server registry-credential set --url <registry> --oidc --issuer-domain <domain>`. Losing what reaches the registry's sign-in — membership of one of the registry's domains, or, for an external member, the grant on its client — revokes a user's registry tokens.
|
|
220
|
+
- **Moving Feria to another domain**: move its plugin's domain, re-create its tunnel there, point Feria's `auth.json` at the new issuer and audience, and set the credential again with the new `issuerDomain`.
|
|
221
|
+
|
|
209
222
|
## Related
|
|
210
223
|
|
|
211
224
|
- [Authentication (Authelia)](../01-concepts/authentication.md) — the OpenID Connect provider configuration
|
|
@@ -150,6 +150,7 @@ For a custom domain:
|
|
|
150
150
|
| `aliases` | `string[]` | Custom sites only, optional, at most 10 hostnames | Hostnames that 301 to the site, e.g. `["www.myblog.net"]`. Each needs its own A record; one certificate covers them all |
|
|
151
151
|
| `spaMode` | `boolean` | Optional, defaults to `false` | Serve `index.html` for all routes |
|
|
152
152
|
| `autheliaProtected` | `boolean` | Optional, defaults to `false` | Require Authelia login |
|
|
153
|
+
| `domain` | `string` | Optional, managed sites only; an active domain (defaults to the primary) | A managed site's base domain: `<name>.<domain>` ([Domains](domains.md)) |
|
|
153
154
|
|
|
154
155
|
**Name regex:**
|
|
155
156
|
|
|
@@ -620,6 +621,10 @@ curl -s --cert client.p12:password \
|
|
|
620
621
|
| 400 | `{"error":"..."}` | Path traversal attempt or file not found |
|
|
621
622
|
| 404 | `{"error":"Site not found"}` | No site with the given UUID |
|
|
622
623
|
|
|
624
|
+
## Sites and domains
|
|
625
|
+
|
|
626
|
+
A managed site is `<name>.<domain>`; site **names** stay unique across the relay (agents' `allowedSites` and the CLI name sites by them), so a second `blog.` on another domain is a custom site there. A custom site's hostname names its own domain. An Authelia-protected site admits members of its domain only (its `allowedUsers` narrowed to them); a site whose hostname is under none of the relay's domains cannot be protected (400 `NOT_UNDER_DOMAIN` — no session cookie reaches it). A signed-in user Authelia refuses gets Gatekeeper's refusal page (`error_page 403` → `/authz/refusal/<fqdn>`), which names both possible reasons: not a member of the domain, or not one of the site's users. The relay's own hosts (`panel.`, `auth.`, `tunnel.`, `agent-*` under any domain) are never a custom site or an alias; a domain's apex and `www.` may be.
|
|
627
|
+
|
|
623
628
|
## Quick Reference
|
|
624
629
|
|
|
625
630
|
| Method | Path | Description |
|
|
@@ -98,15 +98,16 @@ The carrying agent's sync timer picks the tunnel up within about 30 seconds and
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
| Field | Type | Validation
|
|
102
|
-
| --------------- | --------- |
|
|
103
|
-
| `subdomain` | `string` | Lowercase alphanumeric + hyphens, max 63 chars, cannot start/end with hyphen
|
|
104
|
-
| `port` | `integer` | 1024 - 65535, not a [reserved port](#port)
|
|
105
|
-
| `agentLabel` | `string` | An enrolled, non-revoked agent's label
|
|
106
|
-
| `description` | `string` | Max 200 chars, optional (defaults to `""`)
|
|
107
|
-
| `type` | `string` | `'app'` (default), `'panel'`, or `'plugin'`
|
|
108
|
-
| `accessMode` | `string` | `'restricted'` (default), `'authenticated'`, `'public'`
|
|
109
|
-
| `maxBodySizeMb` | `integer` | 1 - 10240 for admins, 1 - 100 for agents; optional (defaults to 10)
|
|
101
|
+
| Field | Type | Validation | Description |
|
|
102
|
+
| --------------- | --------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `subdomain` | `string` | Lowercase alphanumeric + hyphens, max 63 chars, cannot start/end with hyphen | The subdomain to create |
|
|
104
|
+
| `port` | `integer` | 1024 - 65535, not a [reserved port](#port) | Port the agent forwards |
|
|
105
|
+
| `agentLabel` | `string` | An enrolled, non-revoked agent's label | **Required for admin certificates.** An agent certificate may omit it (defaults to itself) but may not name another agent |
|
|
106
|
+
| `description` | `string` | Max 200 chars, optional (defaults to `""`) | Human-readable description |
|
|
107
|
+
| `type` | `string` | `'app'` (default), `'panel'`, or `'plugin'` | `panel` requires the `panel:expose` capability; `plugin` is admin-only |
|
|
108
|
+
| `accessMode` | `string` | `'restricted'` (default), `'authenticated'`, `'public'` | `authenticated` and `public` are admin-only |
|
|
109
|
+
| `maxBodySizeMb` | `integer` | 1 - 10240 for admins, 1 - 100 for agents; optional (defaults to 10) | Written into the vhost as `client_max_body_size`; larger requests get HTTP 413. Raise it for apps that take big uploads |
|
|
110
|
+
| `domain` | `string` | An active domain of the relay; optional (defaults to the primary); panel tunnels only on the primary | The base domain of the hostname: `<subdomain>.<domain>` ([Domains](domains.md)) |
|
|
110
111
|
|
|
111
112
|
**Subdomain regex:**
|
|
112
113
|
|
|
@@ -318,7 +319,7 @@ curl -s --cert client.p12:password \
|
|
|
318
319
|
| `chiselArgs` | `string[]` | Chisel client arguments: `client`, the server URL, and one `R:127.0.0.1:<port>:127.0.0.1:<port>` remote per tunnel. No `--tls-skip-verify` (the agent verifies the relay's certificate) and no credential — the agent supplies its own through the service environment, never argv |
|
|
319
320
|
| `chiselCredentialIssuedAt` | `string \| null` | When the agent's current Chisel credential was issued — a timestamp, never the credential. An agent holding a credential with a different issue time (an administrator rotated it) fetches the current one from `GET /api/agents/me/chisel-credential` |
|
|
320
321
|
| `tunnels` | `array` | The agent's enabled tunnels with port and subdomain |
|
|
321
|
-
| `hostFactsHash` | `string \| null` | Agent certificates only: the SHA-256 of the [host facts](agent-hosted-plugins.md#host-facts) the relay stores for the agent, computed as the agent computes it over its own (`hostFactsHash` in `@lamalibre/lamaste@4.
|
|
322
|
+
| `hostFactsHash` | `string \| null` | Agent certificates only: the SHA-256 of the [host facts](agent-hosted-plugins.md#host-facts) the relay stores for the agent, computed as the agent computes it over its own (`hostFactsHash` in `@lamalibre/lamaste@4.7.0`); `null` when it stores none. The agent's sync reports its facts again when this is not the hash of its own, so a record replaced since its last report — the agent enrolled again on its label, the relay restored from a backup — has them again after one sync, not a day. An agent behind a relay without the field reports as before |
|
|
322
323
|
|
|
323
324
|
**Errors:**
|
|
324
325
|
|
|
@@ -407,6 +408,10 @@ Revoking an agent's certificate releases its tunnels: its app and plugin tunnels
|
|
|
407
408
|
- Must be unique across all tunnels
|
|
408
409
|
- Must not be a hostname a static site or site alias already serves (checked on tunnel creation; site creation and alias changes check the reverse)
|
|
409
410
|
|
|
411
|
+
### Hostname
|
|
412
|
+
|
|
413
|
+
A tunnel's hostname is `<subdomain>.<domain>` and is unique as a whole: `feria.samplepark.tr` and `feria.codelama.com.tr` may both exist. Reserved labels (`panel`, `auth`, `tunnel`, `www`, `mail`, `ftp`, `api`, and `agent-*` except an agent's own panel tunnel) are reserved on every domain. `agent-config` lists each carried tunnel with its `fqdn`; agents' chisel needs only the port and the primary's `tunnel.` endpoint, so a tunnel on another domain changes nothing on the agent. The vhost is `lamalibre-lamaste-app-<fqdn>` (`lamalibre-lamaste-agent-panel-<fqdn>` for a panel tunnel).
|
|
414
|
+
|
|
410
415
|
### Port
|
|
411
416
|
|
|
412
417
|
- Must be an integer
|
|
@@ -19,10 +19,10 @@ If onboarding is not complete, all endpoints return `503 Service Unavailable`.
|
|
|
19
19
|
## Important Behavior
|
|
20
20
|
|
|
21
21
|
- **Password hashing:** Passwords are hashed with bcrypt (not argon2id — argon2id uses ~93MB per hash and causes OOM kills on a 512MB droplet).
|
|
22
|
-
- **Authelia restart:**
|
|
22
|
+
- **No Authelia restart:** Authelia watches `users.yml` (`authentication_backend.file.watch: true`) and re-reads a user's details at each of their requests, so a user change (create, update, disable, delete) applies without a restart and without ending anyone else's session: a disabled or deleted user's session ends at their next request. Authelia skips a reload within 0.5 s of its previous one, so the panel writes the file once more 0.6 s after its last write — the change is always picked up. TOTP reset writes directly to Authelia's SQLite database via the `authelia storage user totp generate` CLI command, which takes effect immediately.
|
|
23
23
|
- **Atomic writes:** The `users.yml` file is written atomically (write to temp file, then rename) to prevent Authelia from reading a partially written file.
|
|
24
|
-
- **One lock for every user write:** creating, updating, disabling and deleting users, and accepting an invitation, are read-modify-writes of `users.yml` under one lock in the panel. Under the same lock the panel brings the directory in line with the file
|
|
25
|
-
- **A change must stick:** Authelia's own password reset rewrites `users.yml` from what
|
|
24
|
+
- **One lock for every user write:** creating, updating, disabling and deleting users, and accepting an invitation, are read-modify-writes of `users.yml` under one lock in the panel. Under the same lock the panel brings the directory in line with the file — so a disabled user is refused by plugins at once — [offboards](#offboarding) every user that stopped being one, renders who may reach each domain, drops Gatekeeper's cached sessions, then recomputes the plugins' audiences. Concurrent user changes therefore run one after the other.
|
|
25
|
+
- **A change must stick:** Authelia's own password reset rewrites `users.yml` from what it holds in memory, so a change written in the instant before can be lost. The panel reads `users.yml` again and checks that it holds the change (the fields set, the user created or gone); when it does not, the change is made once more. When it still does not, the answer carries `"ok": false`, `"persisted": false` and a `persist` warning — never a silent success.
|
|
26
26
|
- **The directory follows `users.yml`, read only:** at start, after onboarding and every minute the panel reconciles the directory with the file without writing it (Authelia's own password reset rewrites it; an operator may edit it): an unknown username gets a new `userId`, a changed display name, email or `disabled` is adopted, and a username gone from the file — seen missing in two reads, so a half-written file never tombstones anyone — is removed as `deleted-outside`. A user the reconcile sees disabled or removed is offboarded exactly as one disabled or deleted through the API. Every change is audited (`directory.user.created`, `.updated`, `.removed`) by `userId` alone; audit entries never name a user.
|
|
27
27
|
- **Audit:** besides the directory's own entries, the users API records `user.password.set` (a password set through `PUT`), `user.groups.changed` (`added`, `removed` group names) and `user.totp.reset` — each by `userId` alone.
|
|
28
28
|
- **Last user protection:** You cannot delete the last remaining user. At least one user must always exist.
|
|
@@ -75,10 +75,11 @@ curl -s --cert client.p12:password \
|
|
|
75
75
|
| `username` | `string` | Unique login identifier |
|
|
76
76
|
| `displayname` | `string` | Human-readable display name |
|
|
77
77
|
| `email` | `string` | User's email address |
|
|
78
|
-
| `groups` | `string[]` | Authelia group memberships
|
|
78
|
+
| `groups` | `string[]` | Authelia group memberships an administrator assigned — never the groups Lamaste owns ([Domains](#domains)) |
|
|
79
79
|
| `disabled` | `boolean` | Authelia's `disabled: true` — the user cannot sign in |
|
|
80
80
|
| `userId` | `string \| null` | The user's directory id; `null` until the directory has seen the user (within a minute of an edit of `users.yml` made outside the panel) |
|
|
81
81
|
| `status` | `string` | `active` or `disabled` |
|
|
82
|
+
| `domains` | `object[]` | Domain memberships, `{ name, kind }` — `kind` `member` or `external` ([Domains](#domains)) |
|
|
82
83
|
|
|
83
84
|
Users deleted from `users.yml` are not listed here; the [directory](#get-api-identity-directory) keeps their tombstones.
|
|
84
85
|
|
|
@@ -106,13 +107,14 @@ Creates a new Authelia user. The password is hashed with bcrypt before being sto
|
|
|
106
107
|
}
|
|
107
108
|
```
|
|
108
109
|
|
|
109
|
-
| Field | Type | Validation
|
|
110
|
-
| ------------- | ---------- |
|
|
111
|
-
| `username` | `string` | 2-32 chars, lowercase alphanumeric + underscores + hyphens, starting with a letter or digit
|
|
112
|
-
| `displayname` | `string` | 1-100 chars
|
|
113
|
-
| `email` | `string` | Valid email format
|
|
114
|
-
| `password` | `string` | 8-128 chars
|
|
115
|
-
| `groups` | `string[]` | Optional, defaults to `[]`
|
|
110
|
+
| Field | Type | Validation | Description |
|
|
111
|
+
| ------------- | ---------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
112
|
+
| `username` | `string` | 2-32 chars, lowercase alphanumeric + underscores + hyphens, starting with a letter or digit | Login identifier |
|
|
113
|
+
| `displayname` | `string` | 1-100 chars | Display name |
|
|
114
|
+
| `email` | `string` | Valid email format | Email address |
|
|
115
|
+
| `password` | `string` | 8-128 chars | Plain-text password (hashed before storage) |
|
|
116
|
+
| `groups` | `string[]` | Optional, defaults to `[]` | Authelia group memberships |
|
|
117
|
+
| `domains` | array | Names or `{ name, kind }`, at most 50; required once the relay has two (400 `domains_required`) | Memberships ([Domains](#domains)); with one domain, a member of it unless named `external` |
|
|
116
118
|
|
|
117
119
|
**Username regex** (the same rule applies to invitations — a leading `-` would read as a flag to Authelia's CLI, which the panel runs per username for TOTP secrets, second factors and OpenID Connect identities):
|
|
118
120
|
|
|
@@ -145,7 +147,7 @@ curl -s --cert client.p12:password \
|
|
|
145
147
|
}
|
|
146
148
|
```
|
|
147
149
|
|
|
148
|
-
A new user gets a new `userId`, even when an earlier, deleted user had the same username. Before the new user is created ([Reused usernames](#reused-usernames)): the username is refused when Authelia holds an OpenID Connect identity for it; and when a deleted user held it, Gatekeeper drops everything it holds under the username (grants, group memberships) and Authelia the second factors (TOTP, WebAuthn) — always, not only when a cleanup was pending — so a reused username inherits no grant, group, role or second factor. `warnings` (`[{ step, message }]`) lists what went wrong after `users.yml` was written — the directory not updated,
|
|
150
|
+
A new user gets a new `userId`, even when an earlier, deleted user had the same username. Before the new user is created ([Reused usernames](#reused-usernames)): the username is refused when Authelia holds an OpenID Connect identity for it; and when a deleted user held it, Gatekeeper drops everything it holds under the username (grants, group memberships) and Authelia the second factors (TOTP, WebAuthn) — always, not only when a cleanup was pending — so a reused username inherits no grant, group, role or second factor. `warnings` (`[{ step, message }]`) lists what went wrong after `users.yml` was written — the directory not updated, domain access not rendered; a change that did not stick answers `200` with `"ok": false`, `"persisted": false`.
|
|
149
151
|
|
|
150
152
|
**Errors:**
|
|
151
153
|
|
|
@@ -179,13 +181,14 @@ If a new password is provided, it is hashed with bcrypt before storage.
|
|
|
179
181
|
}
|
|
180
182
|
```
|
|
181
183
|
|
|
182
|
-
| Field | Type | Validation | Description
|
|
183
|
-
| ------------- | ---------- | --------------------- |
|
|
184
|
-
| `displayname` | `string` | 1-100 chars, optional | New display name
|
|
185
|
-
| `email` | `string` | Valid email, optional | New email address
|
|
186
|
-
| `password` | `string` | 8-128 chars, optional | New password (hashed before storage)
|
|
187
|
-
| `groups` | `string[]` | Optional | New group memberships (replaces entire list)
|
|
188
|
-
| `
|
|
184
|
+
| Field | Type | Validation | Description |
|
|
185
|
+
| ------------- | ---------- | --------------------- | --------------------------------------------------------------------------------- |
|
|
186
|
+
| `displayname` | `string` | 1-100 chars, optional | New display name |
|
|
187
|
+
| `email` | `string` | Valid email, optional | New email address |
|
|
188
|
+
| `password` | `string` | 8-128 chars, optional | New password (hashed before storage) |
|
|
189
|
+
| `groups` | `string[]` | Optional | New group memberships (replaces entire list; Lamaste's own groups are kept) |
|
|
190
|
+
| `domains` | array | Optional | Memberships afterwards, exactly — names or `{ name, kind }` ([Domains](#domains)) |
|
|
191
|
+
| `disabled` | `boolean` | Optional | `true` disables the user, `false` enables them ([below](#disabling-a-user)) |
|
|
189
192
|
|
|
190
193
|
All fields are optional, but at least one must be provided (validated by a Zod `.refine()`).
|
|
191
194
|
|
|
@@ -214,11 +217,11 @@ curl -s --cert client.p12:password \
|
|
|
214
217
|
}
|
|
215
218
|
```
|
|
216
219
|
|
|
217
|
-
`warnings` lists what did not happen after the write (the directory,
|
|
220
|
+
`warnings` lists what did not happen after the write (the directory, rendering domain access, the steps of [offboarding](#offboarding)); `"ok": false` with `"persisted": false` and a `persist` warning says the change did not stick ([above](#important-behavior)). A change of `groups` revokes the user's registry (Feria) tokens, whose scopes were granted from `feria-*` groups at login.
|
|
218
221
|
|
|
219
222
|
#### Disabling a user
|
|
220
223
|
|
|
221
|
-
`{ "disabled": true }` is the offboarding action. It sets Authelia's `disabled: true` — the user can no longer sign in
|
|
224
|
+
`{ "disabled": true }` is the offboarding action. It sets Authelia's `disabled: true` — the user can no longer sign in, and their session ends at their next request — and:
|
|
222
225
|
|
|
223
226
|
- the directory marks the user `disabled`, so every plugin's [user surface](agent-hosted-plugins.md#the-user-surface) refuses them at once (`403`);
|
|
224
227
|
- every plugin audience that holds them shows them `disabled`, and their agent halves are told;
|
|
@@ -256,7 +259,7 @@ Desktop sessions are bound to the `userId` as well as the username: one is valid
|
|
|
256
259
|
|
|
257
260
|
### `DELETE /api/users/:username`
|
|
258
261
|
|
|
259
|
-
Deletes an Authelia user: the user is removed from `users.yml`, the directory turns their row into a **tombstone** (`removed`, reason `deleted`, keeping the `userId`, the username and the last display name and email, so plugins' history stays readable)
|
|
262
|
+
Deletes an Authelia user: the user is removed from `users.yml`, the directory turns their row into a **tombstone** (`removed`, reason `deleted`, keeping the `userId`, the username and the last display name and email, so plugins' history stays readable); their session ends at their next request. With `?erase=true` the tombstone is [erased](#erasing-a-removed-user) at once as well.
|
|
260
263
|
|
|
261
264
|
You cannot delete the last remaining user. This safety check prevents accidentally locking out all users from tunneled applications.
|
|
262
265
|
|
|
@@ -344,6 +347,14 @@ The `totpUri` follows the [Key Uri Format](https://github.com/google/google-auth
|
|
|
344
347
|
| 500 | `{"error":"Failed to read user database"}` | Cannot read `users.yml` |
|
|
345
348
|
| 500 | `{"error":"Failed to write TOTP configuration"}` | Cannot write TOTP secret to Authelia's SQLite database |
|
|
346
349
|
|
|
350
|
+
### Domains
|
|
351
|
+
|
|
352
|
+
Every user listed by `GET /api/users` carries `domains`: their memberships, `{ name, kind }` — `kind` `member` or `external` (empty until the directory has seen them). `PUT /api/users/:username` takes `domains` too — the memberships afterwards, exactly: the difference is applied in one change, each membership audited (409 `user_not_in_directory` until the directory has seen the user, 400 `unknown_domain`, 400 `conflicting_membership` for one domain named twice with two kinds); one membership at a time is the [Domains API](domains.md) (`PUT`/`DELETE /api/domains/:name/members/:user`, by `userId` or username). Wherever a request names memberships, a domain name is a `member` and `{ "name": "…", "kind": "external" }` an external member. On a relay with two domains or more, `POST /api/users` needs a non-empty `domains` (400 `domains_required`). `POST /api/invitations` takes `domains` exactly as `POST /api/users` does, and `GET /api/invitations` lists each invitation's `domains` as `{ name, kind }`; the invitation link is on the sign-in page of the first domain it names (`https://auth.<domain>/invite/<token>`). Disabling a user keeps their memberships (they are let in nowhere while disabled); deleting ends them.
|
|
353
|
+
|
|
354
|
+
**External members** reach only what names them — `restricted` hosts that grant them, static sites that list them, OIDC clients granted to them — and **hold no Authelia groups**: a user who is an external member of any domain cannot be given a group (409 `external_member_groups`), a user with groups cannot be made external (409), and a new user or an invitation asking for both is refused (400 `external_member_groups`).
|
|
355
|
+
|
|
356
|
+
**Groups Lamaste owns.** Who may reach what is written into each user's `groups` in `users.yml` as groups Lamaste owns — `lamaste:m:<key>`, `lamaste:x:<key>` (a random key per domain), `lamaste:s:<site id>`, hostnames (external members' granted hosts), `lamaste:oidc@<client id>` ([Domains API](domains.md#where-membership-is-enforced)). The users and identity APIs never show them and an edit keeps them; an administrator assigns only names of letters, digits, `-` and `_` (400 `reserved_group` for a name with `.`, `:` or `@` the user does not hold already).
|
|
357
|
+
|
|
347
358
|
## The User Directory
|
|
348
359
|
|
|
349
360
|
The directory lives in the panel's `state.db` (migration `0013`): one row per user it ever knew, never deleted, and the directory's own id (`directoryId`, a UUID v4 made with the store), which tells a plugin's agent half when the store it follows was replaced. Admin only.
|
|
@@ -475,13 +486,14 @@ Returned by list and mutation endpoints (sensitive fields excluded):
|
|
|
475
486
|
|
|
476
487
|
### Validation Summary
|
|
477
488
|
|
|
478
|
-
| Field | Rules
|
|
479
|
-
| ------------- |
|
|
480
|
-
| `username` | 2-32 chars, `/^[a-z0-9][a-z0-9_-]*$/`, unique
|
|
481
|
-
| `displayname` | 1-100 chars
|
|
482
|
-
| `email` | Valid email format
|
|
483
|
-
| `password` | 8-128 chars
|
|
484
|
-
| `groups` | Array of strings, optional
|
|
489
|
+
| Field | Rules |
|
|
490
|
+
| ------------- | ----------------------------------------------------------------------------------- |
|
|
491
|
+
| `username` | 2-32 chars, `/^[a-z0-9][a-z0-9_-]*$/`, unique |
|
|
492
|
+
| `displayname` | 1-100 chars |
|
|
493
|
+
| `email` | Valid email format |
|
|
494
|
+
| `password` | 8-128 chars |
|
|
495
|
+
| `groups` | Array of strings, optional; `[A-Za-z0-9_-]{1,64}` each; none for an external member |
|
|
496
|
+
| `domains` | Names or `{ name, kind }`, at most 50 |
|
|
485
497
|
|
|
486
498
|
### curl Cheat Sheet
|
|
487
499
|
|
|
@@ -199,7 +199,7 @@ sudo chown lamaste-authelia:lamaste-authelia /etc/authelia/db.sqlite3* /etc/auth
|
|
|
199
199
|
sudo chmod 600 /etc/authelia/db.sqlite3* /etc/authelia/notifications.txt
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
A backup taken on a version before the privilege boundary holds root-owned Authelia files and older unit files. After restoring one, re-run `npx @lamalibre/create-lamaste@4.
|
|
202
|
+
A backup taken on a version before the privilege boundary holds root-owned Authelia files and older unit files. After restoring one, re-run `npx @lamalibre/create-lamaste@4.7.0 --yes`: the redeploy rewrites the `chisel` and `authelia` units and converts the ownership of `/etc/authelia`, the Chisel key and the PKI directory (see [Upgrades](upgrades.md)).
|
|
203
203
|
|
|
204
204
|
The panel re-applies the authfile's group and mode itself on every start, so a Chisel that fails to read it before the panel is up is started again by the panel's reconciliation.
|
|
205
205
|
|
|
@@ -29,7 +29,7 @@ It stops and disables the panel, gatekeeper, Chisel and Authelia; removes their
|
|
|
29
29
|
The manual guide is also available from the CLI:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
npx @lamalibre/create-lamaste@4.
|
|
32
|
+
npx @lamalibre/create-lamaste@4.7.0 --uninstall
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This prints the same steps listed below.
|
|
@@ -27,7 +27,7 @@ Coming from 3.x, upgrade the **agents first**, as 4.0 required: agents from befo
|
|
|
27
27
|
On each agent machine, after the server:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
sudo npm install -g @lamalibre/lamaste-agent@4.
|
|
30
|
+
sudo npm install -g @lamalibre/lamaste-agent@4.7.0
|
|
31
31
|
lamaste-agent update
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -50,7 +50,7 @@ A machine that hosts app plugins' agent halves needs the root-owned install show
|
|
|
50
50
|
|
|
51
51
|
- **`state.db` migration `0013`.** On its first start the panel creates the user directory (a `directoryId`, a `userId` per user, tombstones), the plugin audiences, the per-plugin feed state and the erasure records, and adds `plugins.identity`. It then reads `users.yml` — without writing it — and gives every existing user a `userId`. Back up `state.db` before upgrading, as before any upgrade: from now on it holds the mapping from usernames to user ids ([Backup and Restore](backup-and-restore.md#backup-considerations)).
|
|
52
52
|
- **The redeploy now touches the IP panel vhost.** Until 4.4 a redeploy left every panel vhost alone. From 4.5 it refreshes `lamalibre-lamaste-panel-ip` — the vhost the installer writes and the panel cannot — when its text differs from the current generator's: written, checked with `nginx -t`, put back on failure, and nginx reloaded; its `sites-enabled` link (which panel 2FA owns) is left as it is. A failure is reported and skipped (`Refreshing the IP panel vhost — Skipped`): the panel keeps working on the vhost it had, which still passes a client's `X-Lamalibre-Relay-Assertion` through to the panel (which accepts only a valid, single-use one Gatekeeper signed) — fix what `nginx -t` names and redeploy. The panel itself rewrites the domain panel, auth portal and tunnel vhosts at start, as since 4.3; each now clears a client's `X-Lamalibre-Relay-Assertion`.
|
|
53
|
-
- **No agent upgrade is needed**, and no agent half must be rebuilt. An agent half built on `@lamalibre/lamaste@4.
|
|
53
|
+
- **No agent upgrade is needed**, and no agent half must be rebuilt. An agent half built on `@lamalibre/lamaste@4.7.0` 4.4 keeps working and **never receives a user caller**: the panel sends one only after the half has answered the identity feed with protocol 1 for its installed version. A plugin gets users by shipping a version that declares `identity` with a half built on 4.5 — an update that waits for approval — then binding its user surface to a tunnel.
|
|
54
54
|
- **What changes for existing setups.** The admin forwarder no longer reaches an agent half's `/admin/app` or anything under it (on the panel `/api/<route>/app*` is the user surface). The hook preview's `inactiveRoutes` is now `userSurfaceRoutes`. Every write of `users.yml` the panel makes — creating, updating, deleting users, accepting invitations — now runs under one lock, one after the other. Deleting a user keeps a tombstone in the directory and clears the username's grants and group memberships in Gatekeeper. A registry credential must carry the `read` scope as well as `admin` (a `PUT /api/registry-credential` with an admin-only identity is refused).
|
|
55
55
|
|
|
56
56
|
### Updating Lamaste (Panel Server and Client)
|
|
@@ -102,7 +102,7 @@ exit
|
|
|
102
102
|
|
|
103
103
|
### Updating Chisel
|
|
104
104
|
|
|
105
|
-
Chisel is the tunnel server binary at `/usr/local/bin/chisel`. Lamaste pins it: server and agents run Chisel 1.12.0 (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.
|
|
105
|
+
Chisel is the tunnel server binary at `/usr/local/bin/chisel`. Lamaste pins it: server and agents run Chisel 1.12.0 (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.7.0`), downloaded from the fixed release URL and verified against a pinned SHA-256 before it is unpacked. **Do not replace the binary by hand.** On the server, `create-lamaste` installs it (as root) and replaces a binary that reports any other version; the panel cannot install binaries, and only logs an error at startup when the installed Chisel is not the pinned release (it then restarts Chisel on every authfile change). Each agent's sync replaces its own copy.
|
|
106
106
|
|
|
107
107
|
A new Chisel version therefore arrives with a Lamaste release that changes the pin: re-run the installer, which installs it and restarts Chisel. To check what is running:
|
|
108
108
|
|
|
@@ -116,7 +116,7 @@ Tunnel clients reconnect on their own after the brief interruption (they retry a
|
|
|
116
116
|
|
|
117
117
|
### Updating Authelia
|
|
118
118
|
|
|
119
|
-
Authelia is the authentication server binary at `/usr/local/bin/authelia`. Lamaste pins it too: Authelia 4.39.28 (`AUTHELIA_RELEASE` in `@lamalibre/lamaste@4.
|
|
119
|
+
Authelia is the authentication server binary at `/usr/local/bin/authelia`. Lamaste pins it too: Authelia 4.39.28 (`AUTHELIA_RELEASE` in `@lamalibre/lamaste@4.7.0`), downloaded by `create-lamaste` from its fixed release URL and verified against a pinned SHA-256 per architecture. A new Authelia version arrives with a Lamaste release that changes the pin — re-run the installer. It stops Authelia while it replaces the binary and starts it again.
|
|
120
120
|
|
|
121
121
|
An installed Authelia **newer** than the pin is kept, never downgraded: its database may already use a schema the pinned release cannot read, and Authelia refuses to start on a database newer than itself. If you install a newer release by hand, stop the service, replace the binary as root with a `root:root 0755` file, and start it again; check the [Authelia changelog](https://github.com/authelia/authelia/blob/master/CHANGELOG.md) first, since the configuration format may change between versions.
|
|
122
122
|
|
|
@@ -255,7 +255,7 @@ The panel server, CLI, client, docs and gatekeeper are always redeployed (files
|
|
|
255
255
|
|
|
256
256
|
### Version Pinning
|
|
257
257
|
|
|
258
|
-
Chisel and Authelia are pinned by version and SHA-256 in `packages/core/lib/src/constants.ts` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`); changing a pin means updating the version and every digest there. The downloads are verified by `downloadVerifiedChisel` / `downloadVerifiedAuthelia` (exported from `@lamalibre/lamaste@4.
|
|
258
|
+
Chisel and Authelia are pinned by version and SHA-256 in `packages/core/lib/src/constants.ts` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`); changing a pin means updating the version and every digest there. The downloads are verified by `downloadVerifiedChisel` / `downloadVerifiedAuthelia` (exported from `@lamalibre/lamaste@4.7.0`) before anything is unpacked; the server installer uses them in `packages/provisioners/server/src/lib/binaries.js`. The configuration the panel writes needs Authelia 4.38 or later.
|
|
259
259
|
|
|
260
260
|
### Automated Updates
|
|
261
261
|
|
|
@@ -277,7 +277,7 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
|
|
277
277
|
| Panel server | `/opt/lamalibre/lamaste/lamaste-serverd/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
278
278
|
| Panel client | `/opt/lamalibre/lamaste/lamaste-server-ui/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
279
279
|
| Chisel | `/usr/local/bin/chisel` | Pinned; installed by `create-lamaste` |
|
|
280
|
-
| Agents | `sudo npm install -g @lamalibre/lamaste-agent@4.
|
|
280
|
+
| Agents | `sudo npm install -g @lamalibre/lamaste-agent@4.7.0` | Upgrade after the server, then `lamaste-agent update` |
|
|
281
281
|
| Authelia | `/usr/local/bin/authelia` | Pinned; installed by `create-lamaste` (newer is kept) |
|
|
282
282
|
| nginx | System package | `sudo apt-get update && sudo apt-get upgrade` |
|
|
283
283
|
| certbot | System package | `sudo apt-get update && sudo apt-get upgrade` |
|
|
@@ -822,7 +822,7 @@ Service files use per-agent names:
|
|
|
822
822
|
- macOS: `com.lamalibre.lamaste.chisel-<label>` (plist label), `~/Library/LaunchAgents/com.lamalibre.lamaste.chisel-<label>.plist` — mode 0600, because it carries `AUTH` in `EnvironmentVariables`
|
|
823
823
|
- Linux: `lamalibre-lamaste-chisel-<label>` (unit name), `~/.config/systemd/user/lamalibre-lamaste-chisel-<label>.service` — a systemd **user** unit with `EnvironmentFile=` pointing at `chisel.env`; enable lingering (`sudo loginctl enable-linger <user>`) for it to start at boot without a login
|
|
824
824
|
|
|
825
|
-
Both run `chisel client --max-retry-interval 30s https://tunnel.<domain>:443 R:127.0.0.1:<port>:127.0.0.1:<port> ...` with TLS verification and no credential in argv. They are generated by `@lamalibre/lamaste@4.
|
|
825
|
+
Both run `chisel client --max-retry-interval 30s https://tunnel.<domain>:443 R:127.0.0.1:<port>:127.0.0.1:<port> ...` with TLS verification and no credential in argv. They are generated by `@lamalibre/lamaste@4.7.0/agent` (`chisel-service.ts`) and rewritten by `lamaste-agent setup`, `update` and `sync` — do not edit them by hand. An agent carrying no tunnels has its service unloaded (Chisel cannot run without a remote).
|
|
826
826
|
|
|
827
827
|
The sync timer, which runs `<node> <installed lamaste-agent> sync --label <label> --quiet` (`<node>` is a stable path such as `/opt/homebrew/bin/node` when one on `PATH` resolves to the running binary, so a Node.js upgrade does not break it):
|
|
828
828
|
|
|
@@ -24,7 +24,7 @@ If you are reading documentation and hit an unfamiliar term, this is the place t
|
|
|
24
24
|
|
|
25
25
|
**Where it appears:**
|
|
26
26
|
|
|
27
|
-
- Enrolled from the panel (**Certificates → Enrollment Token**) and `lamaste-agent setup` (installed globally with `npm install -g @lamalibre/lamaste-agent@4.
|
|
27
|
+
- Enrolled from the panel (**Certificates → Enrollment Token**) and `lamaste-agent setup` (installed globally with `npm install -g @lamalibre/lamaste-agent@4.7.0`)
|
|
28
28
|
- Agent registry: `/etc/lamalibre/lamaste/agents.json` on the server; `~/.lamalibre/lamaste/agents/<label>/` on the agent
|
|
29
29
|
- The **Agent** column and field on the panel's Tunnels page
|
|
30
30
|
|
|
@@ -242,7 +242,7 @@ See also: [mTLS](#mtls-mutual-tls), [CA](#ca-certificate-authority), [PKCS12](#p
|
|
|
242
242
|
|
|
243
243
|
**Where it appears:**
|
|
244
244
|
|
|
245
|
-
- Generated by `lamaste-agent setup` / `update` (implementation in `@lamalibre/lamaste@4.
|
|
245
|
+
- Generated by `lamaste-agent setup` / `update` (implementation in `@lamalibre/lamaste@4.7.0/agent`)
|
|
246
246
|
- Installed to `~/Library/LaunchAgents/com.lamalibre.lamaste.chisel-<label>.plist` on the agent
|
|
247
247
|
- Manages the Chisel client process lifecycle
|
|
248
248
|
|
|
@@ -373,7 +373,7 @@ See also: [Client Certificate](#client-certificate), [CA](#ca-certificate-author
|
|
|
373
373
|
|
|
374
374
|
**Plain English:** A small scheduled job on every agent that checks with the panel every 30 seconds and adjusts the tunnel client when the agent's tunnels changed — so tunnels created, disabled or moved in the panel reach the agent without anyone running a command on it.
|
|
375
375
|
|
|
376
|
-
**Technical:** Runs `lamaste-agent sync --label <label>`, which fetches `GET /api/tunnels/agent-config` and converges the agent's Chisel client with it (relay URL, credential, pinned binary, granted remotes, running or stopped). It exists because Chisel rejects a client's whole session when one requested remote is not granted: an agent must drop a revoked tunnel promptly or all its other tunnels go dark. Requires a global install of `@lamalibre/lamaste-agent@4.
|
|
376
|
+
**Technical:** Runs `lamaste-agent sync --label <label>`, which fetches `GET /api/tunnels/agent-config` and converges the agent's Chisel client with it (relay URL, credential, pinned binary, granted remotes, running or stopped). It exists because Chisel rejects a client's whole session when one requested remote is not granted: an agent must drop a revoked tunnel promptly or all its other tunnels go dark. Requires a global install of `@lamalibre/lamaste-agent@4.7.0`.
|
|
377
377
|
|
|
378
378
|
**Where it appears:**
|
|
379
379
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Installer Flags
|
|
2
2
|
|
|
3
|
-
> CLI flags, environment variables, and exit codes for `npx @lamalibre/create-lamaste@4.
|
|
3
|
+
> CLI flags, environment variables, and exit codes for `npx @lamalibre/create-lamaste@4.7.0`.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx @lamalibre/create-lamaste@4.
|
|
8
|
+
npx @lamalibre/create-lamaste@4.7.0 [flags]
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
The installer runs as root on a fresh Ubuntu 24.04 server. It is completely non-interactive by default — all configuration happens later through the browser-based onboarding wizard.
|
|
@@ -26,7 +26,7 @@ The installer runs as root on a fresh Ubuntu 24.04 server. It is completely non-
|
|
|
26
26
|
Prints a description of what the installer does, lists all flags, and exits with code 0. Does not modify anything on the system.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx @lamalibre/create-lamaste@4.
|
|
29
|
+
npx @lamalibre/create-lamaste@4.7.0 --help
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
### `--yes`, `-y`
|
|
@@ -34,7 +34,7 @@ npx @lamalibre/create-lamaste@4.6.0 --help
|
|
|
34
34
|
Skips the interactive confirmation prompt that appears before installation begins. Useful for automated deployments or scripts where you cannot press Enter.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npx @lamalibre/create-lamaste@4.
|
|
37
|
+
npx @lamalibre/create-lamaste@4.7.0 --yes
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Without this flag, the installer displays a banner listing all changes it will make and waits for the user to press Enter or Ctrl+C.
|
|
@@ -51,7 +51,7 @@ Skips all OS hardening tasks:
|
|
|
51
51
|
Everything else (Node.js, nginx, mTLS, panel deployment) still runs.
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
npx @lamalibre/create-lamaste@4.
|
|
54
|
+
npx @lamalibre/create-lamaste@4.7.0 --skip-harden
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Use this if you manage your own firewall rules, already have fail2ban configured, or are running on a platform that handles security at a different layer.
|
|
@@ -61,7 +61,7 @@ Use this if you manage your own firewall rules, already have fail2ban configured
|
|
|
61
61
|
Allows the installer to run on a machine with a private or non-routable IP address (e.g., `192.168.x.x`, `10.x.x.x`). By default, the installer requires a public IP.
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx @lamalibre/create-lamaste@4.
|
|
64
|
+
npx @lamalibre/create-lamaste@4.7.0 --dev
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
This is useful for:
|
|
@@ -76,7 +76,7 @@ When `--dev` is active, the installer prints the detected IP with a note: `(dev
|
|
|
76
76
|
Forces the installer to run the complete installation pipeline, ignoring skip guards that would normally detect an existing installation. This is useful when an installation is in an inconsistent state and you want to ensure all components are fully deployed.
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
npx @lamalibre/create-lamaste@4.
|
|
79
|
+
npx @lamalibre/create-lamaste@4.7.0 --force-full
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Use this if:
|
|
@@ -90,7 +90,7 @@ Use this if:
|
|
|
90
90
|
Prints a step-by-step guide for manually removing Lamaste from the server, then exits with code 0. Does not modify anything on the system.
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
-
npx @lamalibre/create-lamaste@4.
|
|
93
|
+
npx @lamalibre/create-lamaste@4.7.0 --uninstall
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
The printed guide first points to the automated removal, `sudo lamaste-server uninstall`, then covers:
|
|
@@ -207,25 +207,25 @@ Components that are always redeployed on re-run:
|
|
|
207
207
|
|
|
208
208
|
```bash
|
|
209
209
|
ssh root@203.0.113.42
|
|
210
|
-
npx @lamalibre/create-lamaste@4.
|
|
210
|
+
npx @lamalibre/create-lamaste@4.7.0
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
**Automated install (no confirmation prompt):**
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
npx @lamalibre/create-lamaste@4.
|
|
216
|
+
npx @lamalibre/create-lamaste@4.7.0 --yes
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
**Install without OS hardening:**
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
|
-
npx @lamalibre/create-lamaste@4.
|
|
222
|
+
npx @lamalibre/create-lamaste@4.7.0 --yes --skip-harden
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
**Test in a local VM:**
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
|
-
npx @lamalibre/create-lamaste@4.
|
|
228
|
+
npx @lamalibre/create-lamaste@4.7.0 --dev
|
|
229
229
|
```
|
|
230
230
|
|
|
231
231
|
**Update an existing installation:**
|
|
@@ -237,7 +237,7 @@ npx @lamalibre/create-lamaste@latest --yes
|
|
|
237
237
|
**View the uninstall guide:**
|
|
238
238
|
|
|
239
239
|
```bash
|
|
240
|
-
npx @lamalibre/create-lamaste@4.
|
|
240
|
+
npx @lamalibre/create-lamaste@4.7.0 --uninstall
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
## Quick Reference
|
|
@@ -34,7 +34,7 @@ A tunnel's port is also a loopback port on the relay: its vhost proxies to `127.
|
|
|
34
34
|
| 9292 | IP-based panel listener |
|
|
35
35
|
| 9294 | Gatekeeper |
|
|
36
36
|
|
|
37
|
-
A tunnel on one of them would publish that internal service on a public hostname. For the panel that is an administrator takeover, not a leak: the panel trusts the client-certificate headers nginx sets. `RESERVED_TUNNEL_PORTS` in `@lamalibre/lamaste@4.
|
|
37
|
+
A tunnel on one of them would publish that internal service on a public hostname. For the panel that is an administrator takeover, not a leak: the panel trusts the client-certificate headers nginx sets. `RESERVED_TUNNEL_PORTS` in `@lamalibre/lamaste@4.7.0` is enforced by the request schema, inside the tunnel workflow, and again when the Chisel grants are rendered. Tunnel ports must also be 1024–65535.
|
|
38
38
|
|
|
39
39
|
## Systemd Units
|
|
40
40
|
|