swarmctl: log in with the operator's own SSO account and reach the controller through the swarm domain
`swarmctl --swarm https://<swarm> login` runs the OAuth 2.0 authorization-code flow with PKCE against the swarm's authelia as a new public `swarmctl` client: a pushed authorization request, the browser login and explicit consent, and the code delivered by form_post to a loopback port. The access and refresh tokens are cached at mode 0600 under $XDG_STATE_HOME/swarmctl/ and refreshed under a file lock, since authelia rotates the refresh token and revokes the login when a used one is presented again. With --swarm (or SWARMCTL_SWARM), the agent, schedule and forge verbs send their requests to https://<swarm>/api/ with the bearer token instead of over the controller's unix socket, which stays the default. authelia registers the client on every authelia host with a swarm domain: `kind = "native"`, public, no minted secret, scopes offline_access and authelia.bearer.authz, audience https://<swarm>/api/ (matched as a URL prefix), redirect http://127.0.0.1/callback (any loopback port). Every field is one authelia 4.39.20's validator requires for a public client holding the bearer scope. The swarm apex's /api/ answers a refused caller that sent an Authorization header with the 401 itself instead of the login redirect, so the CLI can tell "log in again" apart from an answer; a browser still gets the redirect. The bearer token is not forwarded to the controller. Refs #4409 (mara: c94764, c94782).
This commit is contained in:
parent
5c15580060
commit
2df19e4899
17 changed files with 1118 additions and 122 deletions
|
|
@ -1,7 +1,9 @@
|
|||
# swarmctl
|
||||
|
||||
Swarm-level operator CLI. Runs as **root on the host running
|
||||
`swarm-controller`**, and acts on that host directly.
|
||||
`swarm-controller`**, and acts on that host directly; or, for the verbs
|
||||
that talk to the controller, from anywhere as your own SSO account (see
|
||||
[Remote use](#remote-use-swarmctl-login)).
|
||||
|
||||
Distinct from `hivectl`, which drives one hive's `hive-c0re` over its
|
||||
admin socket. This crate does not link `swarm-controller`, for the same
|
||||
|
|
@ -19,15 +21,11 @@ allocated inside it, at activation — see the uid-assignment issue) or
|
|||
world-readable password hashes. Both are worse than root.
|
||||
|
||||
So this binary **serves** no socket, publishes no HTTP route and has no
|
||||
privileged helper. When a verb has to run as a non-root user or from
|
||||
another host, the answer is a **group-gated admin socket**, separate from
|
||||
the controller's `0666` gateway-facing one — not a widening of what root
|
||||
does here.
|
||||
privileged helper.
|
||||
|
||||
That rules out a way in, not a way out: `agent create` is a _client_ of
|
||||
the controller's socket, because the work it asks for is a job graph only
|
||||
the controller can queue (see below). Nothing here becomes reachable by
|
||||
that.
|
||||
the controller, because the work it asks for is a job graph only the
|
||||
controller can queue (see below). Nothing here becomes reachable by that.
|
||||
|
||||
## One file, two writers
|
||||
|
||||
|
|
@ -53,6 +51,8 @@ an error.
|
|||
| `SWARMCTL_AUTHELIA_MACHINE` | container name, for `systemctl -M` |
|
||||
| `SWARMCTL_AUTHELIA_UNIT` | authelia's unit inside that container |
|
||||
| `SWARM_CONTROLLER_SOCKET` | the controller's unix socket, for `agent` and `forge` verbs |
|
||||
| `SWARMCTL_SWARM` | `https://<swarm domain>`: reach the controller there, not the socket |
|
||||
| `SWARMCTL_CA_FILE` | a PEM CA to trust beside the system roots, for `SWARMCTL_SWARM` |
|
||||
|
||||
Every verb and flag: [`docs/tools/swarmctl-cli.md`](../docs/tools/swarmctl-cli.md).
|
||||
The sections below cover why each verb behaves as it does.
|
||||
|
|
@ -102,8 +102,9 @@ the swarm UI's job view for the rest.
|
|||
The new agent starts `paused`: it doesn't drive turns until you set it `up`
|
||||
in the swarm UI.
|
||||
|
||||
No approval gate, for the same reason nothing else here has one: running
|
||||
this binary already means being root on the controller's host.
|
||||
No approval gate, for the same reason nothing else here has one: the
|
||||
caller is root on the controller's host, or logged in through the swarm
|
||||
domain's SSO gate.
|
||||
|
||||
`--hive` is required. It is an _address_ — where the deploy message goes
|
||||
— not an attribute of the agent, so there is no sensible default. The
|
||||
|
|
@ -169,3 +170,27 @@ same socket as `agent create`; each verb waits for the stored result. What a
|
|||
schedule does once stored is in
|
||||
[`docs/swarm/schedules.md`](../docs/swarm/schedules.md). The shapes are
|
||||
mirrored in `src/schedule.rs`, for the same reason as `agent create`'s.
|
||||
|
||||
## Remote use: `swarmctl login`
|
||||
|
||||
The `agent`, `schedule` and `forge` verbs also work from any machine, as
|
||||
your own SSO account, through the swarm domain's gateway:
|
||||
|
||||
```console
|
||||
$ export SWARMCTL_SWARM=https://swarm.example.com
|
||||
$ swarmctl login
|
||||
Log in to https://swarm.example.com/ in your browser:
|
||||
…
|
||||
logged in to https://swarm.example.com/; login kept in ~/.local/state/swarmctl/swarm.example.com.json
|
||||
$ swarmctl schedule list
|
||||
```
|
||||
|
||||
`login` opens the swarm's SSO login in a browser and receives the result on
|
||||
a loopback port, so run it on the machine whose browser you use. swarmctl
|
||||
keeps the login, mode 0600, under `$XDG_STATE_HOME/swarmctl/` (default
|
||||
`~/.local/state`) and refreshes it as it's used. When it can't be refreshed,
|
||||
any verb says to run `swarmctl login` again. To log out, delete the file.
|
||||
|
||||
A swarm whose gateway serves a certificate from the swarm's own CA needs
|
||||
that CA: `--ca-file` or `SWARMCTL_CA_FILE`. The `user` verbs edit the local
|
||||
users file and have no remote form.
|
||||
|
|
|
|||
Loading…
Reference in a new issue