@lamalibre/create-lamaste 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +118 -0
- package/README.md +114 -0
- package/bin/create-lamaste.js +15 -0
- package/package.json +54 -0
- package/scripts/bundle-vendor.js +110 -0
- package/scripts/lamaste-cert-info +62 -0
- package/scripts/lamaste-certbot +96 -0
- package/scripts/lamaste-priv +693 -0
- package/src/index.js +646 -0
- package/src/lib/accounts.js +86 -0
- package/src/lib/binaries.js +137 -0
- package/src/lib/cert-help-page.js +160 -0
- package/src/lib/env.js +132 -0
- package/src/lib/http-redirect.js +38 -0
- package/src/lib/ownership.js +231 -0
- package/src/lib/relay-services.js +103 -0
- package/src/lib/secrets.js +19 -0
- package/src/lib/service-config.js +311 -0
- package/src/lib/summary.js +131 -0
- package/src/lib/wrappers.js +63 -0
- package/src/tasks/gatekeeper.js +233 -0
- package/src/tasks/harden.js +293 -0
- package/src/tasks/mtls.js +193 -0
- package/src/tasks/nginx.js +265 -0
- package/src/tasks/node.js +110 -0
- package/src/tasks/panel.js +408 -0
- package/src/tasks/redeploy.js +438 -0
- package/vendor/docs/00-introduction/how-it-works.md +447 -0
- package/vendor/docs/00-introduction/quickstart.md +472 -0
- package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
- package/vendor/docs/01-concepts/authentication.md +464 -0
- package/vendor/docs/01-concepts/authorization.md +242 -0
- package/vendor/docs/01-concepts/certificates.md +452 -0
- package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
- package/vendor/docs/01-concepts/mtls.md +490 -0
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
- package/vendor/docs/01-concepts/security-model.md +600 -0
- package/vendor/docs/01-concepts/tickets.md +414 -0
- package/vendor/docs/01-concepts/tunneling.md +367 -0
- package/vendor/docs/02-guides/agent-setup.md +194 -0
- package/vendor/docs/02-guides/certificate-management.md +341 -0
- package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
- package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
- package/vendor/docs/02-guides/disaster-recovery.md +464 -0
- package/vendor/docs/02-guides/first-tunnel.md +230 -0
- package/vendor/docs/02-guides/installation.md +393 -0
- package/vendor/docs/02-guides/managing-users.md +255 -0
- package/vendor/docs/02-guides/onboarding.md +244 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
- package/vendor/docs/02-guides/static-sites.md +364 -0
- package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
- package/vendor/docs/03-architecture/installer-flow.md +151 -0
- package/vendor/docs/03-architecture/installer.md +445 -0
- package/vendor/docs/03-architecture/management-flow.md +288 -0
- package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
- package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
- package/vendor/docs/03-architecture/overview.md +541 -0
- package/vendor/docs/03-architecture/panel-client.md +416 -0
- package/vendor/docs/03-architecture/panel-server.md +695 -0
- package/vendor/docs/03-architecture/state-management.md +529 -0
- package/vendor/docs/03-architecture/system-overview.md +167 -0
- package/vendor/docs/04-api-reference/certificates.md +1141 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
- package/vendor/docs/04-api-reference/onboarding.md +473 -0
- package/vendor/docs/04-api-reference/overview.md +404 -0
- package/vendor/docs/04-api-reference/services.md +267 -0
- package/vendor/docs/04-api-reference/settings.md +343 -0
- package/vendor/docs/04-api-reference/sites.md +716 -0
- package/vendor/docs/04-api-reference/system.md +268 -0
- package/vendor/docs/04-api-reference/tickets.md +618 -0
- package/vendor/docs/04-api-reference/tunnels.md +417 -0
- package/vendor/docs/04-api-reference/users.md +333 -0
- package/vendor/docs/05-operations/backup-and-restore.md +342 -0
- package/vendor/docs/05-operations/monitoring.md +285 -0
- package/vendor/docs/05-operations/uninstalling.md +344 -0
- package/vendor/docs/05-operations/upgrades.md +270 -0
- package/vendor/docs/06-reference/config-files.md +1080 -0
- package/vendor/docs/06-reference/glossary.md +528 -0
- package/vendor/docs/06-reference/installer-flags.md +260 -0
- package/vendor/docs/06-reference/ports-and-services.md +244 -0
- package/vendor/docs/06-reference/troubleshooting.md +665 -0
- package/vendor/docs/_index.json +233 -0
- package/vendor/docs/gatekeeper-design.md +953 -0
- package/vendor/docs/package.json +15 -0
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
- package/vendor/gatekeeper/dist/boot.d.ts +1 -0
- package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/boot.js +17 -0
- package/vendor/gatekeeper/dist/boot.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.js +45 -0
- package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
- package/vendor/gatekeeper/dist/index.d.ts +9 -0
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/index.js +13 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.js +42 -0
- package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.js +79 -0
- package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.js +314 -0
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.js +247 -0
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.js +79 -0
- package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
- package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.js +151 -0
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.js +2 -0
- package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
- package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/index.js +251 -0
- package/vendor/gatekeeper/dist/server/index.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
- package/vendor/gatekeeper/package.json +49 -0
- package/vendor/server/bin/lamaste-server.js +204 -0
- package/vendor/server/package.json +31 -0
- package/vendor/server/src/commands/certs.js +224 -0
- package/vendor/server/src/commands/chisel.js +132 -0
- package/vendor/server/src/commands/logs.js +49 -0
- package/vendor/server/src/commands/plugins.js +321 -0
- package/vendor/server/src/commands/reset-admin.js +478 -0
- package/vendor/server/src/commands/restart.js +94 -0
- package/vendor/server/src/commands/sites.js +257 -0
- package/vendor/server/src/commands/status.js +158 -0
- package/vendor/server/src/commands/tunnels.js +365 -0
- package/vendor/server/src/commands/uninstall.js +326 -0
- package/vendor/server/src/config.js +38 -0
- package/vendor/server/src/exec.js +26 -0
- package/vendor/server/src/logger.js +39 -0
- package/vendor/server/src/ndjson.js +56 -0
- package/vendor/server/src/panel-api.js +85 -0
- package/vendor/server/src/state.js +45 -0
- package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
- package/vendor/server-ui/dist/index.html +13 -0
- package/vendor/serverd/package.json +44 -0
- package/vendor/serverd/src/boot.js +13 -0
- package/vendor/serverd/src/index.js +191 -0
- package/vendor/serverd/src/lib/app-error.js +14 -0
- package/vendor/serverd/src/lib/authelia.js +85 -0
- package/vendor/serverd/src/lib/certbot.js +74 -0
- package/vendor/serverd/src/lib/chisel-args.js +6 -0
- package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
- package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
- package/vendor/serverd/src/lib/chisel-users.js +109 -0
- package/vendor/serverd/src/lib/chisel.js +81 -0
- package/vendor/serverd/src/lib/config.js +190 -0
- package/vendor/serverd/src/lib/constants.js +8 -0
- package/vendor/serverd/src/lib/csr-signing.js +331 -0
- package/vendor/serverd/src/lib/enrollment.js +302 -0
- package/vendor/serverd/src/lib/files.js +19 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
- package/vendor/serverd/src/lib/invite-page.js +281 -0
- package/vendor/serverd/src/lib/ip-utils.js +79 -0
- package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
- package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
- package/vendor/serverd/src/lib/mtls.js +795 -0
- package/vendor/serverd/src/lib/nginx.js +1090 -0
- package/vendor/serverd/src/lib/pki-paths.js +41 -0
- package/vendor/serverd/src/lib/pki-sign.js +103 -0
- package/vendor/serverd/src/lib/plugins.js +538 -0
- package/vendor/serverd/src/lib/push-install.js +390 -0
- package/vendor/serverd/src/lib/revocation.js +112 -0
- package/vendor/serverd/src/lib/services.js +131 -0
- package/vendor/serverd/src/lib/session.js +151 -0
- package/vendor/serverd/src/lib/state-db.js +130 -0
- package/vendor/serverd/src/lib/state.js +140 -0
- package/vendor/serverd/src/lib/storage.js +63 -0
- package/vendor/serverd/src/lib/system-stats.js +58 -0
- package/vendor/serverd/src/lib/tickets.js +1556 -0
- package/vendor/serverd/src/lib/totp.js +76 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
- package/vendor/serverd/src/lib/user-access-session.js +142 -0
- package/vendor/serverd/src/lib/user-access.js +52 -0
- package/vendor/serverd/src/middleware/errors.js +63 -0
- package/vendor/serverd/src/middleware/mtls.js +120 -0
- package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
- package/vendor/serverd/src/middleware/role-guard.js +58 -0
- package/vendor/serverd/src/middleware/twofa-session.js +65 -0
- package/vendor/serverd/src/middleware/user-access-session.js +52 -0
- package/vendor/serverd/src/routes/enrollment.js +120 -0
- package/vendor/serverd/src/routes/health.js +22 -0
- package/vendor/serverd/src/routes/invite.js +163 -0
- package/vendor/serverd/src/routes/management/agents.js +182 -0
- package/vendor/serverd/src/routes/management/certs.js +911 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
- package/vendor/serverd/src/routes/management/identity.js +107 -0
- package/vendor/serverd/src/routes/management/invitations.js +166 -0
- package/vendor/serverd/src/routes/management/logs.js +218 -0
- package/vendor/serverd/src/routes/management/plugins.js +560 -0
- package/vendor/serverd/src/routes/management/services.js +63 -0
- package/vendor/serverd/src/routes/management/settings.js +325 -0
- package/vendor/serverd/src/routes/management/sites.js +522 -0
- package/vendor/serverd/src/routes/management/storage.js +192 -0
- package/vendor/serverd/src/routes/management/system.js +201 -0
- package/vendor/serverd/src/routes/management/tickets.js +457 -0
- package/vendor/serverd/src/routes/management/tunnels.js +709 -0
- package/vendor/serverd/src/routes/management/users.js +377 -0
- package/vendor/serverd/src/routes/management.js +36 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
- package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
- package/vendor/serverd/src/routes/onboarding/index.js +18 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
- package/vendor/serverd/src/routes/onboarding/status.js +12 -0
- package/vendor/serverd/src/routes/plugin-router.js +79 -0
- package/vendor/serverd/src/routes/user-access.js +389 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Tickets (Agent-to-Agent Authorization)
|
|
2
|
+
|
|
3
|
+
> Tickets are the authorization mechanism that allows one agent to securely communicate with another agent through Lamaste's panel-mediated system, using scoped, time-limited, single-use tokens.
|
|
4
|
+
|
|
5
|
+
## In Plain English
|
|
6
|
+
|
|
7
|
+
Imagine two of your machines — a laptop and a desktop — both connected to Lamaste as agents. You want the laptop to open a remote shell on the desktop. The laptop cannot simply connect directly; it needs permission. Tickets are how Lamaste grants that permission.
|
|
8
|
+
|
|
9
|
+
The process works like getting a visitor badge at an office building:
|
|
10
|
+
|
|
11
|
+
1. **The receptionist knows who works here** — the panel knows which agents are registered and what they can do (scopes and instances).
|
|
12
|
+
2. **A visitor requests access** — the source agent asks the panel for a ticket to reach a specific instance on the target agent.
|
|
13
|
+
3. **The receptionist checks the list** — the panel verifies both agents have the right capabilities and the target is assigned to that instance.
|
|
14
|
+
4. **A one-time badge is issued** — the panel creates a ticket that expires in 30 seconds and can only be used once.
|
|
15
|
+
5. **The badge is verified at the door** — the target agent validates the ticket with the panel, consuming it.
|
|
16
|
+
6. **A session is established** — once the ticket is consumed, a persistent session tracks the connection with regular heartbeat re-validation.
|
|
17
|
+
|
|
18
|
+
## For Users
|
|
19
|
+
|
|
20
|
+
### Key Concepts
|
|
21
|
+
|
|
22
|
+
**Scopes** define what types of interactions are possible. A scope is registered by an admin (or future: by a plugin installer) and declares named capabilities like `shell:connect` or `file:transfer`. Each scope also declares transport preferences (tunnel, relay, or direct).
|
|
23
|
+
|
|
24
|
+
**Instances** are live registrations of an agent offering a specific scope. When your desktop agent starts its shell service, it registers an instance for `shell:connect`. The panel assigns it a unique instance ID and tracks its liveness via heartbeats.
|
|
25
|
+
|
|
26
|
+
**Assignments** link agents to instances. The admin assigns your laptop agent to the desktop's shell instance, granting it permission to request tickets for that specific instance.
|
|
27
|
+
|
|
28
|
+
**Tickets** are the actual authorization tokens. They are 256-bit random values, valid for 30 seconds, single-use, and bound to a specific source, target, scope, and instance.
|
|
29
|
+
|
|
30
|
+
**Sessions** track active connections after a ticket is consumed. The panel re-validates authorization on every heartbeat (every 60 seconds), checking that both agents' certificates are still valid, their capabilities are still present, and the assignment still exists.
|
|
31
|
+
|
|
32
|
+
### Two-Layer Isolation
|
|
33
|
+
|
|
34
|
+
The ticket system enforces authorization at two layers, both mediated by the panel:
|
|
35
|
+
|
|
36
|
+
1. **Certificate capability check** — both the source and target agents must have the base scope capability on their certificates
|
|
37
|
+
2. **Ticket binding** — the source must own the instance, and the target must be assigned to it
|
|
38
|
+
|
|
39
|
+
A third layer (transport CA) is available plugin-side for end-to-end verification, but is not enforced by the panel.
|
|
40
|
+
|
|
41
|
+
### Managing Tickets in the Panel
|
|
42
|
+
|
|
43
|
+
The Tickets page in the admin panel has five tabs:
|
|
44
|
+
|
|
45
|
+
- **Scopes** — view registered scopes, their capabilities, and transport configuration. Delete scopes you no longer need.
|
|
46
|
+
- **Instances** — see which agents have registered instances, their liveness status (active, stale, dead), and deregister instances.
|
|
47
|
+
- **Assignments** — assign agents to instances (granting them permission to receive tickets) and remove assignments.
|
|
48
|
+
- **Tickets** — view pending and used tickets. Revoke tickets that should not be used.
|
|
49
|
+
- **Sessions** — monitor active sessions between agents. Kill sessions if needed.
|
|
50
|
+
|
|
51
|
+
### Instance Liveness
|
|
52
|
+
|
|
53
|
+
Instance owners send heartbeats every 60 seconds to keep their instances alive:
|
|
54
|
+
|
|
55
|
+
| Status | Condition | Effect |
|
|
56
|
+
| ------ | ---------------------- | ----------------------------------------------------- |
|
|
57
|
+
| Active | Heartbeat within 5 min | Tickets can be requested |
|
|
58
|
+
| Stale | No heartbeat for 5 min | New tickets rejected (503) |
|
|
59
|
+
| Dead | No heartbeat for 1 hr | Instance removed, assignments and sessions cleaned up |
|
|
60
|
+
|
|
61
|
+
### Rate Limits and Hard Caps
|
|
62
|
+
|
|
63
|
+
To protect the 512 MB server from resource exhaustion:
|
|
64
|
+
|
|
65
|
+
| Resource | Limit | Enforcement |
|
|
66
|
+
| ----------- | ---------------- | --------------------------- |
|
|
67
|
+
| Instances | 200 | 503 on registration attempt |
|
|
68
|
+
| Tickets | 1000 | 503 on request |
|
|
69
|
+
| Sessions | 500 | 503 on creation |
|
|
70
|
+
| Ticket rate | 10/min per agent | 429 on excess |
|
|
71
|
+
|
|
72
|
+
## For Developers
|
|
73
|
+
|
|
74
|
+
### Authorization Flow
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Source Agent Panel Target Agent
|
|
78
|
+
│ │ │
|
|
79
|
+
│ POST /tickets │ │
|
|
80
|
+
│ {scope, instanceId, │ │
|
|
81
|
+
│ target} │ │
|
|
82
|
+
│──────────────────────────▶│ │
|
|
83
|
+
│ │ ── validate capabilities │
|
|
84
|
+
│ │ ── check instance ownership│
|
|
85
|
+
│ │ ── check assignment │
|
|
86
|
+
│ │ ── rate limit check │
|
|
87
|
+
│ { ticket } │ │
|
|
88
|
+
│◀──────────────────────────│ │
|
|
89
|
+
│ │ │
|
|
90
|
+
│ (out-of-band ticket delivery) │
|
|
91
|
+
│──────────────────────────────────────────────────────▶│
|
|
92
|
+
│ │ │
|
|
93
|
+
│ │ POST /tickets/validate │
|
|
94
|
+
│ │ {ticketId} │
|
|
95
|
+
│ │◀───────────────────────────│
|
|
96
|
+
│ │ ── HMAC timing-safe compare │
|
|
97
|
+
│ │ ── mark as used │
|
|
98
|
+
│ │ { valid, transport } │
|
|
99
|
+
│ │───────────────────────────▶│
|
|
100
|
+
│ │ │
|
|
101
|
+
│ │ POST /tickets/sessions │
|
|
102
|
+
│ │ {ticketId} │
|
|
103
|
+
│ │◀───────────────────────────│
|
|
104
|
+
│ │ { session } │
|
|
105
|
+
│ │───────────────────────────▶│
|
|
106
|
+
│ │ │
|
|
107
|
+
│ (heartbeat loop) │
|
|
108
|
+
│ │ POST /sessions/:id/ │
|
|
109
|
+
│ │ heartbeat │
|
|
110
|
+
│ │◀───────────────────────────│
|
|
111
|
+
│ │ ── re-validate all layers │
|
|
112
|
+
│ │ { authorized: true } │
|
|
113
|
+
│ │───────────────────────────▶│
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Ticket Lifecycle
|
|
117
|
+
|
|
118
|
+
1. **Request** — source calls `POST /api/tickets` with scope, instanceId, and target agent label
|
|
119
|
+
2. **Multi-stage validation** — panel checks: source has capability, target has capability, source owns instance, instance is active (not stale/dead), source is not targeting itself, target is assigned to instance
|
|
120
|
+
3. **Issuance** — 256-bit random ticket ID (`crypto.randomBytes(32)`), 30-second expiry
|
|
121
|
+
4. **Delivery** — ticket appears in target's inbox (`GET /api/tickets/inbox`)
|
|
122
|
+
5. **Validation** — target calls `POST /api/tickets/validate` with the ticket ID; HMAC-based timing-safe comparison marks it as used atomically
|
|
123
|
+
6. **Session** — target reports session creation (`POST /api/tickets/sessions`) with only the ticket ID; the panel generates the session ID server-side (`crypto.randomBytes(16)`) and sets all timestamps server-side, then tracks with heartbeat re-validation
|
|
124
|
+
7. **Cleanup** — tickets expire after 1 hour (removed from store); dead sessions cleaned after 24 hours
|
|
125
|
+
|
|
126
|
+
### Server-Generated Session IDs and Timestamps
|
|
127
|
+
|
|
128
|
+
Session IDs are always generated server-side via `crypto.randomBytes(16).toString('hex')`. The client sends only the `ticketId` when creating a session; the server generates and returns the session ID. This guarantees uniqueness without trusting client input.
|
|
129
|
+
|
|
130
|
+
Similarly, `lastActivityAt` timestamps are always set server-side. The client cannot influence when a session was last active; heartbeats update the timestamp on the server when they arrive, not when the client claims to have sent them.
|
|
131
|
+
|
|
132
|
+
### Session Heartbeat Re-validation
|
|
133
|
+
|
|
134
|
+
Every heartbeat checks six conditions. If any fails, the session is terminated:
|
|
135
|
+
|
|
136
|
+
| Check | Termination reason |
|
|
137
|
+
| ----------------------- | -------------------- |
|
|
138
|
+
| Source cert revoked | `source_revoked` |
|
|
139
|
+
| Source lacks capability | `capability_removed` |
|
|
140
|
+
| Target cert revoked | `target_revoked` |
|
|
141
|
+
| Target lacks capability | `capability_removed` |
|
|
142
|
+
| Assignment removed | `assignment_removed` |
|
|
143
|
+
| Admin killed session | `admin_killed` |
|
|
144
|
+
|
|
145
|
+
### Information Leakage Prevention
|
|
146
|
+
|
|
147
|
+
Security-sensitive endpoints use generic error responses:
|
|
148
|
+
|
|
149
|
+
- `POST /api/tickets` — returns 404 for all authorization failures (no distinction between "target not found", "not assigned", or "instance dead"). The exception is stale instances, which return 503 to signal a retriable condition.
|
|
150
|
+
- `POST /api/tickets/validate` — returns 401 "Invalid ticket" for all failures (expired, used, wrong target, not found)
|
|
151
|
+
- `DELETE /api/tickets/instances/:id` — returns 404 for unauthorized deregistration attempts
|
|
152
|
+
|
|
153
|
+
Ticket validation uses HMAC-based timing-safe comparison to prevent timing attacks. Both the submitted and stored ticket IDs are HMAC-SHA256'd with a per-process random key before being compared with `crypto.timingSafeEqual`. The HMAC step produces fixed-length digests, eliminating the length-leak that raw `timingSafeEqual` would expose if inputs differed in length, and the per-process key prevents pre-computation attacks if an attacker gains read access to the source code.
|
|
154
|
+
|
|
155
|
+
### Host Validation (SSRF Prevention)
|
|
156
|
+
|
|
157
|
+
When an instance registers with a `direct` transport strategy, the `host` field is validated against a deny list of private, reserved, and metadata IP ranges. The following are rejected:
|
|
158
|
+
|
|
159
|
+
- Loopback: `localhost`, `127.0.0.1`, `::1`
|
|
160
|
+
- Private IPv4: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
|
|
161
|
+
- Link-local: `169.254.0.0/16`
|
|
162
|
+
- Cloud metadata endpoints: `169.254.169.254`, `metadata.google.internal`
|
|
163
|
+
- Zero network: `0.0.0.0/8`
|
|
164
|
+
|
|
165
|
+
This prevents agents from using the ticket system to probe internal services on the panel's network (SSRF).
|
|
166
|
+
|
|
167
|
+
### Capability Integration
|
|
168
|
+
|
|
169
|
+
Ticket scopes register capabilities dynamically. When a scope like `shell` declares `scopes: [{ name: 'shell:connect' }]`, the capability `shell:connect` becomes available for assignment to agent certificates alongside base capabilities (`tunnels:read`, etc.) and plugin capabilities.
|
|
170
|
+
|
|
171
|
+
The integration flow:
|
|
172
|
+
|
|
173
|
+
1. `POST /api/tickets/scopes` registers the scope
|
|
174
|
+
2. `refreshTicketScopeCapabilities()` extracts capability names
|
|
175
|
+
3. `setTicketScopeCapabilitiesOnMtls()` updates the mTLS module
|
|
176
|
+
4. `getValidCapabilities()` now includes ticket scope capabilities
|
|
177
|
+
5. Agent certs can be assigned the new capabilities via the Certificates page
|
|
178
|
+
|
|
179
|
+
### Concurrency and Persistence
|
|
180
|
+
|
|
181
|
+
- **Mutex** — all state mutations use a promise-chain mutex (same pattern as enrollment tokens)
|
|
182
|
+
- **Atomic writes** — temp file, fsync, rename pattern prevents corruption
|
|
183
|
+
- **State files** — `/etc/lamalibre/lamaste/ticket-scopes.json` (scope registry, instances, assignments) and `/etc/lamalibre/lamaste/tickets.json` (tickets, sessions), both mode `0600`
|
|
184
|
+
|
|
185
|
+
### Data Model
|
|
186
|
+
|
|
187
|
+
**Scope registry** (`ticket-scopes.json`):
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"scopes": [
|
|
192
|
+
{
|
|
193
|
+
"name": "shell",
|
|
194
|
+
"version": "1.0.0",
|
|
195
|
+
"description": "Remote shell access",
|
|
196
|
+
"scopes": [
|
|
197
|
+
{ "name": "shell:connect", "description": "Connect to shell", "instanceScoped": true }
|
|
198
|
+
],
|
|
199
|
+
"transport": {
|
|
200
|
+
"strategies": ["tunnel", "direct"],
|
|
201
|
+
"preferred": "tunnel",
|
|
202
|
+
"port": 9000,
|
|
203
|
+
"protocol": "wss"
|
|
204
|
+
},
|
|
205
|
+
"hooks": {}, // Reserved for future hook configuration
|
|
206
|
+
"installedAt": "2026-03-26T10:00:00.000Z"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"instances": [
|
|
210
|
+
{
|
|
211
|
+
"scope": "shell:connect",
|
|
212
|
+
"instanceId": "a7f3b2c9d1e2f3a4b5c6d7e8f9a0b1c2",
|
|
213
|
+
"agentLabel": "macbook-pro",
|
|
214
|
+
"registeredAt": "2026-03-26T10:05:00.000Z",
|
|
215
|
+
"lastHeartbeat": "2026-03-26T10:15:30.000Z",
|
|
216
|
+
"status": "active",
|
|
217
|
+
"transport": { "strategies": ["tunnel"], "preferred": "tunnel" }
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"assignments": [
|
|
221
|
+
{
|
|
222
|
+
"agentLabel": "linux-agent",
|
|
223
|
+
"instanceScope": "shell:connect:a7f3b2c9d1e2f3a4b5c6d7e8f9a0b1c2",
|
|
224
|
+
"assignedAt": "2026-03-26T10:10:00.000Z",
|
|
225
|
+
"assignedBy": "admin"
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Ticket store** (`tickets.json`):
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"tickets": [
|
|
236
|
+
{
|
|
237
|
+
"id": "64-hex-char-ticket-id",
|
|
238
|
+
"scope": "shell:connect",
|
|
239
|
+
"instanceId": "a7f3b2c9d1e2f3a4b5c6d7e8f9a0b1c2",
|
|
240
|
+
"source": "macbook-pro",
|
|
241
|
+
"target": "linux-agent",
|
|
242
|
+
"createdAt": "2026-03-26T10:15:00.000Z",
|
|
243
|
+
"expiresAt": "2026-03-26T10:15:30.000Z",
|
|
244
|
+
"used": false,
|
|
245
|
+
"usedAt": null,
|
|
246
|
+
"sessionId": null,
|
|
247
|
+
"transport": {}
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"sessions": [
|
|
251
|
+
{
|
|
252
|
+
"sessionId": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8",
|
|
253
|
+
"ticketId": "64-hex-char-ticket-id",
|
|
254
|
+
"scope": "shell:connect",
|
|
255
|
+
"instanceId": "a7f3b2c9d1e2f3a4b5c6d7e8f9a0b1c2",
|
|
256
|
+
"source": "macbook-pro",
|
|
257
|
+
"target": "linux-agent",
|
|
258
|
+
"createdAt": "2026-03-26T10:15:30.000Z",
|
|
259
|
+
"lastActivityAt": "2026-03-26T10:20:00.000Z",
|
|
260
|
+
"status": "active",
|
|
261
|
+
"reconnectGraceSeconds": 60
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Client SDK (`@lamalibre/lamaste-tickets@4.0.0`)
|
|
268
|
+
|
|
269
|
+
The `@lamalibre/lamaste-tickets@4.0.0` package is a TypeScript SDK that provides the client-side ticket lifecycle for plugins and agents. It uses `undici` for HTTP with mTLS dispatcher support and has no other runtime dependencies.
|
|
270
|
+
|
|
271
|
+
The SDK exports three main classes:
|
|
272
|
+
|
|
273
|
+
- **`TicketClient`** — low-level HTTP client for all ticket API endpoints. Handles mTLS authentication, response validation (`assertObject`/`assertField` checks before type assertions), and structured error reporting via `TicketHttpError` (carries the HTTP status code for retriable vs. permanent error distinction).
|
|
274
|
+
|
|
275
|
+
- **`TicketInstanceManager`** (source side) — manages the full instance lifecycle: creates the mTLS dispatcher, registers an instance for a scope, heartbeats it every 60 seconds, requests tickets with a per-agent cooldown (default 120 seconds) to avoid exhausting the global ticket cap, and auto-re-registers on 404 (instance expired). On `stop()`, it deregisters the instance from the panel immediately rather than waiting for the heartbeat timeout.
|
|
276
|
+
|
|
277
|
+
- **`TicketSessionManager`** (target side) — manages the session lifecycle: polls the ticket inbox for matching tickets (filtering by scope, discarding tickets with less than 5 seconds remaining TTL, picking the freshest), validates them, creates sessions, heartbeats every 60 seconds, transitions through `waiting` / `authorized` / `grace` / `terminated` / `stopped` states, and notifies the consuming plugin via an `onStateChange` callback.
|
|
278
|
+
|
|
279
|
+
The dispatcher factory (`createTicketDispatcher`) supports both PEM (cert + key + CA files) and P12 (single .p12 bundle) certificate configurations.
|
|
280
|
+
|
|
281
|
+
### Plugin Agent Delegation
|
|
282
|
+
|
|
283
|
+
When a standalone plugin (e.g., Sync) runs behind a Lamaste tunnel, its agents need a Lamaste identity to participate in the ticket system. Without one, the panel cannot verify capabilities, bind assignments, or issue tickets for those agents.
|
|
284
|
+
|
|
285
|
+
Delegated enrollment solves this. The plugin server — which already has a Lamaste agent certificate — vouches for its own agents by pre-announcing their enrollment to the panel.
|
|
286
|
+
|
|
287
|
+
#### Delegation Flow
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
Sync Server Lamaste Panel Sync Agent (RPi)
|
|
291
|
+
(CN=agent:macbook-pro) (no Lamaste cert yet)
|
|
292
|
+
│ │ │
|
|
293
|
+
│ POST /api/certs/agent/ │ │
|
|
294
|
+
│ enroll-delegated │ │
|
|
295
|
+
│ {pluginAgentLabel, scope} │ │
|
|
296
|
+
│─────────────────────────────▶│ │
|
|
297
|
+
│ │ ── store delegated token │
|
|
298
|
+
│ { enrollmentToken } │ │
|
|
299
|
+
│◀─────────────────────────────│ │
|
|
300
|
+
│ │ │
|
|
301
|
+
│ (Sync enrollment — agent token exchange) │
|
|
302
|
+
│◀─────────────────────────────────────────────────────────│
|
|
303
|
+
│ │ │
|
|
304
|
+
│ (pass delegated token │ │
|
|
305
|
+
│ to Sync agent) │ │
|
|
306
|
+
│─────────────────────────────────────────────────────────▶│
|
|
307
|
+
│ │ │
|
|
308
|
+
│ │ POST /api/enroll │
|
|
309
|
+
│ │ {CSR, delegatedToken} │
|
|
310
|
+
│ │◀───────────────────────────│
|
|
311
|
+
│ │ ── validate token │
|
|
312
|
+
│ │ ── issue minimal cert │
|
|
313
|
+
│ │ { cert } │
|
|
314
|
+
│ │ CN=plugin-agent: │
|
|
315
|
+
│ │ macbook-pro:rpi-sync │
|
|
316
|
+
│ │───────────────────────────▶│
|
|
317
|
+
│ │ │
|
|
318
|
+
│ (admin assigns capabilities │
|
|
319
|
+
│ and ticket assignments) │
|
|
320
|
+
│ │ │
|
|
321
|
+
│ │ POST /api/tickets │
|
|
322
|
+
│ │ {scope, instanceId, │
|
|
323
|
+
│ │ target} │
|
|
324
|
+
│ │◀───────────────────────────│
|
|
325
|
+
│ │ ── full ticket validation │
|
|
326
|
+
│ │ { ticket } │
|
|
327
|
+
│ │───────────────────────────▶│
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Certificate Format
|
|
331
|
+
|
|
332
|
+
Delegated certs use a three-part CN that encodes the delegation chain:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
CN=plugin-agent:<delegatingLabel>:<pluginAgentLabel>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
- `plugin-agent` — prefix distinguishing these from regular agents (`agent:`) and admin certs
|
|
339
|
+
- `<delegatingLabel>` — the Lamaste agent that vouched for this plugin agent (e.g., `macbook-pro`)
|
|
340
|
+
- `<pluginAgentLabel>` — the plugin's own name for the agent (e.g., `rpi-sync`)
|
|
341
|
+
|
|
342
|
+
#### Capabilities
|
|
343
|
+
|
|
344
|
+
Plugin-agent certs start with **no base capabilities**. They cannot manage tunnels, services, or sites. They can only participate in the ticket system for the scope declared during delegation.
|
|
345
|
+
|
|
346
|
+
The admin can upgrade capabilities via the Certificates page at any time. This is a deliberate opt-in — minimal privilege by default.
|
|
347
|
+
|
|
348
|
+
#### Pre-announcement Endpoint
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
POST /api/certs/agent/enroll-delegated
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
- Authenticated via the delegating agent's mTLS cert
|
|
355
|
+
- Request body includes the plugin agent name and the ticket scope
|
|
356
|
+
- Returns a one-time, time-limited delegated enrollment token
|
|
357
|
+
- The delegating agent passes this token to its plugin agent out-of-band
|
|
358
|
+
- The plugin agent submits the token alongside its CSR to `POST /api/enroll`
|
|
359
|
+
|
|
360
|
+
#### When Delegation Applies
|
|
361
|
+
|
|
362
|
+
Delegation only occurs when the plugin server detects it is running as a Lamaste agent (i.e., it has a Lamaste agent certificate). When running standalone on a direct IP without Lamaste, the plugin server mediates tickets on behalf of its agents using its own cert — no delegation needed.
|
|
363
|
+
|
|
364
|
+
See [Standalone Plugin with Tickets](../02-guides/standalone-plugin-with-tickets.md) for a full walkthrough.
|
|
365
|
+
|
|
366
|
+
### Source Files
|
|
367
|
+
|
|
368
|
+
| File | Purpose |
|
|
369
|
+
| ----------------------------------------------------------- | -------------------------------------------- |
|
|
370
|
+
| `packages/lamaste-serverd/src/lib/tickets.js` | Business logic, validation, state management |
|
|
371
|
+
| `packages/lamaste-serverd/src/routes/management/tickets.js` | HTTP route handlers |
|
|
372
|
+
| `packages/lamaste-server-ui/src/pages/Tickets.jsx` | Admin UI (5-tab interface) |
|
|
373
|
+
| `packages/lamaste-agent/src/lib/panel-api.js` | Agent-side API functions |
|
|
374
|
+
| `packages/lamaste-tickets/src/client.ts` | SDK: mTLS HTTP client for ticket API |
|
|
375
|
+
| `packages/lamaste-tickets/src/instance-manager.ts` | SDK: source-side instance lifecycle |
|
|
376
|
+
| `packages/lamaste-tickets/src/session-manager.ts` | SDK: target-side session lifecycle |
|
|
377
|
+
| `packages/lamaste-tickets/src/types.ts` | SDK: shared type definitions |
|
|
378
|
+
|
|
379
|
+
## Quick Reference
|
|
380
|
+
|
|
381
|
+
### Ticket properties
|
|
382
|
+
|
|
383
|
+
| Property | Value |
|
|
384
|
+
| ---------- | --------------------------------------------------------------- |
|
|
385
|
+
| ID length | 64 hex characters (256-bit) |
|
|
386
|
+
| Expiry | 30 seconds |
|
|
387
|
+
| Usage | Single-use |
|
|
388
|
+
| Comparison | HMAC-SHA256 + `crypto.timingSafeEqual` (per-process random key) |
|
|
389
|
+
| Rate limit | 10 per agent per minute |
|
|
390
|
+
|
|
391
|
+
### Instance lifecycle
|
|
392
|
+
|
|
393
|
+
| Event | Status change | Side effects |
|
|
394
|
+
| -------------- | ------------- | ---------------------------------------------------- |
|
|
395
|
+
| Registration | → active | Instance ID generated |
|
|
396
|
+
| Heartbeat | stays active | `lastHeartbeat` updated |
|
|
397
|
+
| 5 min no beat | → stale | New tickets rejected |
|
|
398
|
+
| 1 hr no beat | → dead | Removed; assignments, tickets, sessions cleaned |
|
|
399
|
+
| Deregistration | removed | Same cleanup as dead; SDK calls `DELETE` on `stop()` |
|
|
400
|
+
|
|
401
|
+
### Session states
|
|
402
|
+
|
|
403
|
+
| State | Meaning |
|
|
404
|
+
| ------ | ------------------------------------------------------------------------- |
|
|
405
|
+
| active | Connection is live, heartbeats succeeding |
|
|
406
|
+
| grace | Temporary disconnection, within reconnect window |
|
|
407
|
+
| dead | Terminated (admin kill, validation failure, or 10 min inactivity timeout) |
|
|
408
|
+
|
|
409
|
+
### Related documentation
|
|
410
|
+
|
|
411
|
+
- [Security Model](security-model.md) — defense-in-depth and capability-based access
|
|
412
|
+
- [Certificates](certificates.md) — agent certificate capabilities
|
|
413
|
+
- [Tickets API](../04-api-reference/tickets.md) — complete endpoint reference
|
|
414
|
+
- [Config Files](../06-reference/config-files.md) — ticket state file formats
|