@lamalibre/create-lamaste 4.2.0 → 4.3.1
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 +13 -8
- package/scripts/lamaste-priv +205 -2
- package/src/index.js +8 -4
- package/src/lib/node-version.js +114 -0
- package/src/lib/panel-pin.js +41 -0
- package/src/lib/relay-services.js +15 -1
- package/src/lib/service-config.js +113 -0
- package/src/tasks/gatekeeper.js +8 -3
- package/src/tasks/node.js +34 -75
- package/src/tasks/redeploy.js +36 -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 -3
- package/vendor/docs/01-concepts/authorization.md +29 -17
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +8 -3
- package/vendor/docs/01-concepts/security-model.md +110 -35
- 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 +34 -16
- package/vendor/docs/02-guides/certificate-management.md +2 -2
- package/vendor/docs/02-guides/cloud-provisioning.md +4 -4
- package/vendor/docs/02-guides/desktop-app-setup.md +11 -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 +4 -4
- package/vendor/docs/03-architecture/management-flow.md +1 -1
- package/vendor/docs/03-architecture/nginx-configuration.md +91 -26
- package/vendor/docs/03-architecture/overview.md +8 -8
- 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/agent-hosted-plugins.md +407 -0
- package/vendor/docs/04-api-reference/app-host.md +360 -0
- package/vendor/docs/04-api-reference/audit-and-notifications.md +57 -0
- package/vendor/docs/04-api-reference/certificates.md +95 -38
- package/vendor/docs/04-api-reference/gatekeeper.md +5 -3
- package/vendor/docs/04-api-reference/lifecycle-hooks.md +333 -0
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/overview.md +137 -111
- package/vendor/docs/04-api-reference/registry-and-oidc.md +8 -4
- package/vendor/docs/04-api-reference/tunnels.md +15 -13
- package/vendor/docs/05-operations/backup-and-restore.md +3 -1
- package/vendor/docs/05-operations/uninstalling.md +1 -1
- package/vendor/docs/05-operations/upgrades.md +25 -20
- package/vendor/docs/06-reference/config-files.md +195 -97
- 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 +33 -8
- package/vendor/docs/_index.json +20 -0
- package/vendor/docs/gatekeeper-design.md +22 -19
- package/vendor/docs/identity-directory-design.md +57 -56
- 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/types.d.ts +12 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.d.ts +2 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.js +47 -8
- 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 +233 -141
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
- package/vendor/gatekeeper/package.json +4 -3
- package/vendor/server/bin/lamaste-server.js +25 -1
- package/vendor/server/package.json +2 -2
- package/vendor/server/src/commands/audit.js +60 -0
- package/vendor/server/src/commands/panel-pin.js +40 -0
- package/vendor/server/src/commands/plugins.js +963 -12
- package/vendor/server/src/commands/push-install.js +317 -0
- package/vendor/server/src/commands/registry-credential.js +2 -1
- package/vendor/server/src/commands/uninstall.js +28 -1
- package/vendor/server/src/ndjson.js +4 -2
- package/vendor/server/src/panel-api.js +277 -34
- package/vendor/server/src/panel-identity.js +165 -0
- package/vendor/server-ui/dist/assets/index-BTlBt_cD.js +84 -0
- package/vendor/server-ui/dist/assets/index-Bc5NCMSv.css +1 -0
- package/vendor/server-ui/dist/index.html +2 -2
- package/vendor/serverd/package.json +2 -2
- package/vendor/serverd/src/index.js +56 -4
- package/vendor/serverd/src/lib/actor.js +24 -0
- package/vendor/serverd/src/lib/agent-jobs.js +330 -0
- package/vendor/serverd/src/lib/app-error.js +10 -0
- package/vendor/serverd/src/lib/audit.js +145 -0
- package/vendor/serverd/src/lib/hook-effects.js +123 -0
- package/vendor/serverd/src/lib/hook-pipelines.js +902 -0
- package/vendor/serverd/src/lib/migrations/state/0006_plugin_agent.sql +4 -0
- package/vendor/serverd/src/lib/migrations/state/0007_audit_and_notifications.sql +29 -0
- package/vendor/serverd/src/lib/migrations/state/0008_plugin_hooks.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0009_plugin_agent_half.sql +53 -0
- package/vendor/serverd/src/lib/migrations/state/0010_plugin_routes_and_retained_data.sql +47 -0
- package/vendor/serverd/src/lib/migrations/state/0011_agent_job_signature.sql +14 -0
- package/vendor/serverd/src/lib/mtls.js +89 -8
- package/vendor/serverd/src/lib/nginx.js +298 -44
- package/vendor/serverd/src/lib/notifications.js +107 -0
- package/vendor/serverd/src/lib/plugin-hooks.js +560 -0
- package/vendor/serverd/src/lib/plugin-hosting.js +688 -0
- package/vendor/serverd/src/lib/plugin-lifecycle.js +603 -0
- package/vendor/serverd/src/lib/plugin-operations.js +295 -0
- package/vendor/serverd/src/lib/plugin-packages.js +249 -0
- package/vendor/serverd/src/lib/plugins.js +298 -68
- package/vendor/serverd/src/lib/push-install.js +88 -2
- package/vendor/serverd/src/lib/relay-hooks.js +100 -0
- package/vendor/serverd/src/lib/relay-signing.js +43 -0
- package/vendor/serverd/src/lib/request-hooks.js +79 -0
- package/vendor/serverd/src/lib/retained-data.js +99 -0
- package/vendor/serverd/src/lib/trust-proxy.js +34 -0
- package/vendor/serverd/src/lib/tunnel-hooks.js +88 -0
- package/vendor/serverd/src/lib/vhost-reconcile.js +260 -0
- package/vendor/serverd/src/middleware/twofa-session.js +11 -1
- package/vendor/serverd/src/routes/agent-forwarder.js +37 -0
- package/vendor/serverd/src/routes/management/agent-panel-authz.js +97 -0
- package/vendor/serverd/src/routes/management/agents.js +262 -1
- package/vendor/serverd/src/routes/management/audit.js +81 -0
- package/vendor/serverd/src/routes/management/certs.js +1 -15
- package/vendor/serverd/src/routes/management/plugins.js +569 -54
- package/vendor/serverd/src/routes/management/tunnels.js +26 -0
- package/vendor/serverd/src/routes/management.js +4 -0
- package/vendor/server-ui/dist/assets/index-B5Wp8lu-.js +0 -257
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +0 -1
|
@@ -63,8 +63,18 @@ The admin panel requires a client certificate at the TLS layer. Without the cert
|
|
|
63
63
|
|
|
64
64
|
**Agent-side TLS verification:** The agent verifies both servers it talks to. The panel uses a self-signed TLS server certificate that is separate from the mTLS CA used to sign client certificates, so `lamaste-agent setup` pins the panel's public key on first contact (trust on first use) and every later panel call rejects any other key; `lamaste-agent panel reset-pin` re-captures it after a deliberate rotation. The tunnel relay, `tunnel.<domain>`, has a publicly trusted Let's Encrypt certificate that the agent's Chisel client verifies normally (see 5a below).
|
|
65
65
|
|
|
66
|
+
**Admin clients verify the panel too.** Whatever an admin client sends — the registry token, plugin settings, a TOTP code, a new user's password, storage keys, the 2FA session cookie — only ever reaches a server that proved it is the panel, and none of them falls back to skipping verification:
|
|
67
|
+
|
|
68
|
+
- **`lamaste-server`** (root, on the relay) reaches the panel through nginx's IP vhost on loopback port 9292. That port is not privileged: whenever nginx does not hold it — panel 2FA switches the IP vhost off, nginx is stopped — any local user could listen there. So the CLI refuses before connecting when panel 2FA is on or the IP vhost is not enabled, and every request verifies the listener against the vhost's own certificate, read from disk as root (`/etc/lamalibre/lamaste/pki/self-signed.pem`; curl `--cacert` with the address the certificate names, connected to `127.0.0.1`; no `.curlrc`, no proxy). It offers TLS 1.2 forward-secret suites only, so an impostor — even one presenting the public certificate — fails the handshake before the CLI presents the admin client certificate, let alone a request. With panel 2FA on, the panel-backed commands (tunnels, sites, plugins, registry credential, push install, audit, chisel) are unavailable: use the panel in the browser, or turn 2FA off.
|
|
69
|
+
- **The desktop app** verifies a panel reached at its domain (`https://panel.<domain>`) by its publicly trusted certificate. A panel reached at its IP address presents the self-signed certificate, so the app pins its public key per server, the same pin agents keep: read from the server over SSH when the app provisioned or recovered it, reported by the installer (or read from the certificate file) for a local install, otherwise taken from the first connection (trust on first use). The pin is stored with the server (`panelServerPubkeySha256` in `servers.json`) and shown on its card on the Servers page; only an explicit **Re-pin** there replaces it, after the app has shown the key the panel presents now. Every call — admin calls, health checks, the panel update, the hardware-bound upgrade — carries the pin (`--pinnedpubkey`); a different key aborts the connection before anything is sent. Agent calls use the pin the agent captured at enrollment.
|
|
70
|
+
- **`create-lamaste-admin`** (the hardware-bound upgrade) installs the CA the panel answers with as trusted in the login keychain, so it verifies the panel the same way: the desktop passes the server's pin; run by hand against an IP address, it shows the key the panel presents before the operator confirms.
|
|
71
|
+
|
|
72
|
+
`sudo lamaste-server panel-pin` prints the pin of the panel's IP certificate on the relay — compare it with what an agent (`lamaste-agent panel reset-pin`) or the desktop app shows to know they pinned the panel and not something in between.
|
|
73
|
+
|
|
66
74
|
**P12 password security:** The agent never passes the P12 password as a command-line argument. Curl calls use a temporary config file (created with mode `0600`, deleted after use), and openssl calls use the `LAMALIBRE_LAMASTE_P12_PASS` environment variable. This prevents the password from being visible in process listings (`ps aux`).
|
|
67
75
|
|
|
76
|
+
**Request bodies and session cookies:** A request body that carries a credential — the enrollment token, the registry token, a TOTP code, a user's password, storage keys — is never a curl argument either. `lamaste-agent setup` (enrollment), `lamaste-server` and the desktop app hand curl the body on stdin (`--data-binary @-`). `lamaste-server` runs as root, so its config file for the P12 password is created exclusively (`O_EXCL`, `0600`) in a private `mkdtemp` directory (`0700`) that is removed on every path. The desktop app keeps the panel's 2FA session cookie in its private `0600` config file next to the certificate password, and sends its calls to the local agent daemon with the Bearer token and the body in one curl config on stdin (`-K -`).
|
|
77
|
+
|
|
68
78
|
**Hardware-bound certificates:** Agents can enroll using a one-time token and a locally generated CSR, binding the private key to the macOS Keychain so it is non-extractable. The public `POST /api/enroll` endpoint uses the token as its sole authentication gate — no mTLS is required for enrollment itself, since the agent does not yet have a certificate. After enrollment, the agent authenticates with the Keychain-backed identity just like any other mTLS client. Admins can optionally upgrade their own authentication to hardware-bound mode (set `adminAuthMode` to `"hardware-bound"` in `panel.json`), which disables P12 download and certificate rotation through the panel UI. If the admin Keychain identity is lost, recovery is possible by running `lamaste-reset-admin` on the server, which reverts to standard P12 authentication.
|
|
69
79
|
|
|
70
80
|
Lamaste supports three types of certificates with different access levels:
|
|
@@ -131,6 +141,48 @@ mTLS is stronger than a login page because:
|
|
|
131
141
|
- There is no login endpoint to discover or attack
|
|
132
142
|
- The rejection happens before any application code runs
|
|
133
143
|
|
|
144
|
+
#### 5c. The agent daemon (`lamaste-agentd`) and relay assertions
|
|
145
|
+
|
|
146
|
+
The agent daemon listens on `127.0.0.1:<port>` (9393 by default) of the agent machine. It is reached by the agent's owner directly and by the relay through two kinds of tunnel, and admits:
|
|
147
|
+
|
|
148
|
+
| Caller | Proof | Access |
|
|
149
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
|
|
150
|
+
| The agent's owning OS user (desktop app, scripts) | `Authorization: Bearer <token>` from `~/.lamalibre/lamaste/agentd.token` | Full |
|
|
151
|
+
| The admin certificate, on `agent-<label>.<domain>` | A relay assertion signed by the panel | Full |
|
|
152
|
+
| The agent's own certificate, on `agent-<label>.<domain>` | A relay assertion signed by the panel | The capabilities the panel lists for it; owner-only routes refused |
|
|
153
|
+
| An Authelia user, on a plugin tunnel (`type: 'plugin'`) | A relay assertion signed by Gatekeeper | That plugin's routes, never `/api/*`; the plugin sees the asserted identity |
|
|
154
|
+
|
|
155
|
+
**Threat model.** Binding to loopback is not an authentication boundary. Two kinds of process can open a connection to the daemon without going through nginx: any OS user on the agent machine (`127.0.0.1:<port>`), and any process on the relay (the tunnel's port on the relay's loopback, which Chisel carries to the agent). Up to 4.2 the daemon took its caller from request headers that nginx sets — `X-SSL-Client-Verify: SUCCESS` with `X-SSL-Client-DN: CN=admin` meant full control, plugin install included (code execution as the agent's user), and `Remote-User` meant any Authelia identity on plugin routes — so either kind of process could claim to be anyone. Its check of whether a request needs a caller also tested the raw URL, while the router decodes percent-escapes: `/%61pi/...` reached `/api/...` routes and `/%68erd/...` a plugin's routes with no credential at all.
|
|
156
|
+
|
|
157
|
+
**Relay assertions.** From 4.3 the relay vouches for each request it sends with `X-Lamalibre-Relay-Assertion` — an Ed25519 signature over the agent the request is for (`aud`), the tunnel port, the caller, the method, the target (the exact `$request_uri` for a certificate caller; the plugin route for an Authelia user, whose request nginx rewrites), the issue time, a nonce and the key id. The daemon verifies it against the relay's public key and refuses it for another agent or port, for another method or target, older than 60 seconds (5 seconds of clock skew), or seen before. The signing key, `/etc/lamalibre/lamaste/relay-signing-key.json` (0600, `lamaste`), is created by the panel at startup:
|
|
158
|
+
|
|
159
|
+
- **Agent-panel vhost:** nginx asks the panel about every request (`auth_request` to `GET /api/agent-panel/authz/<label>`, with the client certificate's verification result, the method and `$request_uri`). The panel's mTLS middleware refuses a revoked certificate — before, the agent panel could not check revocation at all — and the panel admits the admin certificate and the agent's own, then signs.
|
|
160
|
+
- **Plugin tunnels:** Gatekeeper, which already validates the Authelia session for them, signs for the user it found; the answer is never cached (an assertion is single use).
|
|
161
|
+
|
|
162
|
+
Agents hold only the public key. The daemon fetches it, with the label the relay knows the agent by (the audience of its assertions, which a local `--label` need not equal), from `GET /api/agents/me/relay-keys` over its pinned mTLS channel, keeps both in memory and fetches again when an assertion names an unknown key — at most once per 30 seconds. Nothing stored on an agent machine lets anyone mint an assertion, for that agent or another.
|
|
163
|
+
|
|
164
|
+
The owner's token is as good as the owner: the desktop app hands it to curl on stdin, never in a process's arguments, which other local users can read.
|
|
165
|
+
|
|
166
|
+
Headers that only claim a caller — `X-SSL-Client-*`, `Remote-*` and the assertion itself — are removed from every request before a route or plugin sees it; `Remote-*` are set again only from a verified user assertion, so a plugin reading identity headers (`@lamalibre/lamaste-identity@4.3.1`) reads the relay's word. Whether a request needs a caller follows the route the daemon matched, not the URL: every route does except the health check, the plugin bundles and the SPA's static files, and so does a path no route matches under an installed plugin's name (a plugin may answer it with its own not-found handler). The claim headers are removed from `rawHeaders` too.
|
|
167
|
+
|
|
168
|
+
**What remains.** The relay's loopback is still the relay's trust boundary: the panel itself trusts the `X-SSL-Client-*` headers nginx sends to `127.0.0.1:3100`, so a process on the relay that can forge those can obtain an assertion as well — what it could do to agents before 4.3. With panel 2FA on it reaches further into agents than into the panel: forged (or stolen) admin-certificate headers no longer pass the panel's own management routes, but the agent-panel authorization is exempt from 2FA (the browser on `agent-<label>.<domain>` carries no 2FA cookie), so they still mint admin assertions — full control of every agent with a panel tunnel. Agent panels are guarded by the client certificate alone, as before; authenticating nginx to the panel is the relay-wide fix. An assertion binds the agent, the port, the method and the target, not the request body: a process on the agent machine that holds the daemon's port while the daemon is down receives the relay's requests, and could replay one with another body if the daemon comes back within the assertion's 65 seconds.
|
|
169
|
+
|
|
170
|
+
#### 5d. The app host on agents
|
|
171
|
+
|
|
172
|
+
An agent half Lamaste installs runs on its agent as a service of its own, so something on the agent has to act as root: create an account and a unit, write under `/opt`, `/var/lib` and `/etc`, install system packages. That is `lamaste-apphost`, enabled once per machine by the agent's operator (`sudo lamaste-agent apphost enable`). See [App Host](../04-api-reference/app-host.md) for the helper, its files and the unit.
|
|
173
|
+
|
|
174
|
+
**What the sudoers rule grants.** The agent's user may run one root-owned program, `/usr/local/sbin/lamaste-apphost`, with any arguments, a reset environment and Node.js's code-loading variables deleted. The program validates every argument, derives every path from the route and the agent label, takes content on stdin, reads nothing the agent's user can write, and does five things: install a version, write a plugin config, switch the current version, remove, report status. It never runs plugin code: the agent half runs as its own account (`lamaste-app-<route>`) under a hardened unit — no capabilities, `NoNewPrivileges`, the file system read-only but its data directory, its code root-owned 0644. `enable` installs the program only from a root-owned `lamaste-agent` install, so root never runs or copies code another user could have changed.
|
|
175
|
+
|
|
176
|
+
**Why jobs are relay-signed.** Without a signature, the agent's user — or whoever takes its account — could have the helper install any package it can produce with any system packages, activate any kept version, or purge data, as root. Approval of what an agent half brings happens on the relay, so the relay signs every `app-install`, `app-activate` and `app-remove` with its Ed25519 relay key when it creates the job, over the agent's label, the route, the job id, the kind, the payload (the exact package, version, tarball integrity, service and system packages; the version and package to activate; the purge flag and package) and a deadline of at most an hour. The helper verifies it with the key pinned for that agent, compares the payload with what it is asked, and runs each job id once. The agent's sync carries the signature from `agent-config` to the helper; it can neither make nor change one.
|
|
177
|
+
|
|
178
|
+
**The pin is trust on first use.** `enable` and `pin` fetch the relay's key as the agent's user, over that agent's pinned mTLS channel, and root checks the label and the key's shape: a label of that user's agents — from the user's agent registry, which root reads itself as a file that user owns, and never a label root's own agents use when the user is another, since a pin is per label for the whole machine — and an Ed25519 key whose id is derived from it. An agent account that is already compromised at that moment could hand root its own key for its own labels. Compare the pinned id with the relay's once: on the relay the panel logs it at start-up (`Relay assertion signing key ready`, `kid`) and `GET /api/agents/me/relay-keys` with the admin certificate returns it; on the agent `sudo lamaste-agent apphost status` lists each label's pin. The panel shows the first 12 hex digits each agent reports pinned and does not let an agent whose pin is not the relay's key host an agent half. A pin is a root-owned file in root-owned directories; the helper refuses one anyone else could have written or linked. After the relay's key is replaced, the helper refuses its jobs until `sudo lamaste-agent apphost pin` runs.
|
|
179
|
+
|
|
180
|
+
**Owner records.** A route belongs to the agent label and the agent package that first installed it — a root-owned record outside the agent half's data. Another label, or another package, is refused that route and the data kept with it until the owner purges it; the account an agent half runs as names its route, and an account that does not is never reused, given files or deleted. A relay can therefore neither take over another relay's agent half on a shared machine nor hand one package's data to another.
|
|
181
|
+
|
|
182
|
+
**Several agents on one machine.** Agents of different relays may share a machine and its helper. The rule names one OS user, so they run as that user (or root) — `enable` for another user is refused unless `--force`, since the first user's agents could no longer manage their agent halves; each label has its own pin, and a relay's signature names its own label for the agent, so a job signed for one agent is not run as another's.
|
|
183
|
+
|
|
184
|
+
**What a compromised agent account can still do:** read and rewrite the plugin configs it already holds (`config` is not signed) — breaking the admin API of that agent half; run `status`; run a signed job it received when it likes before the job expires, or not at all; report results that are not true (the relay's view of what runs on the agent is the agent's report); stop its tunnel and its sync. **What it cannot:** run anything as root beyond those fixed operations; install, switch or remove anything the relay did not sign, or anything other than exactly what it signed; replay a job; pin a key (root only); read or write an agent half's data directory (0700, another account); act on a route another label or package owns. **A compromised relay** can do on its agents what its signature allows: install any `@lamalibre/` package from its registry at any version, with system packages from the agent's configured apt sources, running as a service account — never plugin code as root — and remove or purge the agent halves it owns.
|
|
185
|
+
|
|
134
186
|
#### 6. TOTP 2FA for app access
|
|
135
187
|
|
|
136
188
|
Visitors to your tunneled apps authenticate through Authelia with a password and a TOTP code from their phone. See [Authentication](authentication.md) for full details.
|
|
@@ -144,7 +196,7 @@ Key details:
|
|
|
144
196
|
- **Session cookie:** `lamaste_2fa_session`, HMAC-SHA256 signed, with 12-hour absolute expiry and 2-hour inactivity timeout. Cookie flags: `HttpOnly`, `Secure`, `SameSite=Strict`
|
|
145
197
|
- **Rate limiting:** 5 failed attempts within 2 minutes trigger a 5-minute ban
|
|
146
198
|
- **TOTP standard:** RFC 6238, SHA-1, 30-second period, +/-1 step clock drift, replay protection
|
|
147
|
-
- **IP vhost disabled:** When panel 2FA is enabled, the IP-based vhost (`https://<IP>:9292`) is disabled and access is domain-only. This is necessary because session cookies require a domain scope.
|
|
199
|
+
- **IP vhost disabled:** When panel 2FA is enabled, the IP-based vhost (`https://<IP>:9292`) is disabled and access is domain-only. This is necessary because session cookies require a domain scope. `lamaste-server`'s panel commands, which use that vhost on loopback and hold no 2FA session, refuse to run while 2FA is on (see "Admin clients verify the panel too" above)
|
|
148
200
|
- **Recovery:** `sudo lamaste-reset-admin` clears 2FA settings and re-enables the IP vhost
|
|
149
201
|
|
|
150
202
|
See [Authentication](authentication.md) for setup details.
|
|
@@ -420,6 +472,10 @@ Sensitive files use restrictive permissions:
|
|
|
420
472
|
| `~/.lamalibre/lamaste/agents/<label>/chisel.json`, `chisel.env` | `600` | Agent's Chisel credential (agent machine) |
|
|
421
473
|
| `~/Library/LaunchAgents/com.lamalibre.lamaste.chisel-<label>.plist` | `600` | Carries `AUTH` in its environment (macOS agent) |
|
|
422
474
|
| `/etc/lamalibre/lamaste/pki/` | `700` | PKI directory itself |
|
|
475
|
+
| `/etc/sudoers.d/lamaste-apphost` | `440` | The app host rule (agent machine) |
|
|
476
|
+
| `/etc/lamalibre/apps/<route>/plugin-config.json` | `600` | An agent half's port and secret; its account |
|
|
477
|
+
| `/var/lib/lamalibre/apps/<route>/` | `700` | An agent half's data; its account |
|
|
478
|
+
| `/var/lib/lamalibre/apps/.jobs/`, `.trash/` | `700` | Signed jobs that ran; purged data (root) |
|
|
423
479
|
|
|
424
480
|
Mode `600` means only the file owner can read or write. Mode `640` adds read access for the file's group, `660` also write access. Mode `700` means only the directory owner can list, read, or modify contents. See [Configuration Files](../06-reference/config-files.md#file-permissions-table) for the owner of every directory.
|
|
425
481
|
|
|
@@ -441,15 +497,21 @@ There is no rule for `mv`, `cp`, `rm`, `chmod`, `chown`, `mkdir`, `cat`, `find`,
|
|
|
441
497
|
- **Binaries, units, accounts.** Chisel and Authelia (pinned, digest-verified releases), their systemd units and the service accounts are installed by `create-lamaste`, which runs as root. The panel never writes a unit or a binary; onboarding only checks they exist.
|
|
442
498
|
- **Code.** `/opt/lamalibre/lamaste` (panel, gatekeeper, `lamaste-server` CLI, UI, docs) is root-owned and read-only for `lamaste`, so a compromised panel cannot rewrite code that root later runs through `sudo lamaste-server`.
|
|
443
499
|
|
|
500
|
+
#### Plugin code on the relay: the hook runner
|
|
501
|
+
|
|
502
|
+
Server plugins run inside the panel (the `@lamalibre/` scope is their trust boundary). Agent-hosted plugins never do: the panel does not import them. Their **relay hooks** — code an administrator approves by digest — run in `lamalibre-lamaste-hooks@<route>.service`: a systemd dynamic user, a read-only file system without Lamaste's state, Authelia, certificates, nginx or SSH configuration, its own network namespace with Unix sockets only, no capabilities, Node's permission model allowing reads of the runner and the approved module only, an empty environment, and its output discarded. The panel calls it over a socket only the `lamaste` group can open; a hook can affect nothing but its answer and the rate-limited effects Lamaste performs for it. See [Lifecycle Hooks](../04-api-reference/lifecycle-hooks.md#the-hook-runner).
|
|
503
|
+
|
|
444
504
|
#### `lamaste-priv`
|
|
445
505
|
|
|
446
506
|
`/usr/local/sbin/lamaste-priv` is a root-owned (`0755`), zero-dependency Node.js program installed by `create-lamaste`. Content (vhost text, TOTP secrets) arrives on stdin, never on the command line, so it is not written to the sudo log. It reads nothing the `lamaste` user can write.
|
|
447
507
|
|
|
448
|
-
| Operation | Does
|
|
449
|
-
| ----------------------------------------------------------------------------------- |
|
|
450
|
-
| `nginx-site write\|backup\|restore\|discard-backup\|enable\|disable\|remove <name>` | Manage `sites-available/<name>` (and its `.bak`) and its `sites-enabled` link. `write` validates the vhost on stdin first.
|
|
451
|
-
| `authelia-totp <username>` | Store the TOTP secret from stdin with Authelia's CLI, run as `lamaste-authelia` (the database is that account's file)
|
|
452
|
-
| `self-update <version>` | Start `npx @lamalibre/create-lamaste@<version> --yes` in a transient systemd unit two seconds later (`MAJOR.MINOR.PATCH` only)
|
|
508
|
+
| Operation | Does |
|
|
509
|
+
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
510
|
+
| `nginx-site write\|backup\|restore\|discard-backup\|enable\|disable\|remove <name>` | Manage `sites-available/<name>` (and its `.bak`) and its `sites-enabled` link. `write` validates the vhost on stdin first. |
|
|
511
|
+
| `authelia-totp <username>` | Store the TOTP secret from stdin with Authelia's CLI, run as `lamaste-authelia` (the database is that account's file) |
|
|
512
|
+
| `self-update <version>` | Start `npx @lamalibre/create-lamaste@<version> --yes` in a transient systemd unit two seconds later (`MAJOR.MINOR.PATCH` only) |
|
|
513
|
+
| `hooks install <route> <sha256\|none>` | Write a plugin's approved relay hook module (stdin, checked against the digest) root-owned to `/opt/lamalibre/lamaste/hooks/<route>/`, enable its runner socket |
|
|
514
|
+
| `hooks activate\|restart\|remove <route> [sha256]` | Point the runner back at the kept previous module; stop a stuck runner; disable it and delete its modules |
|
|
453
515
|
|
|
454
516
|
Site names are limited to the panel's own vhosts: `lamalibre-lamaste-panel-domain`, `-auth`, `-tunnel`, `-app-<subdomain>`, `-agent-panel-<label>` and `-site-<uuid>`. `lamalibre-lamaste-panel-ip` (written by `create-lamaste`) may only be enabled or disabled.
|
|
455
517
|
|
|
@@ -509,40 +571,53 @@ Configuration files that are read live by services (like Authelia's `users.yml`)
|
|
|
509
571
|
|
|
510
572
|
### Source files
|
|
511
573
|
|
|
512
|
-
| File
|
|
513
|
-
|
|
|
514
|
-
| `packages/create-lamaste/src/tasks/harden.js`
|
|
515
|
-
| `packages/create-lamaste/src/tasks/mtls.js`
|
|
516
|
-
| `packages/create-lamaste/src/tasks/nginx.js`
|
|
517
|
-
| `packages/lamaste-serverd/src/lib/authelia.js`
|
|
518
|
-
| `packages/lamaste-serverd/src/lib/mtls.js`
|
|
519
|
-
| `packages/lamaste-serverd/src/lib/nginx.js`
|
|
520
|
-
| `packages/lamaste-serverd/src/middleware/mtls.js`
|
|
521
|
-
| `packages/lamaste-serverd/src/lib/totp.js`
|
|
522
|
-
| `packages/lamaste-serverd/src/lib/session.js`
|
|
523
|
-
| `packages/lamaste-serverd/src/middleware/twofa-session.js`
|
|
574
|
+
| File | Purpose |
|
|
575
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
576
|
+
| `packages/create-lamaste/src/tasks/harden.js` | Swap, UFW, fail2ban, SSH hardening |
|
|
577
|
+
| `packages/create-lamaste/src/tasks/mtls.js` | PKI generation, file permissions |
|
|
578
|
+
| `packages/create-lamaste/src/tasks/nginx.js` | mTLS snippet, self-signed cert |
|
|
579
|
+
| `packages/lamaste-serverd/src/lib/authelia.js` | bcrypt hashing, atomic writes |
|
|
580
|
+
| `packages/lamaste-serverd/src/lib/mtls.js` | Certificate rotation with rollback |
|
|
581
|
+
| `packages/lamaste-serverd/src/lib/nginx.js` | Vhost write-with-rollback pattern |
|
|
582
|
+
| `packages/lamaste-serverd/src/middleware/mtls.js` | Request-level mTLS check |
|
|
583
|
+
| `packages/lamaste-serverd/src/lib/totp.js` | Panel 2FA TOTP verification and replay protection |
|
|
584
|
+
| `packages/lamaste-serverd/src/lib/session.js` | HMAC-SHA256 session cookie signing |
|
|
585
|
+
| `packages/lamaste-serverd/src/middleware/twofa-session.js` | 2FA session enforcement (after mTLS, before roleGuard) |
|
|
586
|
+
| `packages/core/lib/src/relay-assertion.ts` | Relay assertions: signing key file, sign, verify |
|
|
587
|
+
| `packages/core/lib/src/server/agent-panel-authz.ts` | Who may use an agent panel through the relay |
|
|
588
|
+
| `packages/server/daemon/src/routes/management/agent-panel-authz.js` | The agent-panel vhost's `auth_request` target |
|
|
589
|
+
| `packages/sdks/gatekeeper/src/server/routes/authz.ts` | Assertions for Authelia users on plugin tunnels |
|
|
590
|
+
| `packages/agent/daemon/src/lib/auth.js` | lamaste-agentd's authentication hook |
|
|
591
|
+
| `packages/core/lib/src/app-job-signature.ts` | Relay-signed app jobs: envelope, sign, verify |
|
|
592
|
+
| `packages/agent/cli/scripts/lamaste-apphost` | The app host helper (root, Node.js built-ins only) |
|
|
593
|
+
| `packages/agent/cli/src/lib/apphost-setup.js` | Root-owned install check, sudoers rule, relay key pins |
|
|
594
|
+
| `packages/server/daemon/src/lib/agent-jobs.js` | Agent jobs; app jobs signed when created |
|
|
524
595
|
|
|
525
596
|
## Quick Reference
|
|
526
597
|
|
|
527
598
|
### Security layers
|
|
528
599
|
|
|
529
|
-
| Layer | Technology
|
|
530
|
-
| -------------------- |
|
|
531
|
-
| Firewall | UFW
|
|
532
|
-
| Intrusion prevention | fail2ban
|
|
533
|
-
| SSH hardening | sshd_config
|
|
534
|
-
| TLS encryption | Let's Encrypt / self-signed
|
|
535
|
-
| Admin auth | mTLS client certificates
|
|
536
|
-
| Admin 2FA (optional) | Built-in TOTP
|
|
537
|
-
| Agent-to-agent auth | Tickets (time-limited, single-use)
|
|
538
|
-
| Tunnel ownership | Chisel authfile per-agent grants
|
|
539
|
-
| Reserved ports | Tunnel port validation
|
|
540
|
-
| Privileged commands | No-wildcard sudoers, `lamaste-priv`
|
|
541
|
-
| Relay authenticity | Agent verifies `tunnel.<domain>` TLS
|
|
542
|
-
|
|
|
543
|
-
|
|
|
544
|
-
|
|
|
545
|
-
|
|
|
600
|
+
| Layer | Technology | Blocks |
|
|
601
|
+
| -------------------- | --------------------------------------- | ------------------------------------- |
|
|
602
|
+
| Firewall | UFW | Connections to non-allowed ports |
|
|
603
|
+
| Intrusion prevention | fail2ban | Repeated failed login attempts |
|
|
604
|
+
| SSH hardening | sshd_config | Password-based SSH access |
|
|
605
|
+
| TLS encryption | Let's Encrypt / self-signed | Traffic interception and tampering |
|
|
606
|
+
| Admin auth | mTLS client certificates | Unauthorized admin access |
|
|
607
|
+
| Admin 2FA (optional) | Built-in TOTP | Stolen certificate abuse |
|
|
608
|
+
| Agent-to-agent auth | Tickets (time-limited, single-use) | Unauthorized cross-agent access |
|
|
609
|
+
| Tunnel ownership | Chisel authfile per-agent grants | One agent hijacking another's host |
|
|
610
|
+
| Reserved ports | Tunnel port validation | Publishing internal services |
|
|
611
|
+
| Privileged commands | No-wildcard sudoers, `lamaste-priv` | Root code execution via extra flags |
|
|
612
|
+
| Relay authenticity | Agent verifies `tunnel.<domain>` TLS | Relay impersonation, credential theft |
|
|
613
|
+
| Panel authenticity | Pinned panel key, CA for its domain | Panel impersonation, credential theft |
|
|
614
|
+
| Agent daemon auth | Relay assertions (Ed25519), owner token | Callers forged on an agent's loopback |
|
|
615
|
+
| Agent root helper | Relay-signed app jobs, pinned key | Root actions the relay did not sign |
|
|
616
|
+
| Agent half sandbox | Own account, hardened systemd unit | Plugin code as root |
|
|
617
|
+
| App auth | Authelia TOTP 2FA | Unauthorized app access |
|
|
618
|
+
| Service isolation | `127.0.0.1` binding | Direct access to internal services |
|
|
619
|
+
| File permissions | `chmod 600` | Unauthorized secret access |
|
|
620
|
+
| Atomic writes | `mv` pattern | Partial file reads by services |
|
|
546
621
|
|
|
547
622
|
### Firewall rules
|
|
548
623
|
|
|
@@ -264,9 +264,9 @@ The integration flow:
|
|
|
264
264
|
}
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
-
### Client SDK (`@lamalibre/lamaste-tickets@4.
|
|
267
|
+
### Client SDK (`@lamalibre/lamaste-tickets@4.3.1`)
|
|
268
268
|
|
|
269
|
-
The `@lamalibre/lamaste-tickets@4.
|
|
269
|
+
The `@lamalibre/lamaste-tickets@4.3.1` 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
270
|
|
|
271
271
|
The SDK exports three main classes:
|
|
272
272
|
|
|
@@ -144,7 +144,7 @@ On the API side the same rule holds: `GET /api/tunnels/agent-config` returns onl
|
|
|
144
144
|
|
|
145
145
|
### Client configuration
|
|
146
146
|
|
|
147
|
-
The Chisel client runs as a user-level service managed by `lamaste-agent` (and `lamaste-agentd`): a LaunchAgent plist on macOS, a systemd **user** unit on Linux. Both are generated by one implementation in `@lamalibre/lamaste@4.
|
|
147
|
+
The Chisel client runs as a user-level service managed by `lamaste-agent` (and `lamaste-agentd`): a LaunchAgent plist on macOS, a systemd **user** unit on Linux. Both are generated by one implementation in `@lamalibre/lamaste@4.3.1/agent`. The client command looks like:
|
|
148
148
|
|
|
149
149
|
```bash
|
|
150
150
|
chisel client \
|
|
@@ -208,7 +208,7 @@ location / {
|
|
|
208
208
|
|
|
209
209
|
### Concurrency and locking
|
|
210
210
|
|
|
211
|
-
Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH`, releasing a revoked agent's tunnels, and startup reconciliation — runs under one tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.
|
|
211
|
+
Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH`, releasing a revoked agent's tunnels, and startup reconciliation — runs under one tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.3.1/server`'s `tunnels.ts`), with its validation inside the lock. Two concurrent requests therefore cannot both pass a uniqueness check for the same subdomain or port. Authfile renders are additionally serialized with a keyed promise-chain mutex in `chisel-users.ts`, so enrollments and credential rotations never interleave authfile writes with tunnel changes.
|
|
212
212
|
|
|
213
213
|
### Startup reconciliation
|
|
214
214
|
|
|
@@ -223,7 +223,7 @@ It **fails closed**: if the authfile cannot be established, Chisel is stopped **
|
|
|
223
223
|
|
|
224
224
|
### Installation
|
|
225
225
|
|
|
226
|
-
On the server, Chisel is installed by `npx @lamalibre/create-lamaste@4.
|
|
226
|
+
On the server, Chisel is installed by `npx @lamalibre/create-lamaste@4.3.1` — as root, together with its hardened systemd unit (`User=nobody`, `Group=lamaste-chisel`, `NoNewPrivileges`, `ProtectSystem=strict`, empty `CapabilityBoundingSet`) — and kept at the pinned release by every redeploy. The panel never installs a binary or writes the unit; onboarding only checks the binary exists, creates the server key and authfile, and starts the service. Both the server installer and the agents download the pinned release (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.3.1`'s `constants.ts`) from its fixed URL, with no GitHub API lookup and no "latest":
|
|
227
227
|
|
|
228
228
|
```
|
|
229
229
|
https://github.com/jpillora/chisel/releases/download/v1.12.0/chisel_1.12.0_<os>_<arch>.gz
|
|
@@ -320,7 +320,7 @@ sudo systemctl restart chisel
|
|
|
320
320
|
|
|
321
321
|
```bash
|
|
322
322
|
# Install globally, then set up the agent (interactive)
|
|
323
|
-
npm install -g @lamalibre/lamaste-agent@4.
|
|
323
|
+
npm install -g @lamalibre/lamaste-agent@4.3.1
|
|
324
324
|
lamaste-agent setup --label <label>
|
|
325
325
|
|
|
326
326
|
# Apply tunnel changes now (the sync timer does it within 30 s anyway)
|
|
@@ -59,7 +59,7 @@ The token is single-use and expires after 10 minutes. `tunnels:read` is enough f
|
|
|
59
59
|
Install the agent **globally** first:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
npm install -g @lamalibre/lamaste-agent@4.
|
|
62
|
+
npm install -g @lamalibre/lamaste-agent@4.3.1
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
A global install is required, not a convenience. Setup installs a sync timer (see [How tunnel changes reach the agent](#how-tunnel-changes-reach-the-agent)) that runs the installed `lamaste-agent` every 30 seconds for as long as the agent exists. An `npx` copy lives in npm's cache, which npm may clear at any time — the timer would silently stop. `setup` and `update` therefore refuse to run from `npx`, and they check this **before** an enrollment token is spent. For an installation npm does not manage, set `LAMALIBRE_LAMASTE_AGENT_CLI_PATH` to the absolute path of the `lamaste-agent` script the timer should run.
|
|
@@ -117,7 +117,7 @@ The relay only lets an agent open the ports of the enabled tunnels it carries, a
|
|
|
117
117
|
4. The service definition lists exactly the tunnels the relay grants this agent.
|
|
118
118
|
5. The client runs when there is something to carry, and is stopped when the agent carries no tunnel or the operator stopped it.
|
|
119
119
|
|
|
120
|
-
When everything already matches, a run costs one panel request and changes nothing. The timer runs it:
|
|
120
|
+
When everything already matches, a run costs one panel request and changes nothing. After converging, a run also reports the agent's host facts when they changed and runs the jobs the relay listed for the plugins this agent hosts — lifecycle hook calls, and installing, switching or removing an agent half ([App Host](../04-api-reference/app-host.md)); an install may take up to 10 minutes, so systemd allows a run 20 (`TimeoutStartSec=20min`). The timer runs it:
|
|
121
121
|
|
|
122
122
|
| Platform | Units | Schedule |
|
|
123
123
|
| -------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
@@ -148,7 +148,7 @@ Each agent authenticates to the relay with its own chisel credential, stored in
|
|
|
148
148
|
Upgrade agents **before** the server. Agents from before this version reject the new shape of the relay's Chisel arguments; a current agent works against an older server, keeping its credential until the server offers rotation.
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
|
-
npm install -g @lamalibre/lamaste-agent@4.
|
|
151
|
+
npm install -g @lamalibre/lamaste-agent@4.3.1
|
|
152
152
|
lamaste-agent update
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -167,21 +167,39 @@ lamaste-agent switch <label> # change the default agent
|
|
|
167
167
|
lamaste-agent uninstall --label <l> # stop and remove one agent, including its sync timer
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
## Hosting app plugins (Linux)
|
|
171
|
+
|
|
172
|
+
To let the panel install the agent halves of app plugins on this machine — their accounts, units and directories — enable the app host helper once, from a root-owned install:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
sudo npm install -g @lamalibre/lamaste-agent@4.3.1
|
|
176
|
+
sudo lamaste-agent apphost enable # the agent runs as the user who ran sudo
|
|
177
|
+
lamaste-agent apphost status # helper, pinned relay keys, installed agent halves
|
|
178
|
+
sudo lamaste-agent apphost pin # after the relay rotated its signing key
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The agent reports it on its next sync, and the panel can then host plugins whose agent half Lamaste installs here, while this agent's push-install window is open. `lamaste-agent uninstall` refuses while agent halves are installed for the agents it would remove. See [App Host](../04-api-reference/app-host.md).
|
|
182
|
+
|
|
170
183
|
## Troubleshooting
|
|
171
184
|
|
|
172
|
-
| Symptom | Cause and fix
|
|
173
|
-
| ----------------------------------------------------------- |
|
|
174
|
-
| `setup` says the agent must be installed globally | It was run through `npx`. Run `npm install -g @lamalibre/lamaste-agent@4.
|
|
175
|
-
| Log shows a TLS / x509 error | `tunnel.<domain>` does not present a valid certificate for its name. Check the relay's certificate (`Certificates` page) and that the agent's clock is correct.
|
|
176
|
-
| Log shows authentication failed | The credential was rotated on the panel. The next sync picks it up; `lamaste-agent chisel refresh-credential` applies it now.
|
|
177
|
-
| Log shows `access denied` for a remote | The relay no longer grants this agent that port: the tunnel was disabled, deleted, or moved to another agent. The next sync drops it. If it persists, check `lamaste-agent status` — the sync timer may not be running.
|
|
178
|
-
| `status` shows `Sync: not installed` | The agent was set up before the timer existed. Install globally, then run `lamaste-agent update`.
|
|
179
|
-
| `status` shows `Sync: last ran ... not running its program` | The timer's last run is over 2 minutes old: the timer was unloaded, or the program it runs is gone (Node.js or the agent was moved or uninstalled). Run `lamaste-agent update` from the global install to rewrite it.
|
|
180
|
-
| `status` shows `Sync: failing since ...` | The error is the last sync's. `The panel reports domain X, but this agent is enrolled with Y` means the panel moved to another domain: re-enroll the agent. Otherwise check the panel is reachable. Details in `logs/sync.log`.
|
|
181
|
-
| Tunnels do not start although the agent carries some | The tunnels were stopped from the agent panel or desktop app, and the timer respects that. Start them there.
|
|
182
|
-
| Tunnel works until reboot | Lingering is off. See step 3.
|
|
183
|
-
| New tunnel returns 502 | The agent has not synced yet (up to ~30 s), or nothing listens on `127.0.0.1:<port>` on the agent. Run `lamaste-agent update` to apply at once.
|
|
184
|
-
| Upload fails with 413 | The request is larger than the tunnel's body limit. Raise **Largest request body** on the tunnel (`maxBodySizeMb`).
|
|
185
|
+
| Symptom | Cause and fix |
|
|
186
|
+
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
+
| `setup` says the agent must be installed globally | It was run through `npx`. Run `npm install -g @lamalibre/lamaste-agent@4.3.1`, then `lamaste-agent setup ...` again. The enrollment token was not spent. |
|
|
188
|
+
| Log shows a TLS / x509 error | `tunnel.<domain>` does not present a valid certificate for its name. Check the relay's certificate (`Certificates` page) and that the agent's clock is correct. |
|
|
189
|
+
| Log shows authentication failed | The credential was rotated on the panel. The next sync picks it up; `lamaste-agent chisel refresh-credential` applies it now. |
|
|
190
|
+
| Log shows `access denied` for a remote | The relay no longer grants this agent that port: the tunnel was disabled, deleted, or moved to another agent. The next sync drops it. If it persists, check `lamaste-agent status` — the sync timer may not be running. |
|
|
191
|
+
| `status` shows `Sync: not installed` | The agent was set up before the timer existed. Install globally, then run `lamaste-agent update`. |
|
|
192
|
+
| `status` shows `Sync: last ran ... not running its program` | The timer's last run is over 2 minutes old: the timer was unloaded, or the program it runs is gone (Node.js or the agent was moved or uninstalled). Run `lamaste-agent update` from the global install to rewrite it. |
|
|
193
|
+
| `status` shows `Sync: failing since ...` | The error is the last sync's. `The panel reports domain X, but this agent is enrolled with Y` means the panel moved to another domain: re-enroll the agent. Otherwise check the panel is reachable. Details in `logs/sync.log`. |
|
|
194
|
+
| Tunnels do not start although the agent carries some | The tunnels were stopped from the agent panel or desktop app, and the timer respects that. Start them there. |
|
|
195
|
+
| Tunnel works until reboot | Lingering is off. See step 3. |
|
|
196
|
+
| New tunnel returns 502 | The agent has not synced yet (up to ~30 s), or nothing listens on `127.0.0.1:<port>` on the agent. Run `lamaste-agent update` to apply at once. |
|
|
197
|
+
| Upload fails with 413 | The request is larger than the tunnel's body limit. Raise **Largest request body** on the tunnel (`maxBodySizeMb`). |
|
|
198
|
+
| `uninstall` refuses: agent halves are installed | Agent halves run here for this agent. Unhost or uninstall those plugins on the panel (the next sync removes them), or `sudo lamaste-agent apphost disable --force` (data is kept). |
|
|
199
|
+
| `apphost enable` says the install is not root-owned | Root installs the helper only from code no other user can change: `sudo npm install -g @lamalibre/lamaste-agent@4.3.1`, then `sudo lamaste-agent apphost enable` from that install. |
|
|
200
|
+
| `apphost enable` says the helper serves another user | App hosting serves one OS user per machine. Unhost or uninstall the plugins that user's agents host (`sudo lamaste-agent apphost status` lists their agent halves), then `sudo lamaste-agent apphost enable --user <name> --force`. |
|
|
201
|
+
| `apphost pin` says a label is not an agent of the user | Root pins only labels the agent user's registry lists. Pin as the user the agent runs as (`--user <name>`; `--user root` for an agent running as root). |
|
|
202
|
+
| Hosting refused: the helper trusts another relay key | The relay's signing key was replaced, or the agent enrolled again. Run `sudo lamaste-agent apphost pin`; compare the key id with the relay's ([App Host](../04-api-reference/app-host.md#pinning-the-relay-key)). |
|
|
185
203
|
|
|
186
204
|
## For Developers
|
|
187
205
|
|
|
@@ -196,7 +196,7 @@ The admin certificate (`CN=admin`) has unrestricted access to all panel endpoint
|
|
|
196
196
|
|
|
197
197
|
### Distribute to the Agent Machine
|
|
198
198
|
|
|
199
|
-
Send the `.p12` file and password to the agent machine (macOS or Linux) securely, and install the agent there with `npm install -g @lamalibre/lamaste-agent@4.
|
|
199
|
+
Send the `.p12` file and password to the agent machine (macOS or Linux) securely, and install the agent there with `npm install -g @lamalibre/lamaste-agent@4.3.1` and run `lamaste-agent setup --label <label>` — or import it into the Desktop App. An enrollment token is preferable where possible, because the private key is then generated on the agent and never travels. See [Agent Setup](agent-setup.md) for details.
|
|
200
200
|
|
|
201
201
|
### Update Capabilities
|
|
202
202
|
|
|
@@ -293,7 +293,7 @@ Revocation is immediate — the certificate serial is added to a revocation list
|
|
|
293
293
|
|
|
294
294
|
### mTLS Rotation Internals
|
|
295
295
|
|
|
296
|
-
Admin certificate rotation lives only in `lamaste-server reset-admin` (`packages/server/cli/src/commands/reset-admin.js`), run as root on the server. The panel's `rotateClientCert()` in `lib/mtls.js` is a stub that returns `503` with that instruction; `@lamalibre/lamaste@4.
|
|
296
|
+
Admin certificate rotation lives only in `lamaste-server reset-admin` (`packages/server/cli/src/commands/reset-admin.js`), run as root on the server. The panel's `rotateClientCert()` in `lib/mtls.js` is a stub that returns `503` with that instruction; `@lamalibre/lamaste@4.3.1/server` no longer exports a rotation function. The PKI directory belongs to `lamaste`, so `reset-admin` runs every file operation inside it (`openssl genrsa`/`req`/`x509 -req`/`pkcs12`, `cp`, `chmod`) as `lamaste`, and writes `panel.json` and `revoked.json` as fresh files renamed into place — root never writes through a path the panel could have replaced with a symlink.
|
|
297
297
|
|
|
298
298
|
The PKCS12 bundle uses legacy encryption settings (`-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1`) because newer OpenSSL defaults produce files that macOS Keychain Access cannot import.
|
|
299
299
|
|
|
@@ -14,7 +14,7 @@ Before you start, make sure you have:
|
|
|
14
14
|
|
|
15
15
|
| Requirement | Details |
|
|
16
16
|
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
-
| **Desktop app installed** | `npx @lamalibre/create-lamaste-desktop@4.
|
|
17
|
+
| **Desktop app installed** | `npx @lamalibre/create-lamaste-desktop@4.3.1` ([setup guide](desktop-app-setup.md)) |
|
|
18
18
|
| **DigitalOcean account** | A free account at [cloud.digitalocean.com](https://cloud.digitalocean.com). **If you have other infrastructure on DO, create a dedicated team first** (see below) |
|
|
19
19
|
| **Payment method** | A credit card or PayPal on file in DigitalOcean (required to create droplets) |
|
|
20
20
|
| **Domain name** | Optional for initial setup — you can add a domain later through the panel |
|
|
@@ -221,8 +221,8 @@ The wizard shows up to 12 steps with live progress indicators. Each step shows a
|
|
|
221
221
|
| **Waiting for boot** | Polls DigitalOcean until the droplet has a public IP and is ready (up to 5 minutes) |
|
|
222
222
|
| **Setting up DNS records** | Creates A and wildcard A records in DigitalOcean DNS (only if a domain was selected in the Domain step) |
|
|
223
223
|
| **Connecting via SSH** | Establishes an SSH connection using the temporary key |
|
|
224
|
-
| **Installing Lamaste** | Runs `npx @lamalibre/create-lamaste@4.
|
|
225
|
-
| **Retrieving credentials** | Downloads the admin certificate (`.p12` file) from the droplet
|
|
224
|
+
| **Installing Lamaste** | Runs `npx @lamalibre/create-lamaste@4.3.1` on the droplet (up to 10 minutes) |
|
|
225
|
+
| **Retrieving credentials** | Downloads the admin certificate (`.p12` file) and reads the panel's TLS key pin from the droplet |
|
|
226
226
|
| **Enrolling admin certificate** | Imports the certificate into your OS credential store |
|
|
227
227
|
| **Saving configuration** | Writes the server entry to `~/.lamalibre/lamaste/servers.json` |
|
|
228
228
|
| **Cleaning up** | Deletes the temporary SSH key from DigitalOcean and your machine |
|
|
@@ -315,7 +315,7 @@ If you already have a Lamaste server set up (via the manual SSH method), you can
|
|
|
315
315
|
|
|
316
316
|
The app checks that the panel is reachable before adding it. The server is added with `active: false` — click **Set Active** on its card to start using it.
|
|
317
317
|
|
|
318
|
-
> **Note:** Adding an existing server registers it in the app but does not configure authentication. You will still need to set up your agent certificate separately using `lamaste-agent setup` (after `npm install -g @lamalibre/lamaste-agent@4.
|
|
318
|
+
> **Note:** Adding an existing server registers it in the app but does not configure authentication. You will still need to set up your agent certificate separately using `lamaste-agent setup` (after `npm install -g @lamalibre/lamaste-agent@4.3.1`) or by importing a certificate manually.
|
|
319
319
|
|
|
320
320
|
---
|
|
321
321
|
|
|
@@ -26,7 +26,7 @@ The Lamaste Desktop app is a native application (built with Tauri) that wraps th
|
|
|
26
26
|
Run the installer:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx @lamalibre/create-lamaste-desktop@4.
|
|
29
|
+
npx @lamalibre/create-lamaste-desktop@4.3.1
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
The installer:
|
|
@@ -56,11 +56,11 @@ After the app launches, it shows a setup screen with two options:
|
|
|
56
56
|
Install the agent globally and run its setup command:
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
npm install -g @lamalibre/lamaste-agent@4.
|
|
59
|
+
npm install -g @lamalibre/lamaste-agent@4.3.1
|
|
60
60
|
lamaste-agent setup --label my-server
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
The agent must be a global install — setup installs a sync timer that runs it every 30 seconds to pick up tunnel changes, and refuses to run from `npx`. (When the app sets up an agent itself, it installs `@lamalibre/lamaste-agent@4.
|
|
63
|
+
The agent must be a global install — setup installs a sync timer that runs it every 30 seconds to pick up tunnel changes, and refuses to run from `npx`. (When the app sets up an agent itself, it installs `@lamalibre/lamaste-agent@4.3.1` globally for you.) The `--label` flag names this agent connection (used for multi-agent support). This command connects to your VPS panel using the agent certificate and configures the local Chisel client. During setup, the client certificate and key are extracted from the P12 bundle for mTLS client authentication. Once setup completes, the app detects the configuration and switches to the main interface.
|
|
64
64
|
|
|
65
65
|
**Option B: Create a new server**
|
|
66
66
|
|
|
@@ -72,7 +72,7 @@ The desktop app has two modes, toggled via a pill switch in the sidebar header.
|
|
|
72
72
|
|
|
73
73
|
### Agent Mode (default)
|
|
74
74
|
|
|
75
|
-
Agent mode provides local agent management. The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are imported from the shared `@lamalibre/lamaste-agent-ui@4.
|
|
75
|
+
Agent mode provides local agent management. The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are imported from the shared `@lamalibre/lamaste-agent-ui@4.3.1` package. The landing page is the **Agents** list, showing all configured agents with their connection status. Click an agent to drill into its management pages:
|
|
76
76
|
|
|
77
77
|
- **Agents** (landing) — list of all configured agents with start/stop controls and status
|
|
78
78
|
- **Dashboard** — per-agent connection status, Chisel version, start/stop/restart controls
|
|
@@ -84,7 +84,7 @@ Agent mode provides local agent management. The agent-mode pages (Dashboard, Tun
|
|
|
84
84
|
|
|
85
85
|
### Server Mode
|
|
86
86
|
|
|
87
|
-
Server mode provides the full admin panel — the same management pages available in the browser-based panel UI. These pages are imported from the shared `@lamalibre/lamaste-server-ui@4.
|
|
87
|
+
Server mode provides the full admin panel — the same management pages available in the browser-based panel UI. These pages are imported from the shared `@lamalibre/lamaste-server-ui@4.3.1` package:
|
|
88
88
|
|
|
89
89
|
- **Dashboard** — system stats (CPU, RAM, disk, uptime) + service health
|
|
90
90
|
- **Tunnels** — tunnel CRUD, carrying agent, access mode, body limit
|
|
@@ -149,6 +149,7 @@ The multi-server management page:
|
|
|
149
149
|
- **Add existing server** — connect to a server you already set up manually by providing the panel URL and certificate
|
|
150
150
|
- **Server switching** — switch the active server; the app reloads configuration and reconnects
|
|
151
151
|
- **Server removal** — remove a server from the registry (cloud-provisioned servers can also be destroyed)
|
|
152
|
+
- **Panel TLS key** — a panel reached at its IP address (`https://<ip>:9292`) has a self-signed certificate, so the app pins its public key: read from the server over SSH when the app provisioned it, otherwise on the first connection. The card shows the pin (`sha256//…`) and where it came from; compare it with `sudo lamaste-server panel-pin` on the server. Every call to the panel must present that key — a different one is refused before anything is sent. After reinstalling a server or replacing its certificate, **Re-pin** shows the key the panel presents now next to the pinned one and replaces it only when you accept. A panel reached at its domain is verified by its Let's Encrypt certificate instead
|
|
152
153
|
|
|
153
154
|
**Cloud provider tokens** are stored in your OS credential store (macOS Keychain or Linux libsecret) — never in plaintext files or process arguments. P12 passwords for each server are also stored in the credential store.
|
|
154
155
|
|
|
@@ -169,7 +170,7 @@ Stopping an agent's tunnels from the app is remembered: the agent's sync timer k
|
|
|
169
170
|
Run the installer again to get the latest version:
|
|
170
171
|
|
|
171
172
|
```bash
|
|
172
|
-
npx @lamalibre/create-lamaste-desktop@4.
|
|
173
|
+
npx @lamalibre/create-lamaste-desktop@4.3.1
|
|
173
174
|
```
|
|
174
175
|
|
|
175
176
|
If a new release is available, it downloads and installs the update. The previous version is replaced.
|
|
@@ -202,9 +203,9 @@ Deleting `~/.lamalibre/lamaste/` by hand instead would leave the agents' service
|
|
|
202
203
|
|
|
203
204
|
| Action | Command / Location |
|
|
204
205
|
| -------------------- | ------------------------------------------------------------------------------------ |
|
|
205
|
-
| **Install** | `npx @lamalibre/create-lamaste-desktop@4.
|
|
206
|
-
| **Update** | `npx @lamalibre/create-lamaste-desktop@4.
|
|
207
|
-
| **Agent setup** | `npm install -g @lamalibre/lamaste-agent@4.
|
|
206
|
+
| **Install** | `npx @lamalibre/create-lamaste-desktop@4.3.1` |
|
|
207
|
+
| **Update** | `npx @lamalibre/create-lamaste-desktop@4.3.1` |
|
|
208
|
+
| **Agent setup** | `npm install -g @lamalibre/lamaste-agent@4.3.1`, then `lamaste-agent setup --label <name>` |
|
|
208
209
|
| **App location** | `/Applications/Lamaste.app` (macOS) |
|
|
209
210
|
| **Agent registry** | `~/.lamalibre/lamaste/agents.json` |
|
|
210
211
|
| **Per-agent data** | `~/.lamalibre/lamaste/agents/<label>/` |
|
|
@@ -215,7 +216,7 @@ Deleting `~/.lamalibre/lamaste/` by hand instead would leave the agents' service
|
|
|
215
216
|
| **Chisel logs** | `~/.lamalibre/lamaste/agents/<label>/logs/chisel.log` |
|
|
216
217
|
| **Cloud tokens** | OS credential store (`com.lamalibre.cloud`) |
|
|
217
218
|
| **P12 passwords** | OS credential store (`com.lamalibre.lamaste.server`) |
|
|
218
|
-
| **npm package** | `@lamalibre/create-lamaste-desktop@4.
|
|
219
|
+
| **npm package** | `@lamalibre/create-lamaste-desktop@4.3.1` |
|
|
219
220
|
|
|
220
221
|
### Related Documentation
|
|
221
222
|
|
|
@@ -62,7 +62,7 @@ Execute the Lamaste installer:
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
apt install -y npm
|
|
65
|
-
npx @lamalibre/create-lamaste@4.
|
|
65
|
+
npx @lamalibre/create-lamaste@4.3.1
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
The installer displays a confirmation banner before making any changes:
|
|
@@ -90,7 +90,7 @@ The installer displays a confirmation banner before making any changes:
|
|
|
90
90
|
Press Enter to continue or Ctrl+C to abort...
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
Press **Enter** to proceed. To skip the confirmation prompt, use `npx @lamalibre/create-lamaste@4.
|
|
93
|
+
Press **Enter** to proceed. To skip the confirmation prompt, use `npx @lamalibre/create-lamaste@4.3.1 --yes`.
|
|
94
94
|
|
|
95
95
|
### 4. What the Installer Does
|
|
96
96
|
|
|
@@ -380,7 +380,7 @@ The service includes security hardening: `ProtectHome=true`, `PrivateTmp=true`,
|
|
|
380
380
|
|
|
381
381
|
| Item | Value |
|
|
382
382
|
| ------------------------ | ------------------------------------------------------------ |
|
|
383
|
-
| **Install command** | `npx @lamalibre/create-lamaste@4.
|
|
383
|
+
| **Install command** | `npx @lamalibre/create-lamaste@4.3.1` |
|
|
384
384
|
| **Minimum VPS** | Ubuntu 24.04, 512 MB RAM |
|
|
385
385
|
| **Required ports** | 22 (SSH), 80 (HTTP/Let's Encrypt), 443 (HTTPS), 9292 (panel) |
|
|
386
386
|
| **Certificate location** | `/etc/lamalibre/lamaste/pki/client.p12` |
|
|
@@ -113,7 +113,7 @@ Chisel is the tunneling engine. The installer already put the pinned Chisel rele
|
|
|
113
113
|
|
|
114
114
|
Authelia provides two-factor authentication for your tunneled apps. The installer already put the pinned Authelia release, its systemd unit and its `lamaste-authelia` service account in place; the provisioner checks the binary is there, writes the configuration file (using your domain), creates the initial admin user with a random password, and starts the service. Authelia listens on `127.0.0.1:9091`.
|
|
115
115
|
|
|
116
|
-
Onboarding downloads nothing. If either task fails with "… is not installed. Run `npx @lamalibre/create-lamaste@4.
|
|
116
|
+
Onboarding downloads nothing. If either task fails with "… is not installed. Run `npx @lamalibre/create-lamaste@4.3.1` on the server to repair the installation.", re-run the installer on the server (it is idempotent) and start provisioning again.
|
|
117
117
|
|
|
118
118
|
Authelia uses **bcrypt** for password hashing (not argon2id) because argon2id uses approximately 93 MB per hash, which causes out-of-memory kills on a 512 MB droplet.
|
|
119
119
|
|
|
@@ -213,7 +213,7 @@ Each provisioning task calls library functions in `packages/lamaste-serverd/src/
|
|
|
213
213
|
|
|
214
214
|
The DNS check resolves these hostnames using Node's `dns.resolve4()`:
|
|
215
215
|
|
|
216
|
-
1. `panel.example.com`, `auth.example.com`, `tunnel.example.com` — must resolve to the server IP (required; `CORE_SUBDOMAINS` in `@lamalibre/lamaste@4.
|
|
216
|
+
1. `panel.example.com`, `auth.example.com`, `tunnel.example.com` — must resolve to the server IP (required; `CORE_SUBDOMAINS` in `@lamalibre/lamaste@4.3.1`)
|
|
217
217
|
2. `test-lamaste-check.example.com` — tests wildcard resolution (optional)
|
|
218
218
|
3. `example.com` — reported, not required
|
|
219
219
|
|
|
@@ -27,7 +27,7 @@ Using the desktop app's cloud provisioning wizard:
|
|
|
27
27
|
|
|
28
28
|
- Enter your DigitalOcean API token
|
|
29
29
|
- Select a region and droplet size
|
|
30
|
-
- The wizard calls `@lamalibre/lamaste-cloud@4.
|
|
30
|
+
- The wizard calls `@lamalibre/lamaste-cloud@4.3.1` to create the droplet
|
|
31
31
|
- Progress streams back as NDJSON events in the desktop UI
|
|
32
32
|
- Complete onboarding in the browser panel
|
|
33
33
|
|
|
@@ -96,7 +96,7 @@ The Sync server now runs as a local service on the Mac. It operates independentl
|
|
|
96
96
|
|
|
97
97
|
### 5. Register Sync Scopes and Instance on Lamaste
|
|
98
98
|
|
|
99
|
-
The Sync server detects that the Mac has a Lamaste agent certificate. Using that cert (mTLS), it registers a ticket scope and instance with the Lamaste panel. This is handled automatically by `TicketInstanceManager` from `@lamalibre/lamaste-tickets@4.
|
|
99
|
+
The Sync server detects that the Mac has a Lamaste agent certificate. Using that cert (mTLS), it registers a ticket scope and instance with the Lamaste panel. This is handled automatically by `TicketInstanceManager` from `@lamalibre/lamaste-tickets@4.3.1` — the admin does not need to do anything.
|
|
100
100
|
|
|
101
101
|
```mermaid
|
|
102
102
|
sequenceDiagram
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Installer Flow
|
|
2
2
|
|
|
3
|
-
## What `npx @lamalibre/create-lamaste@4.
|
|
3
|
+
## What `npx @lamalibre/create-lamaste@4.3.1` Does
|
|
4
4
|
|
|
5
5
|
The installer is a **zero-prompt, fire-and-forget** command. It assumes:
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ It requires **no user input**. All configuration happens later through the brows
|
|
|
13
13
|
## Execution Flow
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
$ npx @lamalibre/create-lamaste@4.
|
|
16
|
+
$ npx @lamalibre/create-lamaste@4.3.1
|
|
17
17
|
|
|
18
18
|
Lamaste Installer v1.0.0
|
|
19
19
|
|