Registry Authentication in Kyverno#
Overview#
Registry authentication in Kyverno controls how the admission controller pulls image metadata from container registries. This matters in two distinct code paths:
- Image verification (
ImageValidatingPolicy, classicClusterPolicy) — uses the legacyregistryclientpackage - CEL image data (
MutatingPolicy,ValidatingPolicy,ImageValidatingPolicyCEL functions) — uses theimagedataloadermodule from the Kyverno SDK
Until recently, only the legacy path was wired to CLI credential flags. The CEL path received nil for auth options, causing image.GetMetadata() to fail with 401 Unauthorized against private registries.
The imagedataloader SDK Module (PR #15450)#
PR #15450 (merged March 2026) migrated all image data loading from an internal package (pkg/imageverification/imagedataloader/*, ~1000 lines) to the shared SDK module github.com/kyverno/sdk/extensions/imagedataloader . This affected 19 files across CLI commands, CEL libraries, and image verification evaluators.
The SDK module is now the canonical source for image fetching logic. All previously internal types (Fetcher, ImageData, option constructors) are provided by the SDK.
Key entry points after migration:
pkg/cel/libs/context.go—NewContextProvideraccepts[]imagedataloader.Optionand passes them toimagedataloader.New()pkg/cel/libs/imageverify/utils.go—GetRemoteOptsFromPolicyconverts policy-levelCredentialsinto[]imagedataloader.Optionviaimagedataloader.BuildRemoteOpts
The Auth Gap in CEL Context Provider (Issue #15776)#
After the SDK migration, NewContextProvider in all three controller entry points (cmd/kyverno/main.go, cmd/background-controller/main.go, cmd/cleanup-controller/main.go) passed nil for the imageOpts argument :
contextProvider, err := libs.NewContextProvider(
setup.KyvernoDynamicClient,
nil, // <-- no credentials passed
gcstore,
restMapper,
false,
)
This meant any image.GetMetadata() call in a MutatingPolicy or ValidatingPolicy variable expression would attempt registry pulls anonymously. Private registries would return 401 Unauthorized; with failurePolicy: Ignore the policy silently no-ops, and with failurePolicy: Fail it blocks admission .
The CEL-based MutatingPolicy had no per-policy credentials field (unlike ImageValidatingPolicy), so there was no policy-level workaround available either .
The Fix: Wiring CLI Flags into the CEL Context Provider (PR #16491)#
PR #16491 (filed July 2026) closes this gap by threading the existing registry CLI flags through to the CEL image loader :
1. New helper in cmd/internal/registry.go
imageLoaderOptions() calls imagedataloader.BuildRemoteOpts using the three global CLI flag variables already parsed by cmd/internal/flag.go:
imagePullSecrets— comma-separated Kubernetes secret namesregistryCredentialHelpers— cloud credential helper names (e.g., ECR via IRSA)allowInsecureRegistry— insecure registry flag
A companion splitAndTrim() utility normalizes the comma-separated strings.
2. ImageLoaderOptions field on SetupResult
cmd/internal/setup.go gains an ImageLoaderOptions []imagedataloader.Option field, populated during Setup() by calling imageLoaderOptions(). This distributes the options consistently to all controllers.
3. Context provider calls updated
libs.NewContextProvider(...) in all three controller mains now passes setup.ImageLoaderOptions instead of nil, ensuring the imagedataloader.Fetcher stored in contextProvider carries full registry credentials.
The authentication flow after the fix:
CLI flags (--imagePullSecrets, --registryCredentialHelpers, --allowInsecureRegistry)
→ imageLoaderOptions() [cmd/internal/registry.go]
→ SetupResult.ImageLoaderOptions [cmd/internal/setup.go]
→ libs.NewContextProvider(...) [cmd/kyverno/main.go, background-controller, cleanup-controller]
→ imagedataloader.New() [pkg/cel/libs/context.go:88]
→ contextProvider.imagedata (Fetcher)
→ image.GetMetadata() in CEL expressions
No new user-facing configuration is needed — the same flags that configure image verification already control CEL image data access after this fix .
Credential Sources Supported#
The imagedataloader.BuildRemoteOpts function (consumed by both paths) supports:
| Source | CLI Flag / Config |
|---|---|
| Kubernetes image pull secrets | --imagePullSecrets |
| Cloud credential helpers (ECR, GCR, ACR, etc.) | --registryCredentialHelpers |
| Insecure (HTTP) registries | --allowInsecureRegistry |
| Policy-level secrets (ImageValidatingPolicy only) | credentials.secrets in policy spec, via GetRemoteOptsFromPolicy |
Key Files#
| File | Purpose |
|---|---|
pkg/cel/libs/context.go | NewContextProvider — accepts and stores imagedataloader.Option |
pkg/cel/libs/imageverify/utils.go | GetRemoteOptsFromPolicy — policy-level credentials → options |
cmd/internal/registry.go | imageLoaderOptions() and setupRegistryClient() |
cmd/internal/setup.go | SetupResult struct — shared config for all controllers |
cmd/kyverno/main.go | Admission controller context provider initialization |