Install an agent on a one-off PC from a single command
An admin generates a short-lived token in Settings > Endpoints > Endpoints and
copies the command it produces. A tech runs that one line in an elevated
PowerShell on a PC with no agent:
powershell -ExecutionPolicy Bypass -c "irm https://<server>/shopdb/api/
endpoints/siteagent/bootstrap.ps1?t=<token> | iex"
The script refuses to run unelevated, refuses a PC that already has the agent
(a managed bay gets it from GE-Enforce), downloads the package the existing
builder produces, checks both binaries against the hashes the server baked in,
installs with NO shared key, and reports what happened. The PC then asks to
enroll and waits for an admin to approve it, which the script watches for
locally for two minutes before exiting.
- New endpointbootstraptokens table and migration
endpoints0024bootstraptokens: hash only, expiry, use count, revoke.
- Admin routes to generate, list and revoke tokens; the secret is shown once.
- Two token-only routes: the script, and the package (which spends a use).
Anything wrong with the token answers 404 rather than confirming it exists,
both are rate limited per address and both are audited with the caller.
- The package builder is now shared rather than duplicated, and a bootstrap
package can never carry a collector key.
This commit is contained in:
@@ -763,3 +763,74 @@ Read needs `endpoints.fleet`; writes need the admin role and are audited.
|
||||
`{"assetids": [...]}`. An explicit id is queued when this is the PC's only
|
||||
blocker and is reported in `retried`; `{"next": n}` never picks such a PC, and
|
||||
a PC with any other blocker as well is still skipped.
|
||||
|
||||
### 9.8 One-line bootstrap install
|
||||
|
||||
For a ONE-OFF PC. A managed bay keeps getting the agent from GE-Enforce; this
|
||||
is the path for a machine nobody is going to author a manifest entry for.
|
||||
|
||||
An admin mints a short-lived token in Settings > Endpoints > Build site agent >
|
||||
One-line install and hands a technician one command, which is run in an
|
||||
ELEVATED PowerShell on a PC with no agent:
|
||||
|
||||
```
|
||||
powershell -ExecutionPolicy Bypass -c "irm <baseurl>/api/endpoints/siteagent/bootstrap.ps1?t=<token> | iex"
|
||||
```
|
||||
|
||||
The token is only an authorization to FETCH the installer. The package it
|
||||
serves carries no shared key, whatever `plugin.endpoints.sharedkeyallowed`
|
||||
says, so the PC enrolls by the normal 9.2 path and waits in Pending until an
|
||||
admin approves it.
|
||||
|
||||
Admin routes (admin role, audited):
|
||||
|
||||
- `POST /api/endpoints/siteagent/bootstrap/tokens` `{"hours": 24, "maxuses": 25}`
|
||||
(`hours` 1-168, `maxuses` 1-500). Returns `{"secret", "command", "token"}`.
|
||||
The secret is in the response exactly once; only its sha256 and a 12-character
|
||||
`tokenprefix` are stored.
|
||||
- `GET /api/endpoints/siteagent/bootstrap/tokens` - prefix, created, expires,
|
||||
`usecount` / `maxuses`, `revokedat`, `lastusedat`, `lastusedip` and a `state`
|
||||
of `active`, `expired`, `exhausted` or `revoked`. Never a secret.
|
||||
- `POST /api/endpoints/siteagent/bootstrap/tokens/<tokenid>/revoke`
|
||||
|
||||
Token-authenticated routes. No login; the token is the whole authorization and
|
||||
travels in the query string as `t`, because a PC with no agent has neither a
|
||||
credential nor a user session to put in a header:
|
||||
|
||||
| Route | Spends a use | Returns |
|
||||
|---|---|---|
|
||||
| `GET /api/endpoints/siteagent/bootstrap.ps1?t=...` | no | `text/plain` PowerShell, generated per token |
|
||||
| `GET /api/endpoints/siteagent/bootstrap/package?t=...` | yes | the keyless agent package ZIP |
|
||||
|
||||
Both answer **404** for a token that is unknown, malformed, expired, revoked or
|
||||
exhausted, so the URL confirms nothing about which tokens exist. Both are
|
||||
rate-limited per IP (fixed window, `ENDPOINTS_BOOTSTRAP_RATELIMIT_MAX` in
|
||||
`ENDPOINTS_BOOTSTRAP_RATELIMIT_WINDOW_SECONDS`, 429 `RATE_LIMITED` over
|
||||
budget) and both write an audit row against `EndpointBootstrapToken` carrying
|
||||
the caller's IP. Fetching the script deliberately does not spend a use: a
|
||||
retried paste must not burn the download budget.
|
||||
|
||||
The generated script:
|
||||
|
||||
- refuses to run unelevated (exit 1) and says to reopen PowerShell as
|
||||
administrator;
|
||||
- refuses when the agent service already exists (exit 2) and says a managed bay
|
||||
should get the agent from GE-Enforce, unless `SHOPDB_BOOTSTRAP_FORCE` is set
|
||||
to anything but `0` in the same session;
|
||||
- downloads the package to a temp directory over TLS with normal chain
|
||||
validation (nothing relaxes certificate checking), expands it, and verifies
|
||||
`shopdbagent.exe` and `shopdb-endpoint-tray.exe` against the SHA-256 values
|
||||
the server baked in at generation time. A mismatch, a missing binary, or a
|
||||
binary the server never staged refuses the install (exit 3);
|
||||
- runs the package's `install.ps1` with no key, then deletes the temp
|
||||
directory;
|
||||
- prints the installed version and service state, and exits 4 when the service
|
||||
is not Running;
|
||||
- prints "This PC is waiting for approval - an admin approves it in Settings >
|
||||
Endpoints > Enrollment", then watches
|
||||
`%ProgramData%\ShopDB\EndpointAgent\credential\credential.bin` for up to two
|
||||
minutes and prints "approved and enrolled" if the credential lands. This is a
|
||||
LOCAL file watch, not a server poll: there is no public enrollment-status
|
||||
route and adding one would hand an anonymous caller a way to probe the fleet.
|
||||
|
||||
Exit code 0 on install success even when the PC is still pending.
|
||||
|
||||
Reference in New Issue
Block a user