Documentation
¶
Overview ¶
Package authz holds the admission decisions for the objects that lend something: a GitProvider's credential (spec.allowedGitBranches), a GitBranch (spec.allowedGitTargets), and a KubeProvider's cluster connection (spec.allowedGitTargets, spec.allowedApplierClaims). Each grant list lives on the referenced object, and nothing is admitted automatically, the object's own namespace included.
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
- type Decision
- func ApplierAdmitted(ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget, ...) (Decision, error)
- func ApplierClaimAdmitted(ctx context.Context, reader client.Reader, claim configv1alpha3.ApplierClaim) (Decision, error)
- func AuditRouteHeld(ctx context.Context, reader client.Reader, ...) (Decision, error)
- func CommitRequestAdmitted(ctx context.Context, reader client.Reader, ...) (Decision, error)
- func GitBranchClaimAdmitted(ctx context.Context, reader client.Reader, branch *configv1alpha3.GitBranch, ...) (Decision, error)
- func GitTargetAdmitted(ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget) (Decision, error)
- func GitTargetSourceScope(ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget) (Decision, configv1alpha3.KubeProviderGitTargetScope, error)
- type ResolvedSourceScope
- type SourceNamespaceDecision
Constants ¶
const ( // ReasonKubeProviderNotFound is the denial reason when a referenced KubeProvider does not // exist. This is a HARD GATE: a GitTarget may mirror a source cluster ONLY through an existing // KubeProvider. The operator never creates one, so a target whose provider was never declared // is denied rather than mirroring on an implicit identity. ReasonKubeProviderNotFound = "KubeProviderNotFound" // ReasonNamespaceNotAuthorized is the denial reason when no grant on the referenced object admits // the referring object's namespace — including a grant whose selector the apiserver accepted but // that does not convert to a selector. ReasonNamespaceNotAuthorized = "NamespaceNotAuthorized" // ReasonBranchNotAllowed is the denial reason when a GitProvider admits a GitBranch's namespace, // but none of the grants that admit it allows the branch the GitBranch claims. ReasonBranchNotAllowed = "BranchNotAllowed" // ReasonApplierNamespaceNotAllowed is the denial reason when an applier claim names an applier in // a namespace outside the applierNamespaces its KubeProvider grants it. ReasonApplierNamespaceNotAllowed = "ApplierNamespaceNotAllowed" // ReasonAuditRouteConflict is the denial reason when a KubeProvider's audit route is held by a // namespace other than its own (v1alpha3.AuditRouteHolder). Such a provider is not used at all: // mirroring through it would read the attribution facts of a tenant it was never admitted to. ReasonAuditRouteConflict = "AuditRouteConflict" )
const ( // ReasonSourceNamespaceAllowed is the True reason when every item watches a namespace the // KubeProvider's grants give the GitTarget's namespace. ReasonSourceNamespaceAllowed = "SourceNamespaceAllowed" // ReasonSourceNamespaceNotAllowed is the TERMINAL False reason for a refusal: the referenced // KubeProvider does not exist, does not admit this GitTarget's namespace, or its grants do not // give it the namespace an item asks for. 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.
Every input is on the WatchRule, the GitTarget, the KubeProvider and the GitTarget's Namespace, 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 consumer's namespace may use the KubeProvider.
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 KubeProvider namespace-admission check. A denial always carries a Reason and an operator-legible Message; an admission carries neither.
func ApplierAdmitted ¶ added in v0.53.0
func ApplierAdmitted( ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget, claim configv1alpha3.ApplierClaim, ) (Decision, error)
ApplierAdmitted reports whether target may ask the applier its applier claim holds to apply: target may mirror (GitTargetAdmitted), and the applier claim may hold its applier (ApplierClaimAdmitted). Both are always checked, also when the two name one KubeProvider from one namespace: mirroring and applying are granted separately. A KubeProvider's client is shared by every object that uses the cluster, so a client being available is no permission to use it.
func ApplierClaimAdmitted ¶ added in v0.54.0
func ApplierClaimAdmitted( ctx context.Context, reader client.Reader, claim configv1alpha3.ApplierClaim, ) (Decision, error)
ApplierClaimAdmitted reports whether an applier claim may hold its applier through the KubeProvider it names (spec.kubeProviderRef): a grant in that provider's spec.allowedApplierClaims admits the applier claim's namespace, and the applier lives in one of the applierNamespaces those grants give it. The applier is reached with that provider's credential, so admission to mirror through it is no admission to apply.
func AuditRouteHeld ¶ added in v0.54.0
func AuditRouteHeld( ctx context.Context, reader client.Reader, provider *configv1alpha3.KubeProvider, ) (Decision, error)
AuditRouteHeld reports whether provider's namespace holds the audit route provider carries (v1alpha3.AuditRouteHolder). A provider whose route another namespace holds is denied with ReasonAuditRouteConflict: it is not used at all, by any consumer.
It reads every KubeProvider through reader, which on the manager's client is the informer cache. The holder is recomputed on every call rather than kept anywhere, so it can never disagree with the providers as they are.
func CommitRequestAdmitted ¶ added in v0.55.0
func CommitRequestAdmitted( ctx context.Context, reader client.Reader, request *configv1alpha3.CommitRequest, target *configv1alpha3.GitTarget, ) (Decision, error)
CommitRequestAdmitted reports whether request may save the commit windows of target, the GitTarget its spec.gitTargetRef names: a grant in target's spec.allowedCommitRequests admits the request's namespace, by the labels that namespace carries. A non-NotFound read of the Namespace is returned as err, so the caller retries instead of refusing a request on a transient failure.
func GitBranchClaimAdmitted ¶ added in v0.54.0
func GitBranchClaimAdmitted( ctx context.Context, reader client.Reader, branch *configv1alpha3.GitBranch, provider *configv1alpha3.GitProvider, ) (Decision, error)
GitBranchClaimAdmitted reports whether branch may claim its spec.branch through provider, the GitProvider it references: a grant in provider's spec.allowedGitBranches admits the GitBranch's namespace, and one of the patterns those grants give it matches the branch. A namespace no grant admits is ReasonNamespaceNotAuthorized; an admitted one asking for a branch outside its patterns is ReasonBranchNotAllowed. A non-NotFound read of the Namespace is returned as err, so a caller keeps its last answer rather than revoking on a transient failure.
func GitTargetAdmitted ¶
func GitTargetAdmitted( ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget, ) (Decision, error)
GitTargetAdmitted reports whether target may mirror through the KubeProvider it references (spec.allowedGitTargets) and write through the GitBranch it references (that GitBranch's spec.allowedGitTargets), reading the provider, the GitBranch 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 grant still admits a namespace that has not been created yet while a selector correctly does not match it.
func GitTargetSourceScope ¶ added in v0.54.0
func GitTargetSourceScope( ctx context.Context, reader client.Reader, target *configv1alpha3.GitTarget, ) (Decision, configv1alpha3.KubeProviderGitTargetScope, error)
GitTargetSourceScope is GitTargetAdmitted, plus what the grants that admit target let it mirror: the source namespaces its WatchRules may watch and the cluster-scoped resources a ClusterWatchRule may mirror through it. The scope is meaningful only when the decision allows.
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, the grants that admit the GitTarget) 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 → KubeProvider — 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.
An item may watch a namespace only while the KubeProvider's spec.allowedGitTargets admit the GitTarget's namespace and give it that namespace in sourceNamespaces: a name, '{consumer.namespace}' for the GitTarget's own namespace (which is the WatchRule's), or "*" for every namespace, which is also what an item asking for "*" needs. The rule's own namespace is in scope only when a grant lists it. The source credential's RBAC stays the hard maximum: a request this gate permits still fails 403 at the apiserver if the credential cannot read that namespace.
A non-NotFound 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 computed from the RESOLVED set rather than the requested one, because the two differ for a wildcard — "*" resolves to the empty string — and for a denied item, which resolves to nothing: 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.