You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
cloudfall audit reports false drift once a PostgreSQL service declares remoteAccess #143
Found while building #123 (PR #141, spec S-006 remoteAccess).
The problem
cloudfall audit will report false drift on a server whose PostgreSQL service declares remoteAccess:
sdk/src/cloudfall/audit.py compares the host's nftables rules without their source match, so the new ip saddr <cidr> tcp dport 5432 accept rule reads as an unexpected open tcp/5432 and the firewall check reports a mismatch
its listener check flags PostgreSQL listening on listenAddress as an exposed port, though the firewall only lets the declared sources in
Reproduce
Once #141 lands: declare remoteAccess with listenAddress: 10.0.0.2 and allowedSources: [10.0.0.0/24] on a PostgreSQL service, converge baseline and services, then run cloudfall audit. Expected: no finding for port 5432. Actual (from reading the code, not run live): a firewall mismatch and an exposed-listener finding.
Proposed fix
Teach audit.py the rendered allowedInbound entries' sources: a rule matches when its port, protocol and source CIDRs match, and a listener on a port open only to declared sources is not "exposed". Servers without remoteAccess audit exactly as today.
Triage: implement, for the next release candidate.
Confirmed on main (2828e49) by reading the code, as the report says:
firewall.allowedInbound in sdk/src/cloudfall/audit.py builds its expected list from firewall_rules_for_server, which knows only the server type's declared rules. The remoteAccess rules are added later, in the engine's _remote_access_rules (engine/src/cloudfall_engine/ansible_inventory.py), so the expected list lacks tcp/5432 while the host has it: a mismatch on every server with remoteAccess
_accepted_inbound_endpoint drops the ip saddr match, so even with the rule expected, a rule open to every address would read the same as one narrowed to the declared sources
services.bind[<id>] counts any non-loopback listener on the port as exposed, so PostgreSQL on listenAddress fails it
The plan:
Derive the source-restricted entries in the SDK (one function both audit.py and the engine's inventory use, so they can't drift) and add them to the expected firewall rules
Read the ip saddr / ip6 saddr match of each accepted rule; a restricted rule compares as tcp/5432 from 10.0.0.0/24, one entry per source as nftables holds them. Rules without sources keep today's tcp/22 form, so servers without remoteAccess audit exactly as today
services.bind accepts a listener on the service's remoteAccess.listenAddress when the declared sources narrow the port; any other non-loopback listener is still exposed
Tests: a snapshot with the rendered remoteAccess rule audits clean; the same port open to every address, a wrong source, and a listener on an undeclared address each still fail
@romamo Decided without waiting: cloudfall audit shows a source-restricted firewall rule as tcp/5432 from 10.0.0.0/24 in firewall.allowedInbound, one entry per source, and keeps tcp/22 for rules without sources.
Why: the check has to tell a rule narrowed to the declared sources from one open to every address, or the audit would pass a wide-open PostgreSQL port; the new form appears only on servers with remoteAccess, so every existing audit reads as before.
Not taken: keep the protocol/port form and compare sources only internally, which keeps the output shorter but hides which source drifted.
Reply with a comment starting with veto before 2026-10-28 11:15 UTC to reverse it, adding what you want instead if you like. A later veto still counts and gets a follow-up.
The fix is in #148: firewall.allowedInbound now expects one tcp/5432 from <cidr> entry per declared source and reads the ip saddr/ip6 saddr match of each nftables rule, and services.bind requires PostgreSQL to listen on both 127.0.0.1 and the declared listenAddress. The engine and the audit derive the source rules from one SDK function, remote_access_rules_for_server. Servers without remoteAccess audit exactly as before, and a test pins that. The behaviour change for remoteAccess servers: a PostgreSQL that failed to bind listenAddress now reads as drift, and so does any source rule nftables holds in a form the audit doesn't parse (a set, a negation). Unverified: the nft JSON shapes are taken from libnftables' documented format, not captured on a live host; the S-006-9 proving run in #145 can confirm them. This issue closes when #148 merges.
Released in v0.8.0rc7 (pre-release). uv tool install --prerelease allow cloudfall==0.8.0rc7
A plain install gets it with the next stable release.
If you filed this: react 👍 on this comment once it works for you, or reply if it doesn't.
Activity
shipmill-romamo commentedon Oct 27, 2026
Triage: implement, for the next release candidate.
Confirmed on main (2828e49) by reading the code, as the report says:
firewall.allowedInboundinsdk/src/cloudfall/audit.pybuilds its expected list fromfirewall_rules_for_server, which knows only the server type's declared rules. TheremoteAccessrules are added later, in the engine's_remote_access_rules(engine/src/cloudfall_engine/ansible_inventory.py), so the expected list lackstcp/5432while the host has it: a mismatch on every server withremoteAccess_accepted_inbound_endpointdrops theip saddrmatch, so even with the rule expected, a rule open to every address would read the same as one narrowed to the declared sourcesservices.bind[<id>]counts any non-loopback listener on the port as exposed, so PostgreSQL onlistenAddressfails itThe plan:
audit.pyand the engine's inventory use, so they can't drift) and add them to the expected firewall rulesip saddr/ip6 saddrmatch of each accepted rule; a restricted rule compares astcp/5432 from 10.0.0.0/24, one entry per source as nftables holds them. Rules without sources keep today'stcp/22form, so servers withoutremoteAccessaudit exactly as todayservices.bindaccepts a listener on the service'sremoteAccess.listenAddresswhen the declared sources narrow the port; any other non-loopback listener is still exposedremoteAccessrule audits clean; the same port open to every address, a wrong source, and a listener on an undeclared address each still failI'll link the PR here.
shipmill-romamo commentedon Oct 27, 2026
@romamo Decided without waiting:
cloudfall auditshows a source-restricted firewall rule astcp/5432 from 10.0.0.0/24infirewall.allowedInbound, one entry per source, and keepstcp/22for rules without sources.Why: the check has to tell a rule narrowed to the declared sources from one open to every address, or the audit would pass a wide-open PostgreSQL port; the new form appears only on servers with
remoteAccess, so every existing audit reads as before.Not taken: keep the
protocol/portform and compare sources only internally, which keeps the output shorter but hides which source drifted.Reply with a comment starting with
vetobefore 2026-10-28 11:15 UTC to reverse it, adding what you want instead if you like. A later veto still counts and gets a follow-up.Require the remoteAccess listener in services.bind (#143)
shipmill-romamo commentedon Oct 27, 2026
The fix is in #148:
firewall.allowedInboundnow expects onetcp/5432 from <cidr>entry per declared source and reads theip saddr/ip6 saddrmatch of each nftables rule, andservices.bindrequires PostgreSQL to listen on both127.0.0.1and the declaredlistenAddress. The engine and the audit derive the source rules from one SDK function,remote_access_rules_for_server. Servers withoutremoteAccessaudit exactly as before, and a test pins that. The behaviour change for remoteAccess servers: a PostgreSQL that failed to bindlistenAddressnow reads as drift, and so does any source rule nftables holds in a form the audit doesn't parse (a set, a negation). Unverified: the nft JSON shapes are taken from libnftables' documented format, not captured on a live host; the S-006-9 proving run in #145 can confirm them. This issue closes when #148 merges.Audit remoteAccess firewall rules and listener without false drift (#143
shipmill-romamo commentedon Oct 27, 2026
Released in v0.8.0rc7 (pre-release). uv tool install --prerelease allow cloudfall==0.8.0rc7
A plain install gets it with the next stable release.
If you filed this: react 👍 on this comment once it works for you, or reply if it doesn't.