document squid's proxy credentials
This commit is contained in:
parent
fd63b402e1
commit
8328c38a07
3 changed files with 34 additions and 10 deletions
|
|
@ -212,13 +212,27 @@ DNS/gateway), so only expose services safe for any agent to reach.
|
||||||
|
|
||||||
Every hive host runs squid in the host netns, listening on `<bridgeIp>:3128`
|
Every hive host runs squid in the host netns, listening on `<bridgeIp>:3128`
|
||||||
(`nix/host-modules/hive-squid.nix`). The port is open on the bridge interface
|
(`nix/host-modules/hive-squid.nix`). The port is open on the bridge interface
|
||||||
only. **No agent uses it yet**: agent traffic still goes straight out through
|
only. **No agent uses it yet**: none sends a credential, and agent traffic
|
||||||
the NAT. The proxy goes first so you can check its metrics and logs before
|
still goes straight out through the NAT. The proxy goes first so you can check
|
||||||
anything reroutes.
|
its metrics and logs before anything reroutes.
|
||||||
|
|
||||||
Squid filters on the destination hostname: a `CONNECT` target or a plain-HTTP
|
Squid filters on the destination hostname: a `CONNECT` target or a plain-HTTP
|
||||||
host. It never decrypts TLS, so it holds no signing key. It caches nothing.
|
host. It never decrypts TLS, so it holds no signing key. It caches nothing.
|
||||||
|
|
||||||
|
- **Credentials.** A request for a listed name must carry a per-agent proxy
|
||||||
|
credential (HTTP Basic, the agent's name and its secret). `swarm-controller`
|
||||||
|
mints one per agent at `swarm/agents/<agent>/proxy-credential`, and never
|
||||||
|
replaces it. `hive-c0re` hashes the credentials of this hive's agents, and
|
||||||
|
one more for the self-check, into `services.hyperhive.squid.usersFile`
|
||||||
|
every 30 seconds. Squid's `basic_ncsa_auth` helper re-reads the file when it
|
||||||
|
changes. A stopped agent stays in the file. Squid caches a good login for a
|
||||||
|
minute. Nothing rotates a credential: to replace one, delete its leaf in the
|
||||||
|
store and the next pass mints a new one. HTTP Basic crosses the bridge in
|
||||||
|
clear between an agent and the proxy. See
|
||||||
|
[the credentials reference](../swarm/credentials-reference.md).
|
||||||
|
A name that isn't on the allow-list gets the 403 whether or not the request
|
||||||
|
has a credential; a listed name without one gets a 407.
|
||||||
|
|
||||||
- **Allow-list.** `services.hyperhive.squid.allowedDomains` defaults to the
|
- **Allow-list.** `services.hyperhive.squid.allowedDomains` defaults to the
|
||||||
swarm's own service names (`services.hyperhive.swarm.serviceDomains`). Each
|
swarm's own service names (`services.hyperhive.swarm.serviceDomains`). Each
|
||||||
entry matches that exact name. Everything else gets a 403.
|
entry matches that exact name. Everything else gets a 403.
|
||||||
|
|
@ -237,10 +251,17 @@ host. It never decrypts TLS, so it holds no signing key. It caches nothing.
|
||||||
unit and the self-check's to the swarm's log store. A refusal shows as `TCP_DENIED/403` with the
|
unit and the self-check's to the swarm's log store. A refusal shows as `TCP_DENIED/403` with the
|
||||||
client's source address.
|
client's source address.
|
||||||
- **Self-check.** `squid-selfcheck.timer` runs `squid-selfcheck.service`
|
- **Self-check.** `squid-selfcheck.timer` runs `squid-selfcheck.service`
|
||||||
every minute from the bridge IP: `CONNECT` to `services.hyperhive.squid.selfcheck.canary`
|
every minute from the bridge IP, logging in as `selfcheck.squid`: `CONNECT`
|
||||||
(default: the forge's name) must answer 200, and to `deny-canary.invalid`
|
to `services.hyperhive.squid.selfcheck.canary` (default: the forge's name)
|
||||||
must answer 403. It writes one `squid-selfcheck ok=… allow_status=…
|
must answer 200, to `deny-canary.invalid` 403, and to the canary without a
|
||||||
deny_status=…` line per run and fails the unit when either is wrong. The
|
credential 407. It writes one `squid-selfcheck ok=… allow_status=…
|
||||||
|
deny_status=… anon_status=… credential=present|absent` line per run and
|
||||||
|
fails the unit when any is wrong. The password is the hive's
|
||||||
|
`swarm/hives/<hive>/squid-selfcheck` secret, which
|
||||||
|
`squid-selfcheck-credential` lands at `/run/credstore/hive-squid.selfcheck`.
|
||||||
|
Before `swarm-controller` has minted it, or while `hive-c0re` hasn't
|
||||||
|
written the users file, the line reads `credential=absent` or
|
||||||
|
`allow_status=407` and `ok=false`, and nothing else breaks. The
|
||||||
host's resolver looks up the canary, so a host whose resolver maps
|
host's resolver looks up the canary, so a host whose resolver maps
|
||||||
that name to a loopback or bridge address (a single-host swarm with
|
that name to a loopback or bridge address (a single-host swarm with
|
||||||
`gateway.localHostsEntry`) fails the check.
|
`gateway.localHostsEntry`) fails the check.
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ hive's domain and `*.<hive domain>`, read from
|
||||||
`services.hyperhive.swarm.hives.<name>.domain` on the store's host, with no
|
`services.hyperhive.swarm.hives.<name>.domain` on the store's host, with no
|
||||||
IP SANs and no `localhost`. A request for any other name is a refusal from
|
IP SANs and no `localhost`. A request for any other name is a refusal from
|
||||||
the store naming that name. The hive's policy grants `update` on that one
|
the store naming that name. The hive's policy grants `update` on that one
|
||||||
issue path and `read` on its own `attic/public-key` and `attic/pull-token`
|
issue path and `read` on its own `attic/public-key`, `attic/pull-token` and
|
||||||
(see [the cache facts for hive-priv](credentials-reference.md)), and nothing
|
`squid-selfcheck`
|
||||||
|
(see [the cache facts for hive-priv](credentials-reference.md) and the
|
||||||
|
squid self-check password), and nothing
|
||||||
else. `swarm-bao-hive-leaf-policy.service`, on the
|
else. `swarm-bao-hive-leaf-policy.service`, on the
|
||||||
store's host, writes the role, the policy and the cert-auth role for every
|
store's host, writes the role, the policy and the cert-auth role for every
|
||||||
hive in `swarm.hives`.
|
hive in `swarm.hives`.
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,8 @@ pieces fit, and the reasoning behind them, is in [`credentials.md`](credentials.
|
||||||
| the forge's copy of its OIDC secret | `swarm-bao-forge-oidc.service` reads it out of the swarm secret store under the forge publisher's leaf, wherever authelia runs | `/run/hive-forge-oidc/<id>.secret` on the host (tmpfs), bound into the forge container, handed to `forgejo-sso-source` through `LoadCredential` |
|
| the forge's copy of its OIDC secret | `swarm-bao-forge-oidc.service` reads it out of the swarm secret store under the forge publisher's leaf, wherever authelia runs | `/run/hive-forge-oidc/<id>.secret` on the host (tmpfs), bound into the forge container, handed to `forgejo-sso-source` through `LoadCredential` |
|
||||||
| the homeserver's copy of its OIDC secret | `swarm-bao-matrix-oidc.service` reads it out of the swarm secret store under matrix-ctl's leaf, wherever authelia runs | `/run/hive-matrix-oidc/<id>.secret` on the host (tmpfs), bound into the homeserver container, handed to tuwunel through `LoadCredential` |
|
| the homeserver's copy of its OIDC secret | `swarm-bao-matrix-oidc.service` reads it out of the swarm secret store under matrix-ctl's leaf, wherever authelia runs | `/run/hive-matrix-oidc/<id>.secret` on the host (tmpfs), bound into the homeserver container, handed to tuwunel through `LoadCredential` |
|
||||||
| this hive's forge read token | `swarm-controller`, into the secret store at `swarm/hives/<hive>/forge/token`, for the `hive-<hive>` account; a pass at start and every five minutes re-mints a missing or stale token | `hive-c0re` reads it under the hive's own store identity, and copies it (account on the first line, token on the second) to `/run/hyperhive/forge-hive-token` (tmpfs, `0600` `hive-core`) for nix's `git`, which `hive-priv` also runs. Each `internal/meta` read and each boot rewrites the copy when it differs; where they differ after a rotation, a build that starts before the next read fails to authenticate, and a retry reads the new token |
|
| this hive's forge read token | `swarm-controller`, into the secret store at `swarm/hives/<hive>/forge/token`, for the `hive-<hive>` account; a pass at start and every five minutes re-mints a missing or stale token | `hive-c0re` reads it under the hive's own store identity, and copies it (account on the first line, token on the second) to `/run/hyperhive/forge-hive-token` (tmpfs, `0600` `hive-core`) for nix's `git`, which `hive-priv` also runs. Each `internal/meta` read and each boot rewrites the copy when it differs; where they differ after a rotation, a build that starts before the next read fails to authenticate, and a retry reads the new token |
|
||||||
| squid's users file | `hive-c0re` (`workers::squid_users`), every 30 seconds, from the proxy credentials of its agents and the self-check | `services.hyperhive.squid.usersFile` (`/var/lib/hive-squid/users`, `hive-core:squid`, `0640`): `<user>:<hash>` lines, SHA-512 crypt hashes only, renamed into place when it changed. `systemd-tmpfiles` creates it empty so squid's helper never sees a missing file. A hive whose agents have no credential yet (an older controller) has an empty file: every login fails and no agent sends a credential and squid doesn't read the file yet, so no traffic changes |
|
| squid's users file | `hive-c0re` (`workers::squid_users`), every 30 seconds, from the proxy credentials of its agents and the self-check | `services.hyperhive.squid.usersFile` (`/var/lib/hive-squid/users`, `hive-core:squid`, `0640`): `<user>:<hash>` lines, SHA-512 crypt hashes only, renamed into place when it changed. `systemd-tmpfiles` creates it empty so squid's helper never sees a missing file. A hive whose agents have no credential yet (an older controller) has an empty file: every login fails and the self-check reports `ok=false`; no agent sends a credential, so no traffic changes |
|
||||||
|
| the squid self-check's password | `swarm-controller` mints `swarm/hives/<hive>/squid-selfcheck`; `squid-selfcheck-credential` (root, every 10 minutes) reads it as the hive | `/run/credstore/hive-squid.selfcheck` (tmpfs, root, `0400`), imported by `squid-selfcheck.service` as a systemd credential and handed to `curl` on its stdin. Missing, or different from the users file: the self-check logs `credential=absent` or a 407 and fails, and nothing else breaks |
|
||||||
|
|
||||||
### Store identities, one per principal
|
### Store identities, one per principal
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue