Help us shape the future of Go! Take the 2026 Go Developer Survey

webhook

package
v0.53.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const DefaultAuditMaxRequestBodyBytes = int64(10 * 1024 * 1024)

DefaultAuditMaxRequestBodyBytes limits incoming audit payload size.

View Source
const DefaultReceiverMaxInFlight = 8

DefaultReceiverMaxInFlight is how many deliveries the receiver handles at once. One is answered in milliseconds, so a burst of genuine pushes stays far below it; above it a request is answered 429 before anything is read, which bounds what the listener can cost the pod that also mirrors.

View Source
const DefaultReceiverMaxRequestBodyBytes = int64(2 * 1024 * 1024)

DefaultReceiverMaxRequestBodyBytes caps a delivery's body before its signature is checked. A push event is a few kilobytes in practice: GitHub and GitLab list at most 20 commits. The cap keeps an unauthenticated client from making the receiver buffer much; a genuine push over it is refused (413), and the branch worker still finds the push at the next refresh.

View Source
const GitProviderWebhookPathIndex = "status.webhookPath"

GitProviderWebhookPathIndex is the cache index the receiver finds a route's GitProvider by. It is registered on the manager's cache (IndexGitProviderWebhookPath), so a request for a path nothing publishes costs no API read.

View Source
const ReceiverPathPrefix = v1alpha3.ReceiverPathPrefix

ReceiverPathPrefix is the path prefix the push receiver serves. A route is the prefix and a hex HMAC-SHA256 digest, as v1alpha3.ReceiverPath builds it.

View Source
const (
	// ValidateAllPath is the broad observe-all validating admission endpoint. It
	// matches every resource and always allows — a test-only capture/observation
	// surface today (the e2e SUT wires it; the Helm chart does not), kept as the
	// stable extension point for a future cluster-wide policy. Per-our-type handling
	// (authorship, config validation) lives on ValidateOperatorTypesPath instead.
	ValidateAllPath = "/validate-all"
)
View Source
const ValidateOperatorTypesPath = "/validate-operator-types"

ValidateOperatorTypesPath is the validating admission endpoint scoped to our own operator CRDs. Today its one job is command authorship — capturing the submitter of a command kind (a CommitRequest) into Redis and always allowing — but it is the intended home for per-our-type admission generally (e.g. config validation of WatchRule / GitProvider / GitTarget later), which would be added as additional webhook-config entries with their own rules and failurePolicy. Distinct from the broad observe-all ValidateAllPath.

Variables

This section is empty.

Functions

func IndexGitProviderWebhookPath added in v0.53.0

func IndexGitProviderWebhookPath(obj client.Object) []string

IndexGitProviderWebhookPath indexes a GitProvider by its published status.webhookPath.

Types

type AdmissionAllowHandler

type AdmissionAllowHandler struct{}

AdmissionAllowHandler is a validating admission handler that always allows requests.

func (AdmissionAllowHandler) Handle

Handle returns an allow response for every admission request.

type AuditFactPublisher added in v0.41.0

type AuditFactPublisher interface {
	PublishFacts(ctx context.Context, key queue.FactStreamKey, facts []queue.AuthorFact) error
}

AuditFactPublisher appends one request's attribution facts to the stream for a (audit route, group/resource). A nil publisher means the fact stream is not wired.

It is the batch shape that makes this cheap: the API server delivers audit events in batches, so one request's 400 events over three types become THREE appends rather than 400 writes. The batching that causes the delivery delay attribution has to wait out is the same batching that makes publishing nearly free.

type AuditHandler

type AuditHandler struct {
	// contains filtered or unexported fields
}

AuditHandler receives kube-apiserver audit events on /audit-webhook and records the author-attribution fact for each Git-relevant mutation. It never writes object state — that comes from WATCH — so it decodes, applies the intrinsic accept gate (stage, verb, success, dry-run, unchanged-RV, subresource), and records the fact.

func NewAuditHandler

func NewAuditHandler(config AuditHandlerConfig) (*AuditHandler, error)

NewAuditHandler creates a new audit handler with the given configuration.

func (*AuditHandler) ServeHTTP

func (h *AuditHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler for audit event processing.

Every return path records an outcome, rejections included. The timer starts before the first gate so a rejected request is measured the same way an accepted one is.

type AuditHandlerConfig

type AuditHandlerConfig struct {
	// MaxRequestBodyBytes is the maximum accepted HTTP request body size.
	MaxRequestBodyBytes int64
	// FactPublisher appends the facts one request produced, grouped by stream. A publish failure
	// returns an audit-request error so the API server retries the delivery; mirrored-resource
	// author attribution depends on these facts arriving. A nil publisher means configured-author
	// mode — the handler is not wired at all.
	FactPublisher AuditFactPublisher
	// CollectionUIDCap is how many object uids a deletecollection fact may carry before the set is
	// dropped and the join falls back to scope matching. Zero means queue.DefaultCollectionUIDCap.
	CollectionUIDCap int
	// RouteHealth is the process-wide per-route registry. Every fact batch that actually appends
	// records its route there, which is what latches the ClusterProvider AuditFactsReceived
	// condition for the providers reading that route. Nil disables the bookkeeping.
	RouteHealth *attribution.RouteHealth
	// AuditRouteAnnotationKey enables the bare /audit-webhook endpoint for a SHARED audit stream
	// that carries several logical clusters: the AUDIT ROUTE is read PER EVENT from this
	// audit-event annotation, so one batch may fan out to several routes. Empty (the default) means
	// the bare endpoint is NOT enabled and every producer must post to a named
	// /audit-webhook/<audit-route>.
	AuditRouteAnnotationKey string
}

AuditHandlerConfig contains configuration for the audit handler.

type CommandAuthorRecorder

type CommandAuthorRecorder interface {
	RecordCommandAuthor(ctx context.Context, uid types.UID, author queue.CommandAuthor) error
}

CommandAuthorRecorder records the authenticated submitter of one command object. *queue.CommandAuthorStore satisfies it; the interface keeps the handler unit-testable without a live Redis.

type PushNotifier added in v0.53.0

type PushNotifier interface {
	NotifyPush(providerNamespace, providerName, branch, after string) bool
}

PushNotifier hands a verified delivery's branch to the branch worker that owns it. It reports whether such a worker exists. git.WorkerManager implements it.

after is the commit the delivery named. It is passed for logging only: a delivery is a hint, and the worker reads where the branch is from the remote itself rather than trusting it.

type ReceiverHandler added in v0.53.0

type ReceiverHandler struct {
	// contains filtered or unexported fields
}

ReceiverHandler serves POST /git-push/<digest>: the endpoint a Git host's push webhook points at, so GitOps Reverser learns quickly that somebody pushed to a branch.

A delivery is a hint, never a fact. The handler authenticates it, extracts the branch, and asks the branch worker to look; the worker then reads the remote's advertisement and decides for itself whether the branch gained a foreign commit. Nothing the body says beyond the branch name is acted on, so a late, duplicated or replayed delivery costs one ref advertisement and can never make the worker believe something the remote does not say.

Authentication comes first. The signature (or GitLab's token) is checked over the raw bytes before the body is parsed for anything, in constant time, so an unsigned request cannot make a target do work or probe which branches are tracked.

The route does not name the GitProvider. Its digest is keyed by the receiver token, so only whoever holds the token can find it, and every way a request can fail to reach a verified receiver (no such path, a path from before the token rotated, no token, a wrong signature) gets the same 404: the endpoint says nothing about which namespaces or providers exist.

func NewReceiverHandler added in v0.53.0

func NewReceiverHandler(config ReceiverHandlerConfig) (*ReceiverHandler, error)

NewReceiverHandler builds the push receiver handler.

func (*ReceiverHandler) ServeHTTP added in v0.53.0

func (h *ReceiverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler for push deliveries.

The status codes are a contract with Git hosts, which disable a hook that keeps failing: every verified delivery answers 202, including one that names a branch nothing writes, because that is not the host's fault. Only an unparseable verified delivery (400), a request that reaches no verified receiver (404, whatever the reason), a body over the cap (413), too many requests at once (429) and a receiver that cannot read its own configuration (503) fail.

type ReceiverHandlerConfig added in v0.53.0

type ReceiverHandlerConfig struct {
	// Reader looks up the GitProvider a route names and the Secret holding its token. In the
	// manager this is the manager client: GitProviders come from its cache, and Secrets are
	// excluded from that cache (Cache.DisableFor), so each delivery reads the token Secret
	// directly by name. A rotated token therefore takes effect on the next delivery, and the
	// receiver adds no Secret informer. See docs/rbac.md.
	Reader client.Reader
	// Notifier receives each verified push to a branch. Required.
	Notifier PushNotifier
	// MaxRequestBodyBytes caps a delivery's body. Zero means DefaultReceiverMaxRequestBodyBytes.
	MaxRequestBodyBytes int64
	// MaxInFlight bounds the deliveries handled at once. Zero means DefaultReceiverMaxInFlight.
	MaxInFlight int
}

ReceiverHandlerConfig configures the push receiver.

type ValidateOperatorTypesHandler

type ValidateOperatorTypesHandler struct {
	Store CommandAuthorRecorder
	// Client reads the GitTarget a WatchRule names, and that target's other rules. Nil leaves the
	// WatchRule branch evaluating nothing and allowing, which is the same degradation the
	// fail-open failurePolicy already provides.
	Client client.Client
}

ValidateOperatorTypesHandler is the admission handler for our operator CRDs. It dispatches on the resource under review and does two unrelated things:

  • For a COMMAND kind (a CommitRequest) it captures the authenticated submitter into the CommandAuthorStore and always allows — pure observation with a single side effect (a Redis upsert), never a rejection, so a user's command never depends on it succeeding (a missed capture leaves the request without a claimed actor; see docs/spec/commitrequest-admission-authorship.md).
  • For a WatchRule it validates the one cross-object rule admission can usefully give feedback on: a second source namespace against a GitTarget that declared its folder namespace-free. That one CAN reject, and it is the only thing here that does.

The two branches share an endpoint and nothing else. Validation runs on dry-run (it has no side effects, and a server-side dry-run must report the rejection it would get), while the capture branch honours NoneOnDryRun.

func (*ValidateOperatorTypesHandler) Handle

Handle dispatches on the resource under review: a WatchRule is validated (and may be rejected), and a command CREATE has its {uid → author} recorded before the object persists (the authorship invariant, §2) and is then allowed. Every early return on the command path still allows: an unrecognised kind, a dry-run, a missing uid, or an unauthenticated request simply records nothing and leaves the request without a claimed actor downstream.

Jump to

Keyboard shortcuts

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