Repository navigation
fix(classifier): decode masked ct-mark writes in the install's conflict scan (0.7 backport) - #3033
Merged
Merged
Conversation
…ct scan (#3004) * fix(classifier): decode masked ct-mark writes in the install's conflict scan nft prints an iptables-nft CONNMARK --save-mark --nfmask M --ctmask M through libxtables as `ct mark set mark and 0xM`, which drops the --ctmask. The scan read it as a write of every bit and refused to install over Tailscale's rp_filter workaround rules. The kernel keeps every connection-mark bit outside --ctmask, so those rules change the bits M alone. The scan now decodes each `ct mark set` value for the bits it can change: the libxtables save spelling changes M, a value read from the ct mark changes the bits an `and` clears and an `or`/`xor` names, and any other value replaces the whole mark. An `and` that clears the install's bits is now a conflict too. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 * test(minimald): pin the conflict scan's verdict on libxtables ct-mark saves The mask 0x00030000 lies in Tailscale's well-known range, so the install refused it before the conflict scan ran, and the assertion held whatever the decoder did. The table now runs `ct mark set mark and 0xM` against the default bits, over them and beside them, and each refusal must be the conflict scan's. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 * fix(classifier): decode every ct mark set on a rule line in the conflict scan The scan decoded only the first `ct mark set` on a line and read later ones for 0x values alone, so a whole-mark write after a masked one (`ct mark set mark and 0xff0000 ct mark set meta mark`) passed. Each statement is now decoded from where the previous value ended. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 * fix(classifier): read xt compat CONNMARK masks from iptables-save A rule iptables-nft wrote is an xt compat target in the kernel (`[ target name CONNMARK rev 2 ]`), and `nft list` prints it through libxtables, which drops a --ctmask or names no bits at all. The scan now reads `nft -a --debug=netlink list ruleset`, finds each rule whose kernel form carries an xt target by family, table, chain and handle, and decodes it from the nf_tables iptables-save and ip6tables-save listings instead of its nft text. A CONNMARK no listing covers is refused, because the step cannot see which bits it keeps. ip6tables-save is now read the way iptables-save is, for both backends. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 * fix(classifier): fall back to the plain listing when nft prints no netlink form An nft that cannot run `nft -a --debug=netlink list ruleset` blocked every install, though the plain listing had just been read. The scan now uses the plain listing there, and finds an untranslated CONNMARK by its `xt target CONNMARK` text, so one no iptables-save listing covers is still refused. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 * test(install): serve the annotated ruleset listing from the stand-in nft The conflict scan now also runs `nft -a --debug=netlink list ruleset`, and the installer harness's stand-in nft answered it with nothing, so the staged conflict was never read. The stand-in serves the same fixture for it, and the scan falls back to the plain listing when the annotated one carries no table the plain one shows. Refs gominimal/inbox#1201 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CYvTvgTNC876h72BpHfV3 --------- Co-authored-by: Karl Godard <karl@minimal.dev> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit fce331f) Ported to the 0.7 line without main's QUIC443 work, which the line doesn't have: host_ruleset for kept_quic443_leaves, the nft -j list set stub branch and the BEP-018 tests next to the new test helper.
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
Contributor
|
Approved: no findings at Nothing to do. Clobber: none seen: the branch holds one cherry-pick commit. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backports #3004 (
fce331f5b) torelease/0.7.x, as onecherry-pick -xcommit.On a host where tailscaled runs in iptables-nft mode,
min finalize-installrefused deny-all egress, because the ct-mark conflict scan read Tailscale's CONNMARK rule as writing every bit of the connection mark. The scan now decodes masked writes and reads xt CONNMARK masks from the iptables-save listings. It refuses a rule whose masks it can't read. The root cause and the full behaviour are in #3004.Adaptation
Each of the three files conflicted once, all on
main's QUIC443 work, which the line doesn't have. I took #3004's side and left out:scripts/install-host-classifier.shhost_ruleset="$listing"and its commentkept_quic443_leavesscripts/install_test.shnft -j list setstub branchcrates/minimald/src/net/classifier.rsHostToolsThe classifier's T105 content is already on the line, through
c30a54291.Testing
Run in the Lima VM at
d1091c4cd:just fmt-check,just clippy,cargo check -p minimald --all-targets --locked,just lint-shell: pass.just test: 3927 of 3931 pass. The 4 failures are the known VM-only ones, the same as on #3019:a_hook_launch_does_not_advise_unenforced_placementand threefinalize_install_undo_removes_*.just test-one install_ -p minimald: 6 pass. This includesinstall_reads_xt_connmark_masks_from_iptables_save.just test-one classifier -p minimald: 59 pass.just test-one host_ip -p minimald: 16 pass.Not run locally:
scripts/install_test.sh, the root integration tests, the VM lanes and macOS. CI covers those. #3004's open item also applies here:nft -a --debug=netlink list rulesethasn't been checked on a real host with iptables-nft rules.Merging
Use Rebase and merge, so the
fix(classifier)subject lands as written.