Skip to content

OCPBUGS-99202: Prevent daemon panic on out-of-range rule order - #754

Open
smulje wants to merge 1 commit into
openshift:masterfrom
smulje:fix-order-oob-panic
Open

OCPBUGS-99202: Prevent daemon panic on out-of-range rule order#754
smulje wants to merge 1 commit into
openshift:masterfrom
smulje:fix-order-oob-panic

Conversation

@smulje

@smulje smulje commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Cap IngressNodeFirewallProtocolRule.order at 99 in the CRD schema (covers both IngressNodeFirewall and IngressNodeFirewallNodeState)
  • Reject out-of-range order in the admission webhook
  • Validate order in the daemon before using it as an eBPF rules-array index, so a crafted NodeState cannot panic every node's reconciler into CrashLoopBackOff

order is consumed as a raw index into a fixed [100]BpfRuleTypeSt array (index 0 reserved). Previously only minimum: 1 was enforced, and the webhook only checked rule count / uniqueness — any order >= 100 (up to uint32 max) panicked the daemon cluster-wide.

Test plan

  • go test ./pkg/failsaferules/
  • Webhook suite: rejects order == 100 and order == ^uint32(0); accepts order == 99
  • Apply an IngressNodeFirewall with order: 100 and confirm API/webhook rejection
  • Apply an IngressNodeFirewallNodeState with order: 100 and confirm CRD rejection (should be less than or equal to 99)
  • Confirm order: 99 is accepted and daemons attach XDP without panic

Cluster verification (OCP 4.21)

Reproduced the panic on stock 4.21 (index out of range [100] with length 100), then verified the fix with custom images built from this branch:

  • Operator: quay.io/smulje/ingress-node-firewall-operator:order-oob-fix
  • Daemon: quay.io/smulje/ingress-node-firewall-daemon:order-oob-fix

Results with the custom images:

  • order: 100 / order: 4294967295 rejected by webhook
    An error occurred Error "Invalid value: 100: spec.ingress[0].rules[0].order in body should be less than or equal to 99" for field "spec.ingress[0].rules[0].order".
  • order: 99 applied successfully (Synchronized), daemons remained Running
  • Direct IngressNodeFirewallNodeState write with order: 100 rejected by updated CRD schema

Summary by CodeRabbit

  • Bug Fixes

    • Added validation to reject firewall rule orders outside the supported range of 1–99.
    • Prevented invalid rule orders from causing runtime indexing errors.
  • Documentation

    • Clarified that rule orders must be unique and that order 0 is reserved for internal catch-all handling.
    • Updated CRD schemas with the maximum order validation.
  • Tests

    • Added coverage for minimum, maximum, and out-of-range rule order values.

@openshift-ci

openshift-ci Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bc60fce0-05e2-4539-8698-47f6b742b913

📥 Commits

Reviewing files that changed from the base of the PR and between 39fd0d7 and 8d5c01a.

📒 Files selected for processing (11)
  • api/v1alpha1/ingressnodefirewall_types.go
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • config/crd/bases/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • config/crd/bases/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • pkg/ebpf/ingress_node_firewall_loader.go
  • pkg/failsaferules/failsaferules.go
  • pkg/webhook/webhook.go
  • pkg/webhook/webhook_suite_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • config/crd/bases/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • api/v1alpha1/ingressnodefirewall_types.go
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml

Walkthrough

Ingress rule order documentation and CRD schemas now enforce values from 1 through 99. Webhook validation and eBPF loading reject invalid orders, the shared maximum is a constant, and boundary validation tests were added.

Changes

Ingress rule order validation

Layer / File(s) Summary
Order contract and CRD schemas
api/v1alpha1/ingressnodefirewall_types.go, config/crd/bases/*, bundle/manifests/*, manifests/stable/*
The order field documents uniqueness, the valid 1–99 range, and reserved index 0 semantics; CRD schemas add maximum: 99.
Runtime order enforcement
pkg/failsaferules/failsaferules.go, pkg/webhook/webhook.go, pkg/ebpf/ingress_node_firewall_loader.go, pkg/webhook/webhook_suite_test.go
The shared maximum is constant, webhook validation rejects out-of-range values, loader indexing checks bounds, and boundary cases are tested.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: jcaamano, tssurya

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Added boundary tests are single-purpose, but they use bare expectations, lack per-test cleanup on failed create paths, and the suite has an Eventually without an explicit timeout. Add DeferCleanup/AfterEach for CRs, explicit timeout args on Eventually/Consistently, and failure messages on cluster-facing assertions.
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preventing daemon panics from out-of-range rule orders.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The added Ginkgo titles are static strings (e.g. "rejects order equal to MAX_INGRESS_RULES"), with no dynamic names, dates, UUIDs, or generated suffixes.
Microshift Test Compatibility ✅ Passed The new Ginkgo cases only exercise the ingress-node-firewall CRD/webhook in envtest and use no MicroShift-unsupported OpenShift APIs, namespaces, or multi-node assumptions.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The added Ginkgo cases are envtest webhook CR validations; they only create/delete CRs and compare selectors, with no node-count, scheduling, drain, or HA assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only changes CRD/webhook/eBPF validation for rule-order bounds; no deployment, affinity, nodeSelector, spread, replica, or PDB scheduling logic was added.
Ote Binary Stdout Contract ✅ Passed BeforeSuite logs to GinkgoWriter, AfterSuite is quiet, and no changed init/main code writes to stdout.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new Ginkgo cases only exercise order validation in local envtest; they add no external connectivity and no new IPv4-only networking logic.
No-Weak-Crypto ✅ Passed Modified files only add numeric bounds validation; no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret/token comparisons appear in the diff.
Container-Privileges ✅ Passed The PR only changes CRD docs/validation and webhook/daemon bounds checks; no touched manifests add privileged/root/host* settings or allowPrivilegeEscalation.
No-Sensitive-Data-In-Logs ✅ Passed No new logging was added in the PR; the changed code only adds numeric range validation/error messages and no passwords, tokens, PII, or hostnames appear.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/failsaferules/failsaferules.go (1)

5-11: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make the rule bounds immutable. Exported vars can be reassigned by any importer, which lets validation drift from the fixed eBPF array size again. Define both as const so the limit stays coupled to the array capacity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/failsaferules/failsaferules.go` around lines 5 - 11, Change
MAX_INGRESS_RULES and MaxRuleOrder from exported variables to constants,
preserving their existing values and deriving MaxRuleOrder from
MAX_INGRESS_RULES so both bounds remain immutable and coupled to the eBPF array
capacity.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/failsaferules/failsaferules.go`:
- Around line 5-11: Change MAX_INGRESS_RULES and MaxRuleOrder from exported
variables to constants, preserving their existing values and deriving
MaxRuleOrder from MAX_INGRESS_RULES so both bounds remain immutable and coupled
to the eBPF array capacity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7539d913-1ff5-498c-b71c-46d0dbf5196d

📥 Commits

Reviewing files that changed from the base of the PR and between f1e4ccd and 1defebc.

📒 Files selected for processing (12)
  • api/v1alpha1/ingressnodefirewall_types.go
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • bundle/manifests/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • config/crd/bases/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • config/crd/bases/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewallnodestates.yaml
  • manifests/stable/ingressnodefirewall.openshift.io_ingressnodefirewalls.yaml
  • pkg/ebpf/ingress_node_firewall_loader.go
  • pkg/failsaferules/failsaferules.go
  • pkg/failsaferules/failsaferules_test.go
  • pkg/webhook/webhook.go
  • pkg/webhook/webhook_suite_test.go

@smulje
smulje marked this pull request as ready for review July 22, 2026 13:44
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 22, 2026
@smulje
smulje force-pushed the fix-order-oob-panic branch from 39fd0d7 to 22abf0c Compare July 22, 2026 13:53
@smulje smulje changed the title Prevent daemon panic on out-of-range rule order OCPBUGS-99202: Prevent daemon panic on out-of-range rule order Jul 22, 2026
@openshift-ci-robot openshift-ci-robot added jira/severity-important Referenced Jira bug's severity is important for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. labels Jul 22, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@smulje: This pull request references Jira Issue OCPBUGS-99202, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state New, which is one of the valid states (NEW, ASSIGNED, POST)

No GitHub users were found matching the public email listed for the QA contact in Jira (core-networking-bot@redhat.com), skipping review request.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

  • Cap IngressNodeFirewallProtocolRule.order at 99 in the CRD schema (covers both IngressNodeFirewall and IngressNodeFirewallNodeState)
  • Reject out-of-range order in the admission webhook
  • Validate order in the daemon before using it as an eBPF rules-array index, so a crafted NodeState cannot panic every node's reconciler into CrashLoopBackOff

order is consumed as a raw index into a fixed [100]BpfRuleTypeSt array (index 0 reserved). Previously only minimum: 1 was enforced, and the webhook only checked rule count / uniqueness — any order >= 100 (up to uint32 max) panicked the daemon cluster-wide.

Test plan

  • go test ./pkg/failsaferules/
  • Webhook suite: rejects order == 100 and order == ^uint32(0); accepts order == 99
  • Apply an IngressNodeFirewall with order: 100 and confirm API/webhook rejection
  • Apply an IngressNodeFirewallNodeState with order: 100 and confirm CRD rejection (should be less than or equal to 99)
  • Confirm order: 99 is accepted and daemons attach XDP without panic

Cluster verification (OCP 4.21)

Reproduced the panic on stock 4.21 (index out of range [100] with length 100), then verified the fix with custom images built from this branch:

  • Operator: quay.io/smulje/ingress-node-firewall-operator:order-oob-fix
  • Daemon: quay.io/smulje/ingress-node-firewall-daemon:order-oob-fix

Results with the custom images:

  • order: 100 / order: 4294967295 rejected by webhook
    An error occurred Error "Invalid value: 100: spec.ingress[0].rules[0].order in body should be less than or equal to 99" for field "spec.ingress[0].rules[0].order".
  • order: 99 applied successfully (Synchronized), daemons remained Running
  • Direct IngressNodeFirewallNodeState write with order: 100 rejected by updated CRD schema

Summary by CodeRabbit

  • Bug Fixes

  • Rule ordering now enforces valid values from 1 through 99.

  • Invalid rule orders are rejected during configuration validation and rule loading, preventing out-of-range failures.

  • Documentation

  • Clarified ordering requirements, uniqueness, reserved catch-all behavior, and valid limits in resource schemas.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci
openshift-ci Bot requested review from jcaamano and tssurya July 22, 2026 13:55

@danwinship danwinship left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few comments

// + index 0 is used internally as catch all for unclassified packets matching the same sourceCIDR.
// The minimum order value is 1, the maximum is 99, and the values must be unique.
// Index 0 is used internally as catch all for unclassified packets matching the same sourceCIDR.
// Order is consumed as an index into a fixed-size eBPF rules array of length 100.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That last sentence is not part of the API; it's an implementation detail, which could be changed in the future, so it shouldn't be documented here.

if err := failsaferules.ValidateRuleOrder(idx); err != nil {
return keys, rules, err
}
rules.Rules[idx].RuleId = rule.Order

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more robust to just check idx against len(rules.Rules), so that even if ValidateRuleOrder was wrong, it wouldn't crash

Comment thread pkg/failsaferules/failsaferules.go Outdated
// ValidateRuleOrder reports whether order can be used as an index into the
// fixed-size eBPF rules array. Index 0 is reserved; values >= MAX_INGRESS_RULES
// are out of bounds.
func ValidateRuleOrder(order uint32) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failsaferules is about blocking valid-but-dangerous rules, like "deny all access to the apiserver". In this case we are checking for an invalid rule, so it doesn't belong in this package.

(But since you don't need to call this from ingress_node_firewall_loader.go now, the check can just be inlined into validateRules below, like the other checks.)

Order is used as a raw index into a fixed-size eBPF rules array of
length 100. Without an upper bound, values >= 100 panic every node's
daemon into CrashLoopBackOff. Cap order at 99 in the CRD and webhook,
and bound-check against the rules array length before indexing so
crafted NodeState objects cannot crash the daemon.

Signed-off-by: Swati Mulje <smulje@redhat.com>
@smulje
smulje force-pushed the fix-order-oob-panic branch from 22abf0c to 8d5c01a Compare July 24, 2026 08:42
@openshift-ci-robot

Copy link
Copy Markdown

@smulje: This pull request references Jira Issue OCPBUGS-99202, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

No GitHub users were found matching the public email listed for the QA contact in Jira (core-networking-bot@redhat.com), skipping review request.

Details

In response to this:

Summary

  • Cap IngressNodeFirewallProtocolRule.order at 99 in the CRD schema (covers both IngressNodeFirewall and IngressNodeFirewallNodeState)
  • Reject out-of-range order in the admission webhook
  • Validate order in the daemon before using it as an eBPF rules-array index, so a crafted NodeState cannot panic every node's reconciler into CrashLoopBackOff

order is consumed as a raw index into a fixed [100]BpfRuleTypeSt array (index 0 reserved). Previously only minimum: 1 was enforced, and the webhook only checked rule count / uniqueness — any order >= 100 (up to uint32 max) panicked the daemon cluster-wide.

Test plan

  • go test ./pkg/failsaferules/
  • Webhook suite: rejects order == 100 and order == ^uint32(0); accepts order == 99
  • Apply an IngressNodeFirewall with order: 100 and confirm API/webhook rejection
  • Apply an IngressNodeFirewallNodeState with order: 100 and confirm CRD rejection (should be less than or equal to 99)
  • Confirm order: 99 is accepted and daemons attach XDP without panic

Cluster verification (OCP 4.21)

Reproduced the panic on stock 4.21 (index out of range [100] with length 100), then verified the fix with custom images built from this branch:

  • Operator: quay.io/smulje/ingress-node-firewall-operator:order-oob-fix
  • Daemon: quay.io/smulje/ingress-node-firewall-daemon:order-oob-fix

Results with the custom images:

  • order: 100 / order: 4294967295 rejected by webhook
    An error occurred Error "Invalid value: 100: spec.ingress[0].rules[0].order in body should be less than or equal to 99" for field "spec.ingress[0].rules[0].order".
  • order: 99 applied successfully (Synchronized), daemons remained Running
  • Direct IngressNodeFirewallNodeState write with order: 100 rejected by updated CRD schema

Summary by CodeRabbit

  • Bug Fixes

  • Added validation to reject firewall rule orders outside the supported range of 1–99.

  • Prevented invalid rule orders from causing runtime indexing errors.

  • Documentation

  • Clarified that rule orders must be unique and that order 0 is reserved for internal catch-all handling.

  • Updated CRD schemas with the maximum order validation.

  • Tests

  • Added coverage for minimum, maximum, and out-of-range rule order values.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@smulje: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@danwinship

Copy link
Copy Markdown

/lgtm
/approve

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 24, 2026
@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: danwinship, smulje

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/severity-important Referenced Jira bug's severity is important for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants