@lamalibre/create-lamaste 4.0.0 → 4.2.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/lamaste-priv +21 -2
- package/src/lib/oidc-issuer-key.js +124 -0
- package/src/lib/relay-services.js +4 -0
- package/src/lib/service-config.js +16 -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 +45 -1
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/security-model.md +1 -1
- 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 +3 -3
- 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/onboarding.md +2 -2
- 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 +6 -5
- package/vendor/docs/03-architecture/management-flow.md +1 -1
- 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 +2 -2
- package/vendor/docs/03-architecture/system-overview.md +1 -1
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/overview.md +111 -105
- package/vendor/docs/04-api-reference/registry-and-oidc.md +207 -0
- 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 -5
- package/vendor/docs/06-reference/config-files.md +103 -45
- 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 +5 -0
- package/vendor/docs/gatekeeper-design.md +8 -8
- package/vendor/docs/identity-directory-design.md +443 -0
- package/vendor/docs/package.json +1 -1
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
- package/vendor/gatekeeper/package.json +1 -1
- package/vendor/server/bin/lamaste-server.js +10 -1
- package/vendor/server/package.json +4 -3
- package/vendor/server/src/commands/plugins.js +200 -158
- package/vendor/server/src/commands/registry-credential.js +142 -0
- package/vendor/server/src/panel-api.js +86 -39
- package/vendor/server-ui/dist/assets/{index-B0nWuUyL.js → index-B5Wp8lu-.js} +26 -26
- package/vendor/server-ui/dist/index.html +1 -1
- package/vendor/serverd/package.json +5 -2
- package/vendor/serverd/src/index.js +11 -0
- package/vendor/serverd/src/lib/authelia.js +11 -0
- package/vendor/serverd/src/lib/migrations/state/0005_plugin_hosting.sql +3 -0
- package/vendor/serverd/src/lib/plugin-oidc.js +233 -0
- package/vendor/serverd/src/lib/plugins.js +37 -5
- package/vendor/serverd/src/lib/registry-credential.js +145 -0
- package/vendor/serverd/src/routes/management/agents.js +81 -0
- package/vendor/serverd/src/routes/management/plugins.js +65 -2
- package/vendor/serverd/src/routes/management/registry-credential.js +170 -0
- package/vendor/serverd/src/routes/management/storage.js +10 -2
- package/vendor/serverd/src/routes/management/users.js +31 -0
- package/vendor/serverd/src/routes/management.js +2 -0
|
@@ -274,111 +274,117 @@ There are two exceptions:
|
|
|
274
274
|
|
|
275
275
|
### Endpoint Summary
|
|
276
276
|
|
|
277
|
-
| Method | Path
|
|
278
|
-
| ------ |
|
|
279
|
-
| GET | `/api/health`
|
|
280
|
-
| GET | `/api/onboarding/status`
|
|
281
|
-
| POST | `/api/onboarding/domain`
|
|
282
|
-
| POST | `/api/onboarding/verify-dns`
|
|
283
|
-
| POST | `/api/onboarding/provision`
|
|
284
|
-
| WS | `/api/onboarding/provision/stream`
|
|
285
|
-
| GET | `/api/invite/:token`
|
|
286
|
-
| POST | `/api/invite/:token/accept`
|
|
287
|
-
| POST | `/api/enroll`
|
|
288
|
-
| GET | `/api/system/stats`
|
|
289
|
-
| POST | `/api/system/update`
|
|
290
|
-
| POST | `/api/agents/plugins/report`
|
|
291
|
-
| GET | `/api/agents/me/chisel-credential`
|
|
292
|
-
| POST | `/api/agents/me/chisel-credential/rotate`
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
| GET | `/api/tunnels`
|
|
296
|
-
|
|
|
297
|
-
|
|
|
298
|
-
|
|
|
299
|
-
|
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
|
|
|
308
|
-
|
|
|
309
|
-
|
|
|
310
|
-
|
|
|
311
|
-
|
|
|
312
|
-
|
|
|
313
|
-
|
|
|
314
|
-
|
|
|
315
|
-
|
|
|
316
|
-
| GET | `/api/certs
|
|
317
|
-
|
|
|
318
|
-
| POST | `/api/certs/
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
| GET | `/api/certs/agent
|
|
323
|
-
|
|
|
324
|
-
| PATCH | `/api/certs/agent/:label/
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
| POST | `/api/certs/
|
|
330
|
-
|
|
|
331
|
-
| GET | `/api/
|
|
332
|
-
|
|
|
333
|
-
| POST | `/api/settings/2fa/
|
|
334
|
-
| POST | `/api/settings/2fa/
|
|
335
|
-
| POST | `/api/settings/2fa/
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
|
|
|
339
|
-
|
|
|
340
|
-
| GET | `/api/plugins
|
|
341
|
-
|
|
|
342
|
-
| POST | `/api/plugins
|
|
343
|
-
| POST | `/api/plugins/:name/
|
|
344
|
-
|
|
|
345
|
-
|
|
|
346
|
-
|
|
|
347
|
-
|
|
|
348
|
-
|
|
|
349
|
-
|
|
|
350
|
-
| DELETE | `/api/
|
|
351
|
-
|
|
|
352
|
-
|
|
|
353
|
-
| GET | `/api/plugins/push-install/
|
|
354
|
-
| POST | `/api/plugins/push-install
|
|
355
|
-
|
|
|
356
|
-
|
|
|
357
|
-
|
|
|
358
|
-
| DELETE | `/api/
|
|
359
|
-
|
|
|
360
|
-
|
|
|
361
|
-
|
|
|
362
|
-
| POST | `/api/tickets/
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
| POST | `/api/tickets`
|
|
366
|
-
|
|
|
367
|
-
| POST | `/api/tickets/
|
|
368
|
-
|
|
|
369
|
-
| DELETE | `/api/tickets/:
|
|
370
|
-
|
|
|
371
|
-
| POST | `/api/tickets
|
|
372
|
-
|
|
|
373
|
-
|
|
|
374
|
-
| GET | `/api/tickets
|
|
375
|
-
|
|
|
376
|
-
|
|
|
377
|
-
|
|
|
378
|
-
|
|
|
379
|
-
|
|
|
380
|
-
| GET | `/api/
|
|
381
|
-
|
|
|
277
|
+
| Method | Path | Group | Description |
|
|
278
|
+
| ------ | --------------------------------------------------------- | ---------- | ---------------------------------------- |
|
|
279
|
+
| GET | `/api/health` | Always | Health check |
|
|
280
|
+
| GET | `/api/onboarding/status` | Always | Onboarding state |
|
|
281
|
+
| POST | `/api/onboarding/domain` | Onboarding | Set domain and email |
|
|
282
|
+
| POST | `/api/onboarding/verify-dns` | Onboarding | Verify DNS records |
|
|
283
|
+
| POST | `/api/onboarding/provision` | Onboarding | Start provisioning |
|
|
284
|
+
| WS | `/api/onboarding/provision/stream` | Onboarding | Provisioning progress |
|
|
285
|
+
| GET | `/api/invite/:token` | Public | Get invitation details |
|
|
286
|
+
| POST | `/api/invite/:token/accept` | Public | Accept invitation |
|
|
287
|
+
| POST | `/api/enroll` | Public | Enroll agent with token (hardware-bound) |
|
|
288
|
+
| GET | `/api/system/stats` | Management | System statistics |
|
|
289
|
+
| POST | `/api/system/update` | Management | Trigger background server update |
|
|
290
|
+
| POST | `/api/agents/plugins/report` | Management | Report agent plugin capabilities |
|
|
291
|
+
| GET | `/api/agents/me/chisel-credential` | Management | The calling agent's Chisel credential |
|
|
292
|
+
| POST | `/api/agents/me/chisel-credential/rotate` | Management | Rotate own Chisel credential (agent) |
|
|
293
|
+
| GET | `/api/agents/me/registry-credential` | Management | One-hour registry token for the agent |
|
|
294
|
+
| POST | `/api/agents/:label/chisel-credential/rotate` | Management | Rotate an agent's Chisel credential |
|
|
295
|
+
| GET | `/api/tunnels/agent-config` | Management | One agent's tunnel configuration |
|
|
296
|
+
| GET | `/api/tunnels` | Management | List tunnels |
|
|
297
|
+
| POST | `/api/tunnels` | Management | Create tunnel |
|
|
298
|
+
| PATCH | `/api/tunnels/:id` | Management | Move, access mode, body limit, enable |
|
|
299
|
+
| DELETE | `/api/tunnels/:id` | Management | Delete tunnel |
|
|
300
|
+
| GET | `/api/sites` | Management | List static sites |
|
|
301
|
+
| POST | `/api/sites` | Management | Create static site |
|
|
302
|
+
| DELETE | `/api/sites/:id` | Management | Delete static site |
|
|
303
|
+
| PATCH | `/api/sites/:id` | Management | Update site settings |
|
|
304
|
+
| POST | `/api/sites/:id/verify-dns` | Management | Verify site DNS |
|
|
305
|
+
| GET | `/api/sites/:id/files` | Management | List site files |
|
|
306
|
+
| POST | `/api/sites/:id/files` | Management | Upload site files |
|
|
307
|
+
| DELETE | `/api/sites/:id/files` | Management | Delete site file |
|
|
308
|
+
| GET | `/api/invitations` | Management | List invitations |
|
|
309
|
+
| POST | `/api/invitations` | Management | Create invitation |
|
|
310
|
+
| DELETE | `/api/invitations/:id` | Management | Revoke invitation |
|
|
311
|
+
| GET | `/api/users` | Management | List users |
|
|
312
|
+
| POST | `/api/users` | Management | Create user |
|
|
313
|
+
| PUT | `/api/users/:username` | Management | Update user |
|
|
314
|
+
| DELETE | `/api/users/:username` | Management | Delete user |
|
|
315
|
+
| POST | `/api/users/:username/reset-totp` | Management | Reset TOTP secret |
|
|
316
|
+
| GET | `/api/certs` | Management | List certificates |
|
|
317
|
+
| GET | `/api/certs/auto-renew-status` | Management | Auto-renew timer status |
|
|
318
|
+
| POST | `/api/certs/:domain/renew` | Management | Force-renew certificate |
|
|
319
|
+
| POST | `/api/certs/mtls/rotate` | Management | Rotate mTLS cert |
|
|
320
|
+
| GET | `/api/certs/mtls/download` | Management | Download client.p12 |
|
|
321
|
+
| POST | `/api/certs/agent` | Management | Generate agent certificate |
|
|
322
|
+
| GET | `/api/certs/agent` | Management | List agent certificates |
|
|
323
|
+
| GET | `/api/certs/agent/:label/download` | Management | Download agent .p12 |
|
|
324
|
+
| PATCH | `/api/certs/agent/:label/capabilities` | Management | Update agent capabilities |
|
|
325
|
+
| PATCH | `/api/certs/agent/:label/allowed-sites` | Management | Update agent site access |
|
|
326
|
+
| DELETE | `/api/certs/agent/:label` | Management | Revoke agent certificate |
|
|
327
|
+
| POST | `/api/certs/agent/enroll` | Management | Generate enrollment token |
|
|
328
|
+
| DELETE | `/api/certs/agent/enroll/:label` | Management | Revoke unused enrollment token |
|
|
329
|
+
| POST | `/api/certs/agent/upgrade-cert` | Management | Upgrade agent cert to hardware-bound |
|
|
330
|
+
| POST | `/api/certs/admin/upgrade-to-hardware-bound` | Management | Upgrade admin to hardware-bound |
|
|
331
|
+
| GET | `/api/certs/admin/auth-mode` | Management | Get admin auth mode |
|
|
332
|
+
| GET | `/api/settings/2fa` | Management | Get 2FA status |
|
|
333
|
+
| POST | `/api/settings/2fa/setup` | Management | Generate TOTP secret |
|
|
334
|
+
| POST | `/api/settings/2fa/confirm` | Management | Confirm initial code, enable 2FA |
|
|
335
|
+
| POST | `/api/settings/2fa/verify` | Management | Verify code, issue session cookie |
|
|
336
|
+
| POST | `/api/settings/2fa/disable` | Management | Disable 2FA |
|
|
337
|
+
| GET | `/api/services` | Management | List service statuses |
|
|
338
|
+
| POST | `/api/services/:name/:action` | Management | Control a service (start/stop/restart) |
|
|
339
|
+
| WS | `/api/services/:name/logs` | Management | Stream service logs |
|
|
340
|
+
| GET | `/api/plugins` | Management | List installed plugins |
|
|
341
|
+
| GET | `/api/plugins/:name` | Management | Get plugin details |
|
|
342
|
+
| POST | `/api/plugins/install` | Management | Install a plugin |
|
|
343
|
+
| POST | `/api/plugins/:name/enable` | Management | Enable a plugin |
|
|
344
|
+
| POST | `/api/plugins/:name/disable` | Management | Disable a plugin |
|
|
345
|
+
| DELETE | `/api/plugins/:name` | Management | Uninstall a plugin |
|
|
346
|
+
| GET | `/api/plugins/:name/oidc-clients` | Management | A plugin's OIDC clients (no secrets) |
|
|
347
|
+
| POST | `/api/plugins/:name/oidc-clients/:clientId/rotate-secret` | Management | New secret for a confidential client |
|
|
348
|
+
| GET | `/api/registry-credential` | Management | Registry credential summary |
|
|
349
|
+
| PUT | `/api/registry-credential` | Management | Set the registry credential (verified) |
|
|
350
|
+
| DELETE | `/api/registry-credential` | Management | Remove the registry credential |
|
|
351
|
+
| GET | `/api/plugins/push-install/config` | Management | Get push install configuration |
|
|
352
|
+
| PATCH | `/api/plugins/push-install/config` | Management | Update push install configuration |
|
|
353
|
+
| GET | `/api/plugins/push-install/policies` | Management | List push install policies |
|
|
354
|
+
| POST | `/api/plugins/push-install/policies` | Management | Create push install policy |
|
|
355
|
+
| PATCH | `/api/plugins/push-install/policies/:id` | Management | Update push install policy |
|
|
356
|
+
| DELETE | `/api/plugins/push-install/policies/:id` | Management | Delete push install policy |
|
|
357
|
+
| POST | `/api/plugins/push-install/enable/:label` | Management | Enable push install for agent |
|
|
358
|
+
| DELETE | `/api/plugins/push-install/enable/:label` | Management | Disable push install for agent |
|
|
359
|
+
| GET | `/api/plugins/push-install/agent-status` | Management | Agent checks own push install status |
|
|
360
|
+
| POST | `/api/plugins/push-install/:label` | Management | Send push install command to agent |
|
|
361
|
+
| GET | `/api/plugins/push-install/sessions` | Management | List push install audit log |
|
|
362
|
+
| POST | `/api/tickets/scopes` | Management | Register ticket scope |
|
|
363
|
+
| GET | `/api/tickets/scopes` | Management | List scopes, instances, assignments |
|
|
364
|
+
| DELETE | `/api/tickets/scopes/:name` | Management | Delete ticket scope |
|
|
365
|
+
| POST | `/api/tickets/instances` | Management | Register instance |
|
|
366
|
+
| DELETE | `/api/tickets/instances/:instanceId` | Management | Deregister instance |
|
|
367
|
+
| POST | `/api/tickets/instances/:instanceId/heartbeat` | Management | Instance heartbeat |
|
|
368
|
+
| POST | `/api/tickets/assignments` | Management | Assign agent to instance |
|
|
369
|
+
| DELETE | `/api/tickets/assignments/:agentLabel/:instanceScope` | Management | Remove assignment |
|
|
370
|
+
| GET | `/api/tickets/assignments` | Management | List assignments |
|
|
371
|
+
| POST | `/api/tickets` | Management | Request ticket |
|
|
372
|
+
| GET | `/api/tickets/inbox` | Management | Check ticket inbox |
|
|
373
|
+
| POST | `/api/tickets/validate` | Management | Validate and consume ticket |
|
|
374
|
+
| GET | `/api/tickets` | Management | List all tickets (admin) |
|
|
375
|
+
| DELETE | `/api/tickets/:ticketId` | Management | Revoke ticket |
|
|
376
|
+
| POST | `/api/tickets/sessions` | Management | Create session from ticket |
|
|
377
|
+
| POST | `/api/tickets/sessions/:sessionId/heartbeat` | Management | Session heartbeat |
|
|
378
|
+
| PATCH | `/api/tickets/sessions/:sessionId` | Management | Update session status |
|
|
379
|
+
| DELETE | `/api/tickets/sessions/:sessionId` | Management | Kill session |
|
|
380
|
+
| GET | `/api/tickets/sessions` | Management | List sessions |
|
|
381
|
+
| POST | `/api/storage/servers` | Management | Register a storage server |
|
|
382
|
+
| GET | `/api/storage/servers` | Management | List registered storage servers |
|
|
383
|
+
| DELETE | `/api/storage/servers/:id` | Management | Remove a storage server |
|
|
384
|
+
| POST | `/api/storage/bindings` | Management | Bind storage to plugin |
|
|
385
|
+
| GET | `/api/storage/bindings` | Management | List all bindings |
|
|
386
|
+
| GET | `/api/storage/bindings/:pluginName` | Management | Get binding for plugin |
|
|
387
|
+
| DELETE | `/api/storage/bindings/:pluginName` | Management | Unbind storage from plugin |
|
|
382
388
|
|
|
383
389
|
### Agent Capabilities
|
|
384
390
|
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Registry Credential and OIDC Clients
|
|
2
|
+
|
|
3
|
+
Two generic mechanisms a plugin can build on, first used by Feria (the Lamalibre npm registry): the panel's **registry credential**, with which it brokers short-lived install tokens for agents, and **OIDC clients** that plugins register on the relay's Authelia.
|
|
4
|
+
|
|
5
|
+
## In Plain English
|
|
6
|
+
|
|
7
|
+
Agents install plugins with `npm install` from the Lamalibre registry. A networked registry accepts nothing anonymously, and an agent must never hold a long-lived registry credential. So the panel holds one credential for the registry and, each time an agent is about to install a plugin, asks the registry for a one-hour read-only token owned by that agent. The agent writes it to a throw-away npm configuration file for that one install and deletes it afterwards.
|
|
8
|
+
|
|
9
|
+
The panel itself can authenticate to the registry in two ways: with a long-lived token an administrator pasted in, or — the recommended way — as its own **service principal**: an OpenID Connect client on the relay's Authelia named `lamaste-server`. In that mode nothing long-lived is pasted anywhere; the panel proves its identity to Authelia with a secret it generated itself, Authelia issues a short-lived JWT, and the registry exchanges that JWT for a registry token.
|
|
10
|
+
|
|
11
|
+
Plugins use the same Authelia to let people and programs log in. A plugin's manifest declares its OIDC clients — a CLI that logs in with the device-code flow, a service that authenticates with client credentials — and the panel writes them into Authelia's configuration when the plugin is enabled.
|
|
12
|
+
|
|
13
|
+
## Plugin manifest: `oidcClients`
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"name": "feria",
|
|
18
|
+
"oidcClients": [
|
|
19
|
+
{
|
|
20
|
+
"id": "feria-cli",
|
|
21
|
+
"name": "Feria CLI",
|
|
22
|
+
"grantTypes": ["urn:ietf:params:oauth:grant-type:device_code"],
|
|
23
|
+
"scopes": ["openid", "profile", "groups"],
|
|
24
|
+
"audience": ["https://feria.{domain}"],
|
|
25
|
+
"accessTokenClaims": ["groups", "preferred_username"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "feria-ci",
|
|
29
|
+
"confidential": true,
|
|
30
|
+
"grantTypes": ["client_credentials"],
|
|
31
|
+
"scopes": ["registry_publish"],
|
|
32
|
+
"audience": ["https://feria.{domain}"]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Field | Default | Meaning |
|
|
39
|
+
| ------------------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `id` | — | Client id. Must be the plugin's route (`feria` for `@lamalibre/feria-server`) or `<route>-<suffix>`; one plugin cannot claim another's client |
|
|
41
|
+
| `name` | `<plugin> (<id>)` | Shown on Authelia's consent screen |
|
|
42
|
+
| `grantTypes` | — | Any of `urn:ietf:params:oauth:grant-type:device_code`, `authorization_code`, `refresh_token`, `client_credentials`. `client_credentials` requires `confidential: true`; `authorization_code` requires `redirectUris`. A client whose only grant is `client_credentials` may not have `openid` or `offline_access` |
|
|
43
|
+
| `scopes` | `["openid"]` | Scopes the client may request (lowercase letters and underscores) |
|
|
44
|
+
| `audience` | `[]` | Audiences the client may request; `{domain}` becomes the relay domain. Clients must request an audience explicitly (`requested_audience_mode: explicit`) |
|
|
45
|
+
| `redirectUris` | `[]` | For `authorization_code`; `{domain}` is expanded |
|
|
46
|
+
| `accessTokenSignedResponseAlg` | `RS256` | `RS256` makes access tokens JWTs (RFC 9068) a resource server verifies against Authelia's JWKS; `none` keeps them opaque |
|
|
47
|
+
| `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 |
|
|
48
|
+
| `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 |
|
|
49
|
+
| `requirePkce` | `false` | Require PKCE on authorization-code requests |
|
|
50
|
+
| `confidential` | `false` | A confidential client authenticates to the token endpoint with a secret (`client_secret_basic`). Lamaste generates the secret |
|
|
51
|
+
|
|
52
|
+
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: [...]`.
|
|
53
|
+
|
|
54
|
+
### Secrets of confidential clients
|
|
55
|
+
|
|
56
|
+
The panel mints a 32-byte secret when a confidential client first appears (at install — the secret belongs to an installed plugin, enabled or not), stores the plaintext in `/etc/lamalibre/lamaste/oidc-clients.json` (0600) and writes only the digest into Authelia. The plaintext is shown exactly once: in the response of the `POST /api/plugins/install` (or `enable`) call that created it, as `oidcClients: [{ id, secret }]`, and again by `rotate-secret`. Uninstalling the plugin deletes the secret; disabling keeps it.
|
|
57
|
+
|
|
58
|
+
### Where the clients are served
|
|
59
|
+
|
|
60
|
+
Authelia serves its OpenID Connect provider at `https://auth.<domain>`: `/.well-known/openid-configuration`, `/jwks.json`, `/api/oidc/device-authorization`, `/api/oidc/token`, `/api/oidc/userinfo`. The section is written only while at least one client exists — Authelia refuses an `oidc` section without clients.
|
|
61
|
+
|
|
62
|
+
Tokens are signed with an RSA-4096 key at `/etc/authelia/oidc-issuer.pem`, generated by `create-lamaste` (fresh install and every redeploy that finds it missing), owned by the `lamaste-authelia` account, mode 0600. The panel, which writes Authelia's configuration, cannot read it: the configuration references the key with a template expression — `key: {{ secret "/etc/authelia/oidc-issuer.pem" | mindent 10 "|" | msquote }}` — which Authelia expands at load time because its unit sets `X_AUTHELIA_CONFIG_FILTERS=template`. The whole file is then a Go template, so the panel refuses to write any value containing `{{` or `}}`.
|
|
63
|
+
|
|
64
|
+
### Agent-hosted plugins: clients without code on the relay
|
|
65
|
+
|
|
66
|
+
An application that runs on its own agent, behind a Lamaste tunnel, still needs its clients on the relay's Authelia — Feria's `feria-cli` is the first. Its manifest says so with `"hosting": "agent"` (default `"server"`):
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"name": "feria",
|
|
71
|
+
"version": "0.3.1",
|
|
72
|
+
"hosting": "agent",
|
|
73
|
+
"oidcClients": [
|
|
74
|
+
{
|
|
75
|
+
"id": "feria-cli",
|
|
76
|
+
"grantTypes": ["urn:ietf:params:oauth:grant-type:device_code"],
|
|
77
|
+
"scopes": ["openid", "profile", "groups"],
|
|
78
|
+
"audience": ["https://feria.{domain}"],
|
|
79
|
+
"accessTokenClaims": ["groups", "preferred_username"]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
It is installed like any plugin (`POST /api/plugins/install` runs `npm install --ignore-scripts` and reads `<package>/lamaste-plugin.json`), but Lamaste never imports the package — not the panel, and not an agent's plugin host if the package is installed there. Nothing is mounted under `/api/<name>/` (requests there answer `404`), no panel bundle is served, and no storage can be bound to it (`400`). A `panel` declaration is a manifest error (`400` on install). What takes effect is what the manifest declares: its `oidcClients` are written to Authelia on enable and removed on disable or uninstall, confidential-client secrets are minted and shown exactly as above, and its `capabilities` can be granted to agents. `GET /api/plugins` reports each plugin's `hosting`; a plugin installed before 4.2 is `"server"`.
|
|
86
|
+
|
|
87
|
+
The application's own tunnel (`feria.<domain>`) is created by the administrator as for any app; Lamaste does not create it from the manifest.
|
|
88
|
+
|
|
89
|
+
## Plugin OIDC client endpoints
|
|
90
|
+
|
|
91
|
+
### `GET /api/plugins/:name/oidc-clients`
|
|
92
|
+
|
|
93
|
+
Admin only. The clients a plugin declares, with whether they are served (plugin enabled) and, for confidential clients, when the secret was created and last rotated. Never the secret.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"clients": [
|
|
98
|
+
{
|
|
99
|
+
"id": "feria-ci",
|
|
100
|
+
"name": null,
|
|
101
|
+
"confidential": true,
|
|
102
|
+
"grantTypes": ["client_credentials"],
|
|
103
|
+
"scopes": ["registry_publish"],
|
|
104
|
+
"audience": ["https://feria.{domain}"],
|
|
105
|
+
"accessTokenClaims": [],
|
|
106
|
+
"enabled": true,
|
|
107
|
+
"secretCreatedAt": "2026-10-13T10:00:00.000Z",
|
|
108
|
+
"secretRotatedAt": null
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `POST /api/plugins/:name/oidc-clients/:clientId/rotate-secret`
|
|
115
|
+
|
|
116
|
+
Admin only, 10 per minute. Replaces a confidential client's secret in the store and in Authelia (restarted). The response is the only place the new secret appears.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"ok": true,
|
|
121
|
+
"id": "feria-ci",
|
|
122
|
+
"plugin": "feria",
|
|
123
|
+
"secret": "…",
|
|
124
|
+
"rotatedAt": "2026-10-13T11:00:00.000Z"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`400` for a public client, `404` for a client the plugin does not declare.
|
|
129
|
+
|
|
130
|
+
CLI: `lamaste-server plugins oidc-clients <name>` and `lamaste-server plugins rotate-secret <name> <clientId>`. Every `lamaste-server plugins` subcommand goes through the running panel — `install`, `enable`, `disable` and `uninstall` call the routes above, so a plugin the CLI installs is the one the panel serves and its clients reach Authelia — and `lamaste-server plugins list` shows each plugin's hosting.
|
|
131
|
+
|
|
132
|
+
## Registry credential endpoints
|
|
133
|
+
|
|
134
|
+
### `GET /api/registry-credential`
|
|
135
|
+
|
|
136
|
+
Admin only. What is configured — never a token.
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"configured": true,
|
|
141
|
+
"provider": "feria",
|
|
142
|
+
"registryUrl": "https://feria.example.com",
|
|
143
|
+
"scope": "@lamalibre",
|
|
144
|
+
"auth": "oidc",
|
|
145
|
+
"clientId": "lamaste-server",
|
|
146
|
+
"owner": "ci:auth.example.com/lamaste-server",
|
|
147
|
+
"configuredAt": "2026-10-13T10:00:00.000Z"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`{ "configured": false }` when none is set.
|
|
152
|
+
|
|
153
|
+
### `PUT /api/registry-credential`
|
|
154
|
+
|
|
155
|
+
Admin only, 10 per minute. Verifies the whole chain against the registry, then stores the credential at `/etc/lamalibre/lamaste/registry-credential.json` (0600).
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{ "registryUrl": "https://feria.example.com", "auth": { "kind": "oidc" } }
|
|
159
|
+
{ "registryUrl": "https://feria.example.com", "auth": { "kind": "token", "token": "feria_…" }, "scope": "@lamalibre" }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- `auth.kind: "token"` — a long-lived Feria token. The registry's `/-/whoami` must report the `admin` scope (the broker route requires it).
|
|
163
|
+
- `auth.kind: "oidc"` — the panel's own service principal. The panel first makes sure Authelia carries the confidential client `lamaste-server` (grant `client_credentials`, `token_endpoint_auth_method: client_secret_basic`, scope `lamaste.registry` — defined under `identity_providers.oidc.scopes` —, audience = the registry URL without a trailing slash, RS256 JWT access tokens; secret in `oidc-clients.json`). It then obtains a JWT from Authelia's token endpoint (`grant_type=client_credentials&scope=lamaste.registry&audience=<registryUrl>`), posts it to `<registryUrl>/-/feria/login`, and checks that `/-/whoami` reports `admin`. If any step fails the client is removed again and the error says what is missing — typically that Feria's `auth.json` needs a `rules` issuer for `https://auth.<domain>` with audience `<registryUrl>` granting the `admin` scope when the `client_id` claim (or `sub`, which carries the same value) is `lamaste-server`.
|
|
164
|
+
|
|
165
|
+
Response: as `GET`. Errors: `400` (no admin scope), `502` (registry or Authelia refused or unreachable), `503` (no domain yet).
|
|
166
|
+
|
|
167
|
+
The Authelia call goes to `127.0.0.1:9091` with `X-Forwarded-Proto: https` and `X-Forwarded-Host: auth.<domain>`, which is how Authelia derives the issuer — the JWT says `iss: https://auth.<domain>`, exactly as it would through nginx. In the JWT, Authelia sets both `sub` and `client_id` to the client id for the client-credentials flow (there is no end user).
|
|
168
|
+
|
|
169
|
+
With `oidc`, the Feria token the panel obtains is cached in the panel process until five minutes before it expires, then obtained again. Changing or removing the credential drops it, and every agent's cached token.
|
|
170
|
+
|
|
171
|
+
### `DELETE /api/registry-credential`
|
|
172
|
+
|
|
173
|
+
Admin only. Removes the credential; with `oidc`, also removes the `lamaste-server` client from Authelia and deletes its secret.
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{ "configured": false, "removed": true }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `GET /api/agents/me/registry-credential`
|
|
180
|
+
|
|
181
|
+
Agent certificates only; identity is the certificate label, never a parameter. Returns a read-only registry token owned by `agent:<label>`, valid for one hour, which the agent daemon writes to an isolated npm userconfig (`npm_config_userconfig`) for one `npm install` and deletes afterwards.
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"registryUrl": "https://feria.example.com/",
|
|
186
|
+
"scope": "@lamalibre",
|
|
187
|
+
"token": "feria_…",
|
|
188
|
+
"expiresAt": "2026-10-13T11:00:00.000Z",
|
|
189
|
+
"tokenId": "7c1e…"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- The token is cached per agent until five minutes before it expires, so every install within the hour gets the same one.
|
|
194
|
+
- An agent may be issued at most **ten new tokens per hour**; beyond that the route answers `429` with `Retry-After` (seconds) and `retryAfterSeconds`. The per-IP limit (30 per minute) remains as a backstop.
|
|
195
|
+
- `403` for any other certificate — an admin certificate has no agent identity to broker for (plugin-agent certificates get `400`); `404` when no registry credential is configured (the agent installs from the public registry as before); `502` when the registry refuses.
|
|
196
|
+
|
|
197
|
+
CLI: `lamaste-server registry-credential show | set --url <registry> (--oidc | --token-stdin) | clear`. The token for `--token-stdin` is read from stdin (or `LAMASTE_REGISTRY_TOKEN`), never from an argument.
|
|
198
|
+
|
|
199
|
+
### Group changes
|
|
200
|
+
|
|
201
|
+
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.
|
|
202
|
+
|
|
203
|
+
## Related
|
|
204
|
+
|
|
205
|
+
- [Authentication (Authelia)](../01-concepts/authentication.md) — the OpenID Connect provider configuration
|
|
206
|
+
- [Configuration Files](../06-reference/config-files.md) — `registry-credential.json`, `oidc-clients.json`, `oidc-issuer.pem`
|
|
207
|
+
- [Installer](../03-architecture/installer.md) — how the issuer key is provisioned
|
|
@@ -197,7 +197,7 @@ sudo chown lamaste-authelia:lamaste-authelia /etc/authelia/db.sqlite3* /etc/auth
|
|
|
197
197
|
sudo chmod 600 /etc/authelia/db.sqlite3* /etc/authelia/notifications.txt
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
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.
|
|
200
|
+
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.2.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)).
|
|
201
201
|
|
|
202
202
|
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.
|
|
203
203
|
|
|
@@ -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.2.0 --uninstall
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This prints the same steps listed below.
|
|
@@ -25,7 +25,7 @@ When a release changes both sides, upgrade every **agent before the server**. Ag
|
|
|
25
25
|
On each agent machine:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install -g @lamalibre/lamaste-agent@4.
|
|
28
|
+
npm install -g @lamalibre/lamaste-agent@4.2.0
|
|
29
29
|
lamaste-agent update
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -57,6 +57,7 @@ The `@latest` tag ensures you get the newest published version. The `--yes` flag
|
|
|
57
57
|
- It stops the panel and the gatekeeper, and makes `/opt/lamalibre/lamaste/` root-owned: a tree left `lamaste`-owned by an earlier version is moved aside, every component (panel server, `lamaste-server` CLI, UI, docs, gatekeeper, certificate help page) is deployed fresh into a root-owned directory, and the old tree is removed at the end
|
|
58
58
|
- It rewrites `panel.json` (merged, as a fresh file renamed into place — root never writes through a path the `lamaste` user controls)
|
|
59
59
|
- It installs the pinned Chisel and Authelia releases if they are not already installed (a newer Authelia is kept), writes their systemd units, creates the `lamaste-chisel` group and the `lamaste-authelia` account, and sets the ownership described in [Configuration Files](../06-reference/config-files.md#file-permissions-table): `/var/www/lamaste` becomes `lamaste:www-data`, `/etc/authelia` `lamaste:lamaste-authelia`, the Chisel key `lamaste:lamaste-chisel 0640`, and the PKI directory `lamaste`'s. A running Authelia is stopped while its files change hands and started again afterwards
|
|
60
|
+
- It generates the OpenID Connect issuer key `/etc/authelia/oidc-issuer.pem` (0600, `lamaste-authelia`) if it is missing, and the Authelia unit it writes enables the `template` configuration filter. A 4.0 server updating to 4.1 therefore has the key before the new panel starts; plugin OIDC clients and the `--oidc` registry credential need it
|
|
60
61
|
- It rewrites the panel's systemd unit and the sudoers rules (checked with `visudo -c` before they replace the old file), installs the root-owned programs in `/usr/local/sbin/` (`lamaste-priv`, `lamaste-certbot`, `lamaste-cert-info`) and removes the retired `lamaste-sign-csr` and `lamaste-pki-rename`, and installs the port-80 redirect site
|
|
61
62
|
- It restarts Chisel if its binary or unit changed, starts the gatekeeper if it was running, and restarts `lamalibre-lamaste-serverd`, whose startup reconciliation rewrites Chisel's authfile
|
|
62
63
|
|
|
@@ -78,7 +79,7 @@ exit
|
|
|
78
79
|
|
|
79
80
|
### Updating Chisel
|
|
80
81
|
|
|
81
|
-
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.
|
|
82
|
+
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.2.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.
|
|
82
83
|
|
|
83
84
|
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:
|
|
84
85
|
|
|
@@ -92,7 +93,7 @@ Tunnel clients reconnect on their own after the brief interruption (they retry a
|
|
|
92
93
|
|
|
93
94
|
### Updating Authelia
|
|
94
95
|
|
|
95
|
-
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.
|
|
96
|
+
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.2.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.
|
|
96
97
|
|
|
97
98
|
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.
|
|
98
99
|
|
|
@@ -231,7 +232,7 @@ The panel server, CLI, client, docs and gatekeeper are always redeployed (files
|
|
|
231
232
|
|
|
232
233
|
### Version Pinning
|
|
233
234
|
|
|
234
|
-
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.
|
|
235
|
+
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.2.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.
|
|
235
236
|
|
|
236
237
|
### Automated Updates
|
|
237
238
|
|
|
@@ -253,7 +254,7 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
|
|
253
254
|
| Panel server | `/opt/lamalibre/lamaste/lamaste-serverd/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
254
255
|
| Panel client | `/opt/lamalibre/lamaste/lamaste-server-ui/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
255
256
|
| Chisel | `/usr/local/bin/chisel` | Pinned; installed by `create-lamaste` |
|
|
256
|
-
| Agents | `npm install -g @lamalibre/lamaste-agent@4.
|
|
257
|
+
| Agents | `npm install -g @lamalibre/lamaste-agent@4.2.0` | Upgrade before the server, then `lamaste-agent update` |
|
|
257
258
|
| Authelia | `/usr/local/bin/authelia` | Pinned; installed by `create-lamaste` (newer is kept) |
|
|
258
259
|
| nginx | System package | `sudo apt-get update && sudo apt-get upgrade` |
|
|
259
260
|
| certbot | System package | `sudo apt-get update && sudo apt-get upgrade` |
|