Stage one of the per-project allowlist question: the correctness fix that stands on its own, plus the union written down as a bounded property. Isolation is deliberately not in here.
Removing a name revoked nothing
dnsmasq puts the addresses it answers with into the firewall's set, and nothing ever took them out. So removing a domain from a declaration and re-rendering stopped the name resolving and left every address it had ever resolved to still reachable, until the host rebooted. A job that had already looked the name up, or that reads the address out of the declaration's git history, connected exactly as before.
This is independent of the cross-project question and was true of a single-project host too.
The set holds addresses with no memory of which name put them there, so the only revocation ipset offers is to empty it.
Only on a removal. Flushing whenever anything changed would empty the set every time a name was added, and a job mid-run would find a destination it had already resolved refused until it looked it up again. Established connections are unaffected either way, since the firewall accepts ESTABLISHED,RELATED before it consults the set.
Before the reload, not after. The reload is a SIGHUP, which clears dnsmasq's cache, so the next query for a name that is still allowed goes upstream and puts its address back. The other order leaves cached answers that repopulate nothing, and names still on the list unreachable until their TTL expires. There is a test asserting the order rather than trusting the comment.
A missing set is reported. The names leave the resolver either way, so it half-succeeds, and an operator who is not told believes a revocation took effect.
The union, stated rather than discovered
Not bounded now carries it: one project's declaration is reachable from every project's jobs. With the bound that makes it a blast-radius question rather than an open door, since a repository in repos_file is already trusted to run code on that host, and the declaration is read from main.
Rejected, with reasons says what isolating them takes, in the order the pieces constrain each other. The part worth writing down is why a partial fix is not worth building: a per-project resolver without a per-project set buys almost nothing. The set is addresses, every bridge's rule matches the same one, and declarations are public, so a project that cannot resolve a name can still connect to the address another project's lookup put there. Verified from dnsmasq's man page that one resolver can populate several sets per name (ipset=/name/set-a,set-b), which is what keeps the eventual design at one dnsmasq rather than one per project.
Tested: 153 passing, up from 148. Six new tests; the three asserting the new behaviour fail against main, and the other three are guards against over-flushing that pass there by design.
**Summary generated by LLM agent**
Stage one of the per-project allowlist question: the correctness fix that stands on its own, plus the union written down as a bounded property. Isolation is deliberately not in here.
## Removing a name revoked nothing
dnsmasq puts the addresses it answers with into the firewall's set, and nothing ever took them out. So removing a domain from a declaration and re-rendering stopped the name resolving and left **every address it had ever resolved to still reachable**, until the host rebooted. A job that had already looked the name up, or that reads the address out of the declaration's git history, connected exactly as before.
This is independent of the cross-project question and was true of a single-project host too.
The set holds addresses with no memory of which name put them there, so the only revocation ipset offers is to empty it.
**Only on a removal.** Flushing whenever anything changed would empty the set every time a name was added, and a job mid-run would find a destination it had already resolved refused until it looked it up again. Established connections are unaffected either way, since the firewall accepts `ESTABLISHED,RELATED` before it consults the set.
**Before the reload, not after.** The reload is a SIGHUP, which clears dnsmasq's cache, so the next query for a name that is still allowed goes upstream and puts its address back. The other order leaves cached answers that repopulate nothing, and names still on the list unreachable until their TTL expires. There is a test asserting the order rather than trusting the comment.
**A missing set is reported.** The names leave the resolver either way, so it half-succeeds, and an operator who is not told believes a revocation took effect.
## The union, stated rather than discovered
`Not bounded` now carries it: one project's declaration is reachable from every project's jobs. With the bound that makes it a blast-radius question rather than an open door, since a repository in `repos_file` is already trusted to run code on that host, and the declaration is read from `main`.
`Rejected, with reasons` says what isolating them takes, in the order the pieces constrain each other. The part worth writing down is why a partial fix is not worth building: **a per-project resolver without a per-project set buys almost nothing.** The set is addresses, every bridge's rule matches the same one, and declarations are public, so a project that cannot resolve a name can still connect to the address another project's lookup put there. Verified from dnsmasq's man page that one resolver can populate several sets per name (`ipset=/name/set-a,set-b`), which is what keeps the eventual design at one dnsmasq rather than one per project.
Tested: 153 passing, up from 148. Six new tests; the three asserting the new behaviour fail against `main`, and the other three are guards against over-flushing that pass there by design.
branderson
approved these changes 2026-09-01 02:41:36 -07:00
dnsmasq puts the addresses it answers with into the firewall's set, and nothing
ever took them out. So removing a domain from a declaration and re-rendering
stopped the name resolving and left every address it had ever resolved to
reachable, until the host rebooted. A job that had already looked the name up,
or that read the address out of the declaration's history, connected exactly as
before. Revocation looked like it worked and did not.
The set holds addresses with no memory of which name put them there, so the
only revocation ipset offers is to empty it. Flushed on a removal, and only on
a removal: flushing whenever anything changed would empty it every time a name
was added, and a job mid-run would find a destination it had already resolved
refused until it looked it up again. Established connections are unaffected
either way, since the firewall accepts ESTABLISHED,RELATED before it consults
the set.
Before the reload, not after. The reload is a SIGHUP, which clears dnsmasq's
cache, so the next query for a name that is still allowed goes upstream and
puts its address back. The other order leaves cached answers that repopulate
nothing and names still on the list unreachable until their TTL expires.
A set that is not there is reported rather than passed over. The names go out
of the resolver either way, so it half-succeeds, and an operator who is not told
believes a revocation took effect.
The threat model now states the union as a bounded property rather than leaving
it to be discovered: one project's declaration is reachable from every
project's jobs, bounded by the fact that a repository on that list is already
trusted to run code on the host. What isolating them would take is written down
under Rejected, with the constraint that decides the shape - a per-project
resolver without a per-project set buys almost nothing, because the set is
addresses and the addresses are shared.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Stage one of the per-project allowlist question: the correctness fix that stands on its own, plus the union written down as a bounded property. Isolation is deliberately not in here.
Removing a name revoked nothing
dnsmasq puts the addresses it answers with into the firewall's set, and nothing ever took them out. So removing a domain from a declaration and re-rendering stopped the name resolving and left every address it had ever resolved to still reachable, until the host rebooted. A job that had already looked the name up, or that reads the address out of the declaration's git history, connected exactly as before.
This is independent of the cross-project question and was true of a single-project host too.
The set holds addresses with no memory of which name put them there, so the only revocation ipset offers is to empty it.
Only on a removal. Flushing whenever anything changed would empty the set every time a name was added, and a job mid-run would find a destination it had already resolved refused until it looked it up again. Established connections are unaffected either way, since the firewall accepts
ESTABLISHED,RELATEDbefore it consults the set.Before the reload, not after. The reload is a SIGHUP, which clears dnsmasq's cache, so the next query for a name that is still allowed goes upstream and puts its address back. The other order leaves cached answers that repopulate nothing, and names still on the list unreachable until their TTL expires. There is a test asserting the order rather than trusting the comment.
A missing set is reported. The names leave the resolver either way, so it half-succeeds, and an operator who is not told believes a revocation took effect.
The union, stated rather than discovered
Not boundednow carries it: one project's declaration is reachable from every project's jobs. With the bound that makes it a blast-radius question rather than an open door, since a repository inrepos_fileis already trusted to run code on that host, and the declaration is read frommain.Rejected, with reasonssays what isolating them takes, in the order the pieces constrain each other. The part worth writing down is why a partial fix is not worth building: a per-project resolver without a per-project set buys almost nothing. The set is addresses, every bridge's rule matches the same one, and declarations are public, so a project that cannot resolve a name can still connect to the address another project's lookup put there. Verified from dnsmasq's man page that one resolver can populate several sets per name (ipset=/name/set-a,set-b), which is what keeps the eventual design at one dnsmasq rather than one per project.Tested: 153 passing, up from 148. Six new tests; the three asserting the new behaviour fail against
main, and the other three are guards against over-flushing that pass there by design.b0285b164ctof8366eccb5