Skip to content

OCPBUGS-43353: Add pattern validation for registry entries in image config#2787

Draft
reedcort wants to merge 1 commit intoopenshift:masterfrom
reedcort:OCPBUGS-43353
Draft

OCPBUGS-43353: Add pattern validation for registry entries in image config#2787
reedcort wants to merge 1 commit intoopenshift:masterfrom
reedcort:OCPBUGS-43353

Conversation

@reedcort
Copy link
Copy Markdown

@reedcort reedcort commented Mar 31, 2026

Summary

  • Add per-item Pattern validation to insecureRegistries, blockedRegistries, and allowedRegistries fields in the RegistrySources struct (config/v1/types_image.go)
  • Uses the same regex pattern already used by ImageDigestMirrors.Source for consistency
  • Rejects entries with tags (e.g., :latest) or digests (e.g., @sha256:...) that would generate invalid /etc/containers/policy.json and can cause container runtime failures
  • CRD validation ratcheting automatically preserves pre-existing invalid entries on update

Background

Invalid registry entries like registry.io/myrepo:latest in spec.registrySources.blockedRegistries pass through without validation and generate an invalid /etc/containers/policy.json, which can cause container runtime failures.

Validation was initially proposed in hypershift#8070 at the Go level (webhook, controller, nodepool config). That PR was closed in favor of adding the canonical validation here at the API level, as requested by the maintainer.

Test plan

  • make update — all CRD manifests regenerated
  • make -C config/v1 test — all integration tests pass
  • make verify — passes
  • Ratcheting tests verify persisted invalid entries are preserved on update
  • onCreate tests verify invalid tagged/digest entries are rejected
  • Valid entry tests cover hostname, hostname/path, wildcard, hostname:port/path

🤖 Generated with Claude Code

@openshift-ci-robot
Copy link
Copy Markdown

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 31, 2026

Hello @reedcort! Some important instructions when contributing to openshift/api:
API design plays an important part in the user experience of OpenShift and as such API PRs are subject to a high level of scrutiny to ensure they follow our best practices. If you haven't already done so, please review the OpenShift API Conventions and ensure that your proposed changes are compliant. Following these conventions will help expedite the api review process for your PR.

@openshift-ci openshift-ci bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 8c3c4841-83b1-4444-9d3a-a7c745d8173f

📥 Commits

Reviewing files that changed from the base of the PR and between 6ab1419 and 44dae4a.

⛔ Files ignored due to path filters (4)
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_images.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.featuregated-crd-manifests/images.config.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/images.config.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • openapi/generated_openapi/zz_generated.openapi.go is excluded by !openapi/**
📒 Files selected for processing (4)
  • config/v1/tests/images.config.openshift.io/AAA_ungated.yaml
  • config/v1/types_image.go
  • config/v1/zz_generated.swagger_doc_generated.go
  • payload-manifests/crds/0000_10_config-operator_01_images.crd.yaml
✅ Files skipped from review due to trivial changes (1)
  • config/v1/zz_generated.swagger_doc_generated.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • config/v1/types_image.go
  • payload-manifests/crds/0000_10_config-operator_01_images.crd.yaml

📝 Walkthrough

Walkthrough

Added per-item regex validation to RegistrySources' insecureRegistries, blockedRegistries, and allowedRegistries, enforcing entries to match a registry-scope format hostname[:port][/path] with an optional *. wildcard for subdomains and explicitly rejecting image tags or digests. Corresponding field comments and Swagger documentation were updated to describe the format and restrictions. The CRD YAML item schemas were modified to include the new patterns. New conformance tests were added to validate acceptance and rejection of various registry entry formats on create and update.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

Error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented
The command is terminated due to an error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 31, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign deads2k for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@config/v1/types_image.go`:
- Line 176: The XValidation:rule regex used in the kubebuilder tag for registry
hostnames in types_image.go is too permissive and must be replaced with a
stricter pattern that: disallows empty labels and consecutive dots, disallows
labels that start or end with a hyphen, allows an optional leading wildcard of
the form "*.example.com", allows an optional ":port" immediately after the
hostname (port = one or more digits), and permits optional path segments
("/repo" parts) composed of lowercase letters, digits, dot, underscore or
hyphen; update the XValidation:rule value (the kubebuilder tag string containing
the regex) to enforce these constraints (i.e., ensure each label matches
[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])? and labels are separated by single
dots, optional leading "*." allowed, optional :[0-9]+ after the hostname, then
optional (/[a-z0-9._-]+)* path segments) so valid hosts like "good.example.com",
"quay.io/ns/repo", "*.example.com", and "a.b" are accepted while invalid ones
like "example..com" or "a.-b.com" are rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 3199948e-925c-4a7a-8be3-c0988a0268e3

📥 Commits

Reviewing files that changed from the base of the PR and between 96f1f5a and 6ab1419.

⛔ Files ignored due to path filters (4)
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_images.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.featuregated-crd-manifests/images.config.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/images.config.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • openapi/generated_openapi/zz_generated.openapi.go is excluded by !openapi/**
📒 Files selected for processing (3)
  • config/v1/types_image.go
  • config/v1/zz_generated.swagger_doc_generated.go
  • payload-manifests/crds/0000_10_config-operator_01_images.crd.yaml

@JoelSpeed
Copy link
Copy Markdown
Contributor

@reedcort These validations are restricting existing GA APIs. How can you be confident in the maximum limits you have added, and that they won't break existing users/use cases? Do you have any telemetry data that would demonstrate the existing usage of these fields?

@reedcort
Copy link
Copy Markdown
Author

@JoelSpeed I noticed that as well. I'm updating the validation to use Pattern instead, which removes the MaxItems and MaxLength constraints entirely to avoid impacting current users.

@JoelSpeed
Copy link
Copy Markdown
Contributor

I noticed that as well. I'm updating the validation to use Pattern instead, which removes the MaxItems and MaxLength constraints entirely to avoid impacting current users.

Any change you make here is going to potentially be breaking. There are existing users of this API. You need to understand/explain how this will impact existing users before we can accept any change that makes the API more restrictive.

We actually do prefer CEL to pattern btw, but we still need to answer my previous questions. What do you know about existing usage of these API fields?

@reedcort reedcort marked this pull request as draft March 31, 2026 16:55
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 31, 2026
@reedcort
Copy link
Copy Markdown
Author

Thanks, makes sense. I'll work on gathering telemetry data on the existing usage of the fields across the fleet.

@reedcort reedcort changed the title Add CEL validation for registry entries in image config OCPBUGS-43353: Add CEL validation for registry entries in image config Apr 1, 2026
@openshift-ci-robot openshift-ci-robot added jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. labels Apr 1, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@reedcort: This pull request references Jira Issue OCPBUGS-43353, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @xiuwang

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

  • Add per-item CEL validation rules to insecureRegistries, blockedRegistries, and allowedRegistries fields in the RegistrySources struct (config/v1/types_image.go)
  • Each entry is validated against a regex matching valid registry scopes: hostname[:port][/path], optionally with *. wildcard prefix
  • Rejects entries with tags (e.g., :latest) or digests (e.g., @sha256:...) that would generate invalid /etc/containers/policy.json and cause CRI-O to fail
  • Adds MaxItems=512 and items:MaxLength=512 bounds required by the Kubernetes CEL cost estimator

Background

Invalid registry entries like trusted.com/myrepo:latest in spec.configuration.image.registrySources.blockedRegistries pass through without validation, generate an invalid /etc/containers/policy.json, cause CRI-O startup failure, and result in nodes silently failing to join the cluster.

Validation was initially proposed in hypershift#8070 at the Go level (webhook, controller, nodepool config). That PR was closed in favor of adding the canonical CEL validation here at the API level, as requested by the maintainer.

Test plan

  • make update — all CRD manifests regenerated
  • make -C config/v1 test — all 1761 integration tests pass
  • make verify — passes (0 lint issues, 0 kube-api-linter issues)
  • Verify on a staging cluster that invalid entries are rejected at admission

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot requested a review from xiuwang April 1, 2026 15:38
@reedcort
Copy link
Copy Markdown
Author

reedcort commented Apr 2, 2026

@JoelSpeed I gathered telemetry data to answer your questions.

Telemetry

Queried CCX/Insights archive across the fleet:

Metric Count
Total clusters with registrySources data ~61,241
Clusters with tagged entries (e.g. :latest) ~108 (0.18%)

I also verified that setting MaxItems=512 and MaxLength=512 would not impact any existing clusters in the fleet.

Approach

I plan to update the PR to use the same regex pattern already used by ImageDigestMirrors.Source in this repo (config/v1/types_image_digest_mirror_set.go), which supports both wildcard prefixes and Docker-style path separators (__, ---). This rejects tags while allowing all valid registry scopes (hostname[:port][/path]).

Kubernetes CRD validation ratcheting automatically handles existing invalid entries — on update, the API server suppresses validation errors for unchanged values. The ~108 clusters with existing tagged entries would be unaffected; only new invalid entries would be rejected.

One nuance worth noting: during testing I found that reg1.io/myrepo/myapp:latest (2+ path components) is technically valid in policy.json, while trusted.com/myrepo:latest is not — so the breakage from tagged entries is format-dependent and unpredictable for users. Adding this regex would reject both. Since these fields are called blockedRegistries/allowedRegistries/insecureRegistries — semantically they represent registry/repo scopes, not image references — I think this is the right behavior.

Do you think this validation belongs here in the API, or would it be better placed in the MCO / HyperShift operator instead?

@JoelSpeed
Copy link
Copy Markdown
Contributor

Thanks for doing the research. As long as we can add a test case that demonstrates the ratcheting behaviour of currently invalid values (see our tests folder readme and look for ratcheting to see examples), then I think we can proceed with adding the validations here

Invalid registry entries (e.g., with tags like ":latest" or digests like
"@sha256:...") in registrySources fields generate an invalid
/etc/containers/policy.json, which can cause container runtime failures.

Add per-item Pattern validation to insecureRegistries, blockedRegistries,
and allowedRegistries fields in the RegistrySources struct using the same
regex pattern already used by ImageDigestMirrors.Source. Each entry must
match a valid registry scope: hostname[:port][/path], optionally with a
wildcard prefix (*.). CRD validation ratcheting ensures pre-existing
invalid entries are preserved on update.

Includes integration tests demonstrating:
- Invalid tagged/digest entries are rejected on create
- Valid entries (hostname, path, wildcard, port) are accepted
- Ratcheting preserves persisted invalid entries when other fields change
- Modifying an atomic list with a persisted invalid entry is rejected

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@reedcort reedcort changed the title OCPBUGS-43353: Add CEL validation for registry entries in image config OCPBUGS-43353: Add pattern validation for registry entries in image config Apr 2, 2026
@reedcort
Copy link
Copy Markdown
Author

reedcort commented Apr 2, 2026

/test lint
/test integration
/test verify
/test verify-client-go
/test verify-crd-schema
/test verify-crdify
/test verify-deps
/test verify-feature-promotion

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 2, 2026

PR-Agent: could not fine a component named lint in a supported language in this PR.

@reedcort
Copy link
Copy Markdown
Author

reedcort commented Apr 2, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 2, 2026

@reedcort: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants