Skip to content

security(unifi): Git source is an unverified moving branch feeding a credentialed network writeΒ #3020

Closed

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence (verified live, 2026-08-08)

GitRepository/unifi in namespace unifi tracks ref.branch: main β€” a moving branch β€” and
carries verify: NONE. The consuming Kustomization/unifi is not suspended and is actively
reconciling (Applied revision: main@sha1:77dc6590…).

So the cluster continuously applies whatever appears on that branch's head, with no pin and no
signature check, into an app holding a write-scoped UniFi Cloud Connector credential.

Audience and impact

The affected surface is the deployed home network configuration β€” VLANs, firewall rules, SSIDs, device
and VPN config. An unreviewed or compromised push to the source branch reaches the network without a
verification step. This is the same class the platform already closes elsewhere: the root
OCIRepository is cosign-verified, and other sources are pinned. This one is the outlier.

Expected behaviour

The UniFi source is either pinned (spec.ref.tag or spec.ref.commit/digest) or
signature-verified (spec.verify), so a moving branch alone cannot reach the cluster β€” while
normal delivery keeps working.

Why not the existing mitigation

#2705 proposes suspend: true on the Kustomization. It closes the hole but stops delivery entirely,
so every legitimate change stops applying until someone unsuspends. It is a reasonable holding action
and it is why this issue exists, but the pin is strictly better: same floor, no friction. #2705 stays
parked as the fallback.

Acceptance criteria

  • GitRepository/unifi either verifies signatures or pins to an immutable ref.
  • Kustomization/unifi remains unsuspended and still reconciles after the change.
  • Verified against the live cluster after deploy, not from the manifest alone β€” the reconciled
    revision must match the pinned/verified source.
  • Whatever mechanism is chosen is consistent with how the platform's other verified sources are
    configured, rather than a one-off.
  • fix(unifi): suspend Flux Kustomization for unsigned UniFi Git sourceΒ #2705 closed unmerged once this lands.

Trade-off to decide while implementing

Pinning to a tag means UniFi changes need a tag bump to ship, which is friction on a repo the
maintainer edits directly. Signature verification keeps branch-tracking convenience and adds a signing
requirement instead. Prefer whichever matches existing practice in this repo; state the choice and its
everyday cost in the PR body per the two-sided hardening test.

Rough size: small β€” one resource, plus a live post-deploy check.

Activity

  1. coderabbitai commented on Aug 8, 2026

    @coderabbitai
    Contributor
    πŸ”— Related PRs

    #2319 - feat(security): cosign-verify the infrastructure Flux OCI artifact [merged]
    #2816 - fix(security): reject floating workflow refs in cosign subject matchers [merged]
    #2919 - fix(security): move Flux cosign verify to the path KSail reads [merged]
    #2964 - fix(security): pin cosign subject refs at the image-verification layer [merged]
    #3001 - fix(security): enforce cosign verify on the live root OCIRepository [merged]


    πŸ“ Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    πŸ§ͺ Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    πŸ’¬ Have feedback or questions? Drop into our discord!

  2. moved this to πŸ“₯ Backlog in 🌊 Project Boardon Aug 8, 2026
  3. devantler commented on Aug 22, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Most of this has already shipped β€” do not re-implement it

    I selected this as a rung-2 candidate and checked the manifest before starting. The core change is already in place at main, so the issue as written overstates what is left.

    k8s/providers/hetzner/apps/unifi/git-repository.yaml at main:

      ref:
        commit: 7b17f7e33ef507c24c395b884a433c62b92ace98

    with an in-file comment giving this issue's exact rationale β€” "Pin the full commit so a source-repository compromise cannot change resources already authorized for patch/update without a separately reviewed Platform change."

    Status against the acceptance criteria:

    AC State
    Pinned or signature-verified Met β€” pinned to a full commit
    Kustomization/unifi unsuspended and still reconciling Met in manifest β€” no suspend: true at main
    Verified against the live cluster after deploy Open β€” needs a live read; not verifiable from the manifest
    Consistent with other verified sources, not a one-off Open β€” commit-pinning is a different mechanism from the root OCIRepository's cosign verification; worth a sentence either way
    #2705 closed unmerged Met β€” already closed

    The pin arrived on 2026-08-15 in a9a177fb0b ("fix(unifi): prevent pruning managed network resources", #2707) β€” a side effect of a different PR, seven days after this issue's 2026-08-08 live evidence. That is why the issue body still describes a moving branch: it was accurate when written and was overtaken without anyone updating it.

    So what remains is the live-cluster verification (AC3) and a short consistency statement (AC4) β€” a much smaller slice than the body implies, and one that needs the cadence-gated live-cluster path rather than a manifest change. I have not done that check, so I am not closing this.

    I did not start implementation, precisely because the manifest read came first.

  4. devantler commented on Aug 22, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Verified against the live cluster β€” closing

    AC3 asked for verification against the live cluster rather than the manifest alone. Done, read-only against admin@prod:

    $ kubectl -n unifi get gitrepository unifi
    spec.ref:                  {"commit":"7b17f7e33ef507c24c395b884a433c62b92ace98"}
    spec.verify:               NONE
    status.artifact.revision:  sha1:7b17f7e33ef507c24c395b884a433c62b92ace98
    
    $ kubectl -n unifi get kustomization unifi
    suspend:              false
    lastAppliedRevision:  sha1:7b17f7e33ef507c24c395b884a433c62b92ace98
    Ready:                True β€” Applied revision: sha1:7b17f7e33ef507c24c395b884a433c62b92ace98
    

    The reconciled revision equals the pinned commit exactly, and the Kustomization is unsuspended and Ready β€” so delivery still works and the moving branch is gone.

    AC State
    Pinned or signature-verified Met β€” ref.commit, an immutable ref
    Kustomization/unifi unsuspended and still reconciling Met β€” suspend: false, Ready: True
    Verified live, reconciled revision matches the pin Met β€” evidence above
    Consistent with other verified sources, not a one-off Met, with the choice stated below
    #2705 closed unmerged Met β€” already closed

    AC4 β€” the mechanism choice, stated

    This uses commit pinning, not cosign verification. The two coexist deliberately: the root OCIRepository is an artifact we publish, so signing it is possible and cosign verification is the right control. devantler-tech/unifi is a plain Git repository the maintainer edits directly, with no signing pipeline β€” so spec.verify would require standing up commit signing on that repo first. Pinning reaches the same floor (nothing reaches the cluster unless a reviewed Platform change moves the pin) with no signing requirement.

    Everyday cost, per the two-sided hardening test: a UniFi change now needs a Platform PR to bump the pin β€” one extra step, and it is the step that makes the change reviewed. prune: false already means a source change cannot delete live objects, so the pin closes the remaining write path.

    Note on how this landed

    The pin arrived on 2026-08-15 in a9a177fb0b (#2707, "prevent pruning managed network resources") β€” a side effect of a different PR, seven days after this issue's 2026-08-08 evidence. Nobody updated the issue, so it kept describing a moving branch that no longer existed. I nearly re-implemented it; the manifest read came first, which is what caught it.

  5. moved this from πŸ“₯ Backlog to βœ… Done in 🌊 Project Boardon Aug 22, 2026
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

    Fields

    No fields configured for Security.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions