Skip to content

Guard BootcNode.spec against out-of-band edits at admission #154

Description

@zarcen

Follow-up to #146

Problem

BootcNode is controller-owned: the pool controller writes spec.desiredImage
and spec.desiredImageState, and the docs say users manage the pool, not the
node. Nothing enforces that — anyone with write access to bootcnodes can patch
the field:

$ kubectl patch bootcnode <node> --type=merge \
    -p '{"spec":{"desiredImage":"foo.com/img@sha256:<previous-digest>"}}'
bootcnode.node.bootc.dev/<node> patched

The controller repairs the field on the next reconcile, but the repair is not the
whole story. The daemon watches BootcNode spec changes and staging is not gated
on the pool, so it starts bootc switch against the edited digest before the
repair lands. On a live cluster the repair took ~10ms — the switch was already
issued and had reached the registry. Because the daemon runs plain bootc switch
(no --apply), the rogue image becomes the node's next-boot default.

End state: spec.desiredImage reads correctly, the pool reports upToDate, and
the node boots an image nobody asked for at the next reboot (EC2 maintenance,
kernel panic, admin drain). The mutation is transient; the boot-order side effect
is not, and nothing in the API records that it happened.

Request

Reject writes to controller-owned BootcNode spec fields from any identity other
than the operator's own ServiceAccount, at admission. It could be either a
ValidatingAdmissionPolicy or a validating webhook, whichever fits the project
better.

A cluster-admin can still delete or bypass it, and status stays daemon-writable.
This is a guard against user error and over-broad GitOps selectors, not a security
boundary.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions