Skip to content

✨ Accept per-host pull secrets for external OCI registries#2745

Open
mabulgu wants to merge 2 commits intometal3-io:mainfrom
mabulgu:feature/bmo-per-host-oci-auth
Open

✨ Accept per-host pull secrets for external OCI registries#2745
mabulgu wants to merge 2 commits intometal3-io:mainfrom
mabulgu:feature/bmo-per-host-oci-auth

Conversation

@mabulgu
Copy link
Contributor

@mabulgu mabulgu commented Oct 22, 2025

What this PR does / why we need it:

Adds per-host registry authentication for oci:// provisioning images.
New optional field on BareMetalHost:

spec:
  image:
    url: oci://<registry>/<repo>/<artifact>:<tag|digest>
    authSecretName: <k8s-secret-name>  # optional; same namespace as the BMH

When set, the controller validates the Kubernetes Docker-config secret (kubernetes.io/dockerconfigjson or kubernetes.io/dockercfg), selects the correct registry entry (supports exact host and host:port), and uses those credentials during provisioning so private OCI artefacts can be fetched on a per-host basis. Public images continue to work without credentials.
This removes a blocker for users who need different registries/accounts per machine.

Assisted-By: Claude-4.5-sonnet

@metal3-io-bot metal3-io-bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 22, 2025
@metal3-io-bot metal3-io-bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Oct 22, 2025
@metal3-io-bot
Copy link
Contributor

Hi @mabulgu. Thanks for your PR.

I'm waiting for a metal3-io member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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.

@metal3-io-bot metal3-io-bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Oct 22, 2025
@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from 898fffc to 9594af2 Compare October 22, 2025 10:52
@mabulgu mabulgu marked this pull request as ready for review October 27, 2025 09:48
@mabulgu mabulgu changed the title [WIP] ✨ Accept per-host pull secrets for customer OCI registries ✨ Accept per-host pull secrets for customer OCI registries Oct 27, 2025
@metal3-io-bot metal3-io-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 27, 2025
@dtantsur
Copy link
Member

/ok-to-test

@metal3-io-bot metal3-io-bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 27, 2025
@MahnoorAsghar
Copy link
Contributor

It might be too much code to be squashing into one commit...what do we think?

@tuminoid
Copy link
Member

tuminoid commented Oct 28, 2025

It might be too much code to be squashing into one commit...what do we think?

10 is definitely not right either. One for implementation, one for tesrts, and one for docs? There is plenty of work left though, given the conditions need work per Dmitry's review.

@mabulgu
Copy link
Contributor Author

mabulgu commented Nov 3, 2025

Thanks for your comments!

@MahnoorAsghar > It might be too much code to be squashing into one commit...what do we think?

As soon as we itemize them and they are in the same context, I don't think so. I am going to remove everything related to conditions as they were like extra, but everything else share the same context and can be in the same squash commit IMO.

@tuminoid > One for implementation, one for tesrts, and one for docs

+1 for docs -1 for tests as tests are a part of the "implementation". Without tests, I would not count it as implemented.

What I will do is: seperating the code commit (which will have less changes than the current changes because of the condition revert) and the commit for docs.

@tuminoid
Copy link
Member

tuminoid commented Nov 3, 2025

Thanks for your comments!
@tuminoid > One for implementation, one for tesrts, and one for docs

+1 for docs -1 for tests as tests are a part of the "implementation". Without tests, I would not count it as implemented.

This is fine as well, but its quite common to implement tests in separate commit in same PR. Makes it maybe easier to manage, but like said, I'm 100% fine with code+tests in same commit.

@tuminoid
Copy link
Member

tuminoid commented Nov 9, 2025

/retest

@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch 2 times, most recently from b18570e to 27c9860 Compare November 11, 2025 15:19
@mabulgu mabulgu requested a review from dtantsur November 11, 2025 15:22
@mabulgu
Copy link
Contributor Author

mabulgu commented Nov 11, 2025

@dtantsur I applied your suggestions. pls check when you have time. You will find the relevant commetns resolved but pls feel free to reopen them if you feel any of them are not implemented the way you suggested

@mabulgu
Copy link
Contributor Author

mabulgu commented Nov 12, 2025

Not sure if the e2e test filure is related to my changes as it seems to be related to the BMC management credentials

@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from 10854e7 to ae36d3a Compare February 16, 2026 15:24
@mabulgu
Copy link
Contributor Author

mabulgu commented Feb 17, 2026

/retest

@mabulgu mabulgu requested a review from tuminoid February 18, 2026 13:00
@elfosardo
Copy link
Member

/approve

@metal3-io-bot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: elfosardo

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

The pull request process is described 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

@metal3-io-bot metal3-io-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 18, 2026
Copy link
Member

@Rozzii Rozzii left a comment

Choose a reason for hiding this comment

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

In general great improvement and very well tested.
I mainly paid attention to the new logic, just skimmed over the tests.
I have a few nits but in general looks very nice.

I know I have a naming related comment and naming things is hard but I think in this case the name really could be a bit more specific.

Also please rebase the PR.
I have also triggered Co-Pilote reviews too because this change is quite large 1400+ lines.

ociRelevant := img.IsOCI()

// No per-host secret referenced → not required, valid for public images
if img.AuthSecretName == nil || *img.AuthSecretName == "" {
Copy link
Member

Choose a reason for hiding this comment

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

Similar question as above wasn't this tested in getImageAuthSecret ?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@metal3-io-bot metal3-io-bot added the needs-rebase Indicates that a PR cannot be merged because it has merge conflicts with HEAD. label Feb 19, 2026
@mabulgu mabulgu changed the title ✨ Accept per-host pull secrets for customer OCI registries ✨ Accept per-host pull secrets for external OCI registries Mar 2, 2026
@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from ae36d3a to 14d69b3 Compare March 2, 2026 10:40
@metal3-io-bot metal3-io-bot removed the needs-rebase Indicates that a PR cannot be merged because it has merge conflicts with HEAD. label Mar 2, 2026
@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from 14d69b3 to cc34dc5 Compare March 2, 2026 10:52

// Warn if secret is set for non-OCI image (future-proof, do not fail)
if !ociRelevant && v.recorder != nil {
v.recorder.Eventf(bmh, corev1.EventTypeWarning, EventAuthSecretIrrelevant,
Copy link
Member

Choose a reason for hiding this comment

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

Side note: we already have a different pattern to publish events in the controller, we need to reconcile them eventually..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, that's worth a follow-up to unify the event publishing patterns across the controller.

@dtantsur
Copy link
Member

dtantsur commented Mar 2, 2026

@mabulgu the linter failure looks real from a quick glance + a couple of minor comments

@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from cc34dc5 to 0bd7da0 Compare March 4, 2026 11:08
mabulgu added 2 commits March 4, 2026 14:14
This feature allows users to specify per-host OCI registry credentials
via spec.image.authSecretName for authenticated container image pulls.

Key changes:
- Add AuthSecretName field to Image struct in BareMetalHost API
- Add IsOCI() method to Image for checking OCI scheme
- Implement ImageAuthValidator for secret validation and credential extraction
- Add dockerconfig parser using cpuguy83/dockercfg library
- Pass extracted credentials to Ironic provisioner for OCI images
- Support both kubernetes.io/dockerconfigjson and legacy dockercfg formats

Signed-off-by: mabulgu <mabulgu@gmail.com>

# Conflicts:
#	internal/controller/metal3.io/baremetalhost_controller.go

# Conflicts:
#	internal/controller/metal3.io/baremetalhost_controller.go
Comprehensive test coverage for the OCI image authentication feature:

pkg/secretutils/dockerconfig_test.go:
- TestExtractRegistryCredentials: dockerconfigjson format tests
- TestExtractRegistryCredentials_LegacyDockerCfg: legacy dockercfg format
- TestExtractRegistryHost: URL parsing tests

internal/controller/metal3.io/image_auth_validator_test.go:
- Tests for ImageAuthValidator with various secret types and error cases

internal/controller/metal3.io/baremetalhost_controller_test.go:
- Integration tests for getImageAuthSecret covering:
  - OCI image with valid/invalid/missing auth secret
  - Non-OCI images with auth secret (should be ignored)
  - Registry mismatch scenarios

Signed-off-by: mabulgu <mabulgu@gmail.com>
@mabulgu mabulgu force-pushed the feature/bmo-per-host-oci-auth branch from 0bd7da0 to 645c8b1 Compare March 4, 2026 11:16
@mabulgu mabulgu requested review from Rozzii and dtantsur March 4, 2026 14:42
@mabulgu
Copy link
Contributor Author

mabulgu commented Mar 4, 2026

@mabulgu the linter failure looks real from a quick glance + a couple of minor comments

Thanks @dtantsur. I addressed the feedback + commented for one. Pls check when available.

authSecret, err := r.getImageAuthSecret(ctx, info.host, &image)
if err != nil {
return recordActionFailure(info, metal3api.ProvisioningError,
"failed to get image auth secret: "+err.Error())
Copy link
Member

Choose a reason for hiding this comment

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

nit: the error messages that getImageAuthSecret produces already contain enough context, you don't need to add more here

res.Credentials = credentials
}

res.Secret = sec
Copy link
Member

Choose a reason for hiding this comment

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

It looks like Secret is actually unused? In this case, you can simplify this function to return (credentials, error). After all, that's what getImageAuthSecret returns anyway.

return res, nil
}

func isAllowedDockerConfigType(t corev1.SecretType) bool {
Copy link
Member

Choose a reason for hiding this comment

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

nit: inline this

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants