squid blocks a runtime deny list from the controller in every egress mode, ahead of the allow list
This commit is contained in:
parent
99ab0ae77f
commit
dd5fde848f
2 changed files with 1814 additions and 24 deletions
|
|
@ -309,6 +309,60 @@ host. It never decrypts TLS, so it holds no signing key. It caches nothing.
|
|||
time or a count. The self-check's users aren't agents,
|
||||
so `deny-canary.invalid` stays a 403.
|
||||
|
||||
- **Modes.** An agent is in one of three modes, and the deny list (next bullet)
|
||||
applies in all of them:
|
||||
|
||||
| mode | the deny list | a name not on the allow list |
|
||||
| -------------- | ------------- | ---------------------------- |
|
||||
| `restricted` | blocked | blocked |
|
||||
| `log` | blocked | allowed and logged |
|
||||
| `unrestricted` | blocked | allowed |
|
||||
|
||||
- **Deny list.** The swarm's deny list names destinations the proxy refuses
|
||||
whatever else allows them. The swarm controller keeps it as its own value
|
||||
(the `deny` key of the `egress-allow` bucket), each entry for every agent or
|
||||
for one, and serves it at `GET`/`POST`/`DELETE /api/egress/denied-domains`
|
||||
with the same body as the allow-list (`{"domain": ".example.com", "agent":
|
||||
"atlas"}`). Write a name as in the allow-list: at least two labels, one
|
||||
leading dot for the name and its subdomains. The controller refuses IP
|
||||
addresses, other wildcards, ports and paths. Unlike the allow-list it doesn't
|
||||
refuse the base list's names: denying one is the point. Every 30 seconds
|
||||
`hive-c0re` reads the list and renders `services.hyperhive.deploy.hive-controller.squid.denyFile`
|
||||
(`/var/lib/hive-squid/deny.conf`, `hive-core:squid`, `0640`) for the agents it
|
||||
runs, and `squid-unrestricted-reload.path` reloads squid when it changes.
|
||||
- **Precedence.** The configuration includes the file after the destination
|
||||
denies and before `deny !authed` and every allow, so a denied name is
|
||||
refused even when the base list, the hive's `allowedDomains`, the swarm's
|
||||
allow-list, the unrestricted flag or `log` mode would allow it. A swarm-wide
|
||||
entry names no user, so every client gets a 403 for it, one without a
|
||||
credential too. An agent's entry also names the agent's squid user, so it
|
||||
binds that agent: a client that sent no credential gets the usual 407 for
|
||||
it. An agent's entry only adds to the swarm-wide ones; no entry of one scope
|
||||
lifts another.
|
||||
- **Log line.** A request a deny rule refused gets a second log line,
|
||||
`egress_denied user=<agent or -> method=<m> host=<h> port=<p> result=<r>`,
|
||||
in every mode. It differs from the `would_deny` line of `log` mode and
|
||||
from the `TCP_DENIED/403` line every refusal has, so you can tell a
|
||||
deny-list hit from a name that isn't on the allow-list.
|
||||
- **Who writes it.** Only an operator's request to the controller writes the
|
||||
list. It has no automatic entries: nothing derives a deny from an agent's
|
||||
API-key environment or a linked account, and the stored type has no such
|
||||
field. An agent has no queue grant that reaches the key, and the controller's
|
||||
API sits behind the operator's SSO login: nginx checks it before it hands
|
||||
a request to the socket, which only nginx and root can open.
|
||||
- **Empty list.** With no entry `deny.conf` is empty and squid configures as
|
||||
it did without the list: the configuration gains one `include` line and the
|
||||
`egress_denied` log lines, and no rule. A hive on a `hive-c0re` that
|
||||
predates the list never reads the key and keeps its old configuration, so
|
||||
nothing on its wire changes.
|
||||
- **Applied.** Each hive reports the revision of the list it last rendered as
|
||||
`egress_deny_applied` in its status snapshot (see [the hive
|
||||
snapshot](../swarm/README.md)). `GET /api/egress/denied-domains` answers the
|
||||
list's `revision` and `behind`, the hives that haven't reported it: an older
|
||||
`hive-c0re` never does, so it shows there rather than silently allowing the
|
||||
names. A hive that reads the list reports within about a minute. Reporting
|
||||
means the file holds the rules; squid reloads moments later.
|
||||
|
||||
- **Base list.** `services.hyperhive.deploy.hive-controller.squid.baseAllowedDomains` is open to every
|
||||
authenticated user, whatever its flag: what the agent harness needs off the
|
||||
swarm. The default is the hosts Claude Code requires
|
||||
|
|
@ -419,7 +473,7 @@ host. It never decrypts TLS, so it holds no signing key. It caches nothing.
|
|||
`squid.service`, and `services.hyperhive.otel.journalUnits` forwards that
|
||||
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. A request that a `log`-mode agent's rule let
|
||||
through also logs a `would_deny` line (see _Log mode_).
|
||||
through also logs a `would_deny` line (see _Log mode_). A request a deny-list rule refused logs an `egress_denied` line (see _Deny list_).
|
||||
- **Self-check.** `squid-selfcheck.timer` runs `squid-selfcheck.service`
|
||||
every minute from the bridge IP, logging in as `selfcheck.squid`: `CONNECT`
|
||||
to `services.hyperhive.deploy.hive-controller.squid.selfcheck.canary` (default: `cache.nixos.org`,
|
||||
|
|
|
|||
|
|
@ -99,6 +99,11 @@ of names for what the hive's release can do. Two exist: `tolerant-permissions`,
|
|||
[turning an archetype agent into a custom one](archetypes.md#turn-into-custom)
|
||||
needs. A hive on an older release lists none.
|
||||
|
||||
The snapshot also carries `egress_deny_applied`, the revision of the egress deny
|
||||
list the hive last rendered into its proxy (`0` when the swarm has none). A hive
|
||||
on an older release, or one that hasn't read the list yet, leaves it out; see
|
||||
[the egress proxy](../networking/network.md#egress-proxy-squid).
|
||||
|
||||
Every row also carries `last_seen_unix` and `age_seconds` if you want to
|
||||
apply your own threshold. The timestamp is the one the queue recorded on
|
||||
arrival, not one the hive put in its own payload. A roster hive's row
|
||||
|
|
|
|||
Loading…
Reference in a new issue