authz

package
v0.52.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package authz holds the ClusterProvider namespace-admission decision.

A ClusterProvider is cluster-scoped and holds a credential that can read a lot of a source cluster; its spec.accessFrom is that provider's explicit, deny-by-default admission of the GitTarget NAMESPACES permitted to mirror through it.

The decision lives in its own package — outside both internal/controller and internal/watch — because more than one call site compiles rules or starts a data plane for a GitTarget: the GitTarget reconciler, the ClusterWatchRule reconciler, and the watch manager's startup bootstrap. A gate that only one of them runs is not a gate, so every such call site routes through GitTargetAdmitted and one policy read answers all of them identically.

Index

Constants

View Source
const (
	// ReasonClusterProviderNotFound is the denial reason when a GitTarget's referenced
	// ClusterProvider does not exist. This is a HARD GATE: a GitTarget may mirror a source cluster
	// ONLY through an existing ClusterProvider, "default" included. The operator never creates one,
	// so a target whose provider was never declared is denied rather than mirroring on an implicit
	// local identity.
	ReasonClusterProviderNotFound = "ClusterProviderNotFound"

	// ReasonNamespaceNotAuthorized is the denial reason when the GitTarget's namespace is not
	// admitted by its ClusterProvider's spec.accessFrom — including the case where that
	// policy carries a selector the apiserver accepted but that does not convert to a selector.
	ReasonNamespaceNotAuthorized = "NamespaceNotAuthorized"
)
View Source
const (
	// ReasonLegacySourceNamespace is the True reason when EVERY item watches the rule's OWN
	// namespace. No authorization was needed.
	ReasonLegacySourceNamespace = "LegacySourceNamespace"

	// ReasonSourceNamespaceAllowed is the True reason when at least one item names a namespace
	// other than the rule's own — an authorized override or the cluster-wide wildcard — and every
	// item passed the gate.
	ReasonSourceNamespaceAllowed = "SourceNamespaceAllowed"

	// ReasonSourceNamespaceNotAllowed is the TERMINAL False reason for a refusal: the referenced
	// ClusterProvider does not exist, does not admit this GitTarget's namespace, or does not set
	// spec.allowAnySourceNamespace. It is a decision, never an inability to decide.
	ReasonSourceNamespaceNotAllowed = "SourceNamespaceNotAllowed"
)

Reasons for the WatchRule SourceNamespaceAuthorized condition. They are the rule-side names, so a reader never has to know which gate input produced the verdict — the Message carries that.

There used to be five, and three of them existed only because the deleted GitTarget.spec.allowedSourceNamespaces had a SELECTOR half evaluated against Namespace labels in ANOTHER cluster: that read could be Forbidden, or still syncing, or permanently unevaluatable, so the verdict had to be three-valued and two of the reasons described an inability to decide rather than a decision. Nothing here reads another cluster any more. Every input is on the WatchRule, the GitTarget and the ClusterProvider, all in the control plane, so the gate is a comparison and its answer is always allowed or denied.

Variables

This section is empty.

Functions

This section is empty.

Types

type Decision

type Decision struct {
	// Allowed reports whether the GitTarget's namespace may mirror through its ClusterProvider.
	Allowed bool
	// Reason is a CamelCase condition reason, set only when Allowed is false.
	Reason string
	// Message explains the denial to an operator, set only when Allowed is false.
	Message string
}

Decision is the outcome of a ClusterProvider namespace-admission check. A denial always carries a Reason and an operator-legible Message; an admission carries neither.

func GitTargetAdmitted

func GitTargetAdmitted(
	ctx context.Context,
	reader client.Reader,
	target *configv1alpha3.GitTarget,
) (Decision, error)

GitTargetAdmitted reports whether target's namespace is admitted by the ClusterProvider that target references, reading both the provider and the target's Namespace through reader.

It is evaluated on every reconcile rather than only at admission, so a policy TIGHTENED after a GitTarget was created revokes it too. The two denial paths are deliberate and ordered: a missing provider is denied before any namespace policy is consulted, because an absent provider has no policy to consult and defaulting to "allow" would make an undeclared provider a bypass.

A non-NotFound read error is returned as err so the caller requeues instead of tearing down a running data plane on a transient apiserver failure. A NotFound Namespace is NOT an error: the policy is then evaluated against empty labels, so a `names` entry still admits a namespace that has not been created yet while a selector correctly does not match it.

type ResolvedSourceScope

type ResolvedSourceScope struct {
	// Items is index-aligned with spec.rules.
	Items []SourceNamespaceDecision
	// Allowed is the aggregate over Items: every item admitted.
	Allowed bool
	// Reason is the aggregate SourceNamespaceAuthorized reason.
	Reason string
	// Message explains the aggregate, naming the deciding item when one item decided it.
	Message string
}

ResolvedSourceScope is a WHOLE WatchRule's source-namespace verdict: one decision per spec.rules item, index-aligned, plus the aggregate the SourceNamespaceAuthorized condition publishes.

It is a pure function of (rule spec, provider flags) and is recomputed on every compile. Nothing per-item is persisted across a spec change, which is what lets rule items have no stable API identity: no state outlives the spec that produced it.

func ResolveWatchRuleSourceScope

func ResolveWatchRuleSourceScope(
	ctx context.Context,
	reader client.Reader,
	rule *configv1alpha3.WatchRule,
	target *configv1alpha3.GitTarget,
) (ResolvedSourceScope, error)

ResolveWatchRuleSourceScope is the WatchRule source-namespace gate: may each of this rule's items watch the source namespace it asks for?

It is CROSS-OBJECT authorization — WatchRule → GitTarget → ClusterProvider — so it is not expressible in CEL and is deliberately a reconciler check rather than a webhook (docs/spec/where-validation-lives.md). Like GitTargetAdmitted it runs on every reconcile, so a policy TIGHTENED after a rule was accepted revokes it.

The gate is two rules:

  1. An item watching the rule's OWN namespace is allowed, with no provider involvement at all. This is the legacy case and it must stay free: gating it would break every existing WatchRule on upgrade.
  2. Any OTHER namespace — a name, or "*" for every namespace the credential can read — additionally requires the GitTarget's namespace to be admitted by its ClusterProvider, and that provider to set spec.allowAnySourceNamespace.

There is no third rule. GitTarget.spec.allowedSourceNamespaces used to supply one, and what it bounded is now bounded by the source credential's own RBAC: a request this gate permits still fails 403 at the apiserver if the credential cannot read that namespace, which is a better answer than a policy field that restates the credential in the one place that cannot revoke it.

A non-NotFound ClusterProvider read error is returned as err so the caller requeues instead of tearing down a running stream on a transient apiserver failure.

func (ResolvedSourceScope) Admitted

func (s ResolvedSourceScope) Admitted() bool

Admitted reports whether the whole rule may compile.

func (ResolvedSourceScope) Fingerprint

func (s ResolvedSourceScope) Fingerprint() string

Fingerprint renders the resolved scope as a stable string, per item, for the watched-type re-projection gate.

It is now derivable from the rule spec alone, since the resolution reads no cluster state. It is still computed from the RESOLVED set rather than the requested one, because the two differ for a wildcard — "*" resolves to the empty string — and the fingerprint's job is to describe what is actually watched.

func (ResolvedSourceScope) NamespacesFor

func (s ResolvedSourceScope) NamespacesFor(index int) []string

NamespacesFor returns the resolved namespace set for one item index.

type SourceNamespaceDecision

type SourceNamespaceDecision struct {
	// Index is the item's position in spec.rules.
	Index int
	// Requested is what the item asked for, verbatim: "" (omitted), a name, or "*".
	Requested string
	// Namespaces is the RESOLVED source-namespace set this item watches. It is a single-element
	// slice in every case: one name, or the EMPTY STRING for a wildcard, which the planner reads as
	// the cluster-wide collection. It is meaningful only when the item is admitted.
	Namespaces []string
	// Allowed reports whether the item may contribute selections.
	Allowed bool
	// Reason is the SourceNamespaceAuthorized condition reason this item would produce.
	Reason string
	// Message explains the verdict to an operator.
	Message string
}

SourceNamespaceDecision is one rule ITEM's source-namespace verdict, plus the concrete namespace set it resolved to.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL