Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPBUGS-36344: Add CIP relevant mirrors to sigstore attachement cfg #4449

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@ func registriesConfigIgnition(templateDir string, controllerConfig *mcfgv1.Contr
return nil, fmt.Errorf("could not update policy json with new changes: %w", err)
}
// generates configuration under /etc/containers/registries.d to enable sigstore verification
sigstoreRegistriesConfigYaml, err = generateSigstoreRegistriesdConfig(clusterScopePolicies)
sigstoreRegistriesConfigYaml, err = generateSigstoreRegistriesdConfig(clusterScopePolicies, registriesTOML)
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ func verifyRegistriesConfigAndPolicyJSONContents(t *testing.T, mc *mcfgv1.Machin
}

if verifyImagePoliciesRegistriesConfig {
expectedRegistriesConfd, err := generateSigstoreRegistriesdConfig(clusterScopePolicies)
expectedRegistriesConfd, err := generateSigstoreRegistriesdConfig(clusterScopePolicies, expectedRegistriesConf)
require.NoError(t, err)
foundFile := false

Expand Down Expand Up @@ -1794,3 +1794,54 @@ func TestClusterImagePolicyCreate(t *testing.T) {
})
}
}

func TestSigstoreRegistriesConfigIDMSandCIPCreate(t *testing.T) {
for _, platform := range []apicfgv1.PlatformType{apicfgv1.AWSPlatformType, apicfgv1.NonePlatformType, "unrecognized"} {
t.Run(string(platform), func(t *testing.T) {
f := newFixture(t)

cc := newControllerConfig(ctrlcommon.ControllerConfigName, platform)
mcp := helpers.NewMachineConfigPool("master", nil, helpers.MasterSelector, "v0")
mcp2 := helpers.NewMachineConfigPool("worker", nil, helpers.WorkerSelector, "v0")
imgcfg1 := newImageConfig("cluster", &apicfgv1.RegistrySources{InsecureRegistries: []string{"blah.io"}, AllowedRegistries: []string{"example.com"}, ContainerRuntimeSearchRegistries: []string{"search-reg.io"}})

cvcfg1 := newClusterVersionConfig("version", "test.io/myuser/myimage:test")
keyReg1, _ := getManagedKeyReg(mcp, nil)
keyReg2, _ := getManagedKeyReg(mcp2, nil)

mcs1 := helpers.NewMachineConfig(keyReg1, map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}})
mcs2 := helpers.NewMachineConfig(keyReg2, map[string]string{"node-role": "worker"}, "dummy://", []ign3types.File{{}})

// idms source is the same as cip scope
idms := newIDMS("built-in", []apicfgv1.ImageDigestMirrors{
{Source: "built-in-source.example.com", Mirrors: []apicfgv1.ImageMirror{"built-in-mirror.example.com"}},
})
clusterimgPolicy := newClusterImagePolicyWithPublicKey("built-in-source.example.com", []string{"example.com"}, []byte("foo bar"))
f.ccLister = append(f.ccLister, cc)
f.mcpLister = append(f.mcpLister, mcp)
f.mcpLister = append(f.mcpLister, mcp2)
f.imgLister = append(f.imgLister, imgcfg1)
f.idmsLister = append(f.idmsLister, idms)
f.clusterImagePolicyLister = append(f.clusterImagePolicyLister, clusterimgPolicy)
f.cvLister = append(f.cvLister, cvcfg1)
f.imgObjects = append(f.imgObjects, imgcfg1)

f.expectGetMachineConfigAction(mcs1)
f.expectGetMachineConfigAction(mcs1)
f.expectGetMachineConfigAction(mcs1)
f.expectCreateMachineConfigAction(mcs1)

f.expectGetMachineConfigAction(mcs2)
f.expectGetMachineConfigAction(mcs2)
f.expectGetMachineConfigAction(mcs2)

f.expectCreateMachineConfigAction(mcs2)

f.run("")

for _, mcName := range []string{mcs1.Name, mcs2.Name} {
f.verifyRegistriesConfigAndPolicyJSONContents(t, mcName, imgcfg1, nil, idms, nil, clusterimgPolicy, cc.Spec.ReleaseImage, true, true, true)
}
})
}
}
134 changes: 128 additions & 6 deletions pkg/controller/container-runtime-config/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
storageconfig "github.com/containers/storage/pkg/config"
ign3types "github.com/coreos/ignition/v2/config/v3_4/types"
"github.com/ghodss/yaml"
"github.com/opencontainers/go-digest"
apicfgv1 "github.com/openshift/api/config/v1"
apicfgv1alpha1 "github.com/openshift/api/config/v1alpha1"
apioperatorsv1alpha1 "github.com/openshift/api/operator/v1alpha1"
Expand Down Expand Up @@ -933,17 +934,73 @@ func validateClusterImagePolicyWithAllowedBlockedRegistries(clusterScopePolicies
return nil
}

func generateSigstoreRegistriesdConfig(clusterScopePolicies map[string]signature.PolicyRequirements) ([]byte, error) {
func generateSigstoreRegistriesdConfig(clusterScopePolicies map[string]signature.PolicyRequirements, registriesTOML []byte) ([]byte, error) {
if len(clusterScopePolicies) == 0 {
return nil, nil
}

registriesDockerConfig := make(map[string]dockerConfig)
sigstoreAttachment := dockerConfig{
UseSigstoreAttachments: true,
var (
err error
tmpFile *os.File
registriesConf = sysregistriesv2.V2RegistriesConf{}
registriesDockerConfig = make(map[string]dockerConfig)
sigstoreAttachment = dockerConfig{
UseSigstoreAttachments: true,
}
)

if registriesTOML != nil {
tmpFile, err = os.CreateTemp("", "regtemp")
if err != nil {
return nil, err
}
defer func() {
tmpFile.Close()
os.Remove(tmpFile.Name())
}()

_, err = tmpFile.Write(registriesTOML)
if err != nil {
return nil, err
}

if _, err := toml.NewDecoder(bytes.NewBuffer(registriesTOML)).Decode(&registriesConf); err != nil {
return nil, fmt.Errorf("error decoding registries config: %w", err)
}
}
for scope := range clusterScopePolicies {
registriesDockerConfig[scope] = sigstoreAttachment

for policyScope := range clusterScopePolicies {
registriesDockerConfig[policyScope] = sigstoreAttachment
Copy link
Contributor

Choose a reason for hiding this comment

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

(Non-blocking: The entry for the primary repo is not necessary if NeverContactSource … but it also doesn’t really hurt; and otherwise we would need to special-case the “no configuration for registry” code path, i.e. this is a bit simpler than being 100% precise.)

if registriesTOML == nil {
continue
}
sysContext := &types.SystemContext{
SystemRegistriesConfPath: tmpFile.Name(),
SystemRegistriesConfDirPath: os.DevNull,
}
parentReg, err := sysregistriesv2.FindRegistry(sysContext, policyScope)
if err != nil {
return nil, err
}

if err = addScopeMirrorsSigstoreRegistriesdConfig(registriesDockerConfig, policyScope, parentReg, sigstoreAttachment); err != nil {
return nil, fmt.Errorf("error adding clusterimagepolicy scope %s relevant mirrors: %w", policyScope, err)
}
for _, reg := range registriesConf.Registries {
scope := reg.Location
if reg.Prefix != "" {
scope = reg.Prefix
}
if runtimeutils.ScopeIsNestedInsideScope(scope, policyScope) && scope != policyScope {
nestedReg, err := sysregistriesv2.FindRegistry(sysContext, scope)
if err != nil {
return nil, err
}
if err = addScopeMirrorsSigstoreRegistriesdConfig(registriesDockerConfig, scope, nestedReg, sigstoreAttachment); err != nil {
return nil, fmt.Errorf("error adding clusterimagepolicy scope %s relevant mirrors: %w", policyScope, err)
}
}
}
}

registriesConfig := &registriesConfig{}
Expand All @@ -954,3 +1011,68 @@ func generateSigstoreRegistriesdConfig(clusterScopePolicies map[string]signature
}
return data, nil
}

func addScopeMirrorsSigstoreRegistriesdConfig(registriesDockerConfig map[string]dockerConfig, scope string, reg *sysregistriesv2.Registry, sigstoreAttachment dockerConfig) error {
if reg == nil {
return nil
}
dummyDigest := "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
dummyTag := "aaa"
dummyPath := "/a"
dummyPrefix := "matched"
addedDummy := false

if strings.HasPrefix(scope, "*.") {
scope = strings.Replace(scope, "*", dummyPrefix, 1)
}

if !strings.Contains(scope, "/") {
scope += dummyPath
addedDummy = true
}

scopeRef, err := reference.Parse(scope)
Copy link
Contributor

@mtrmac mtrmac Jul 8, 2024

Choose a reason for hiding this comment

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

[As a general rule, prefer ParseNormalizedNamed. It shouldn’t make a difference for valid scopes, … but do we actually validate that? IsValidRegistriesConfScope seems rather loose, and I’m not sure that there is any other location validating scopes more strictly.

I’m … worried what happens in the “scope == host name” case — AFAICS reference.Parse turns that into "[nothing]/$input". But it might work well enough.

OTOH with ParseNormalizedNamed, the dummyPath part would really have to happen before this parsing. See elsewhere.]

Copy link
Contributor

Choose a reason for hiding this comment

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

On second thought, ParseNormalizedNamed("docker.io/library") would produce an unwanted result (or we would have to work around that even more); reference.Parse() is really better here.

if err != nil {
return fmt.Errorf("error parsing scope %s: %w", scope, err)
}

namedScopeRef, ok := scopeRef.(reference.Named)
if !ok {
return fmt.Errorf("scope %s is not a named reference", scope)
}
repo := reference.TrimNamed(namedScopeRef)

d, _ := digest.Parse(dummyDigest)
digestRef, err := reference.WithDigest(repo, d)
if err != nil {
return fmt.Errorf("error parsing digest name for scope %s: %w", scope, err)
}
tagRef, err := reference.WithTag(repo, dummyTag)
if err != nil {
return fmt.Errorf("error parsing tag name for scope %s: %w", scope, err)
}

digestSources, err := reg.PullSourcesFromReference(digestRef)
if err != nil {
return fmt.Errorf("error getting digest sources for scope %s: %w", scope, err)
}
tagSources, err := reg.PullSourcesFromReference(tagRef)
if err != nil {
return fmt.Errorf("error getting tag sources for scope %s: %w", scope, err)
}
for _, s := range append(digestSources, tagSources...) {
endpoint := s.Reference.Name()
if addedDummy {
endpoint = strings.TrimSuffix(endpoint, dummyPath)
}
// "Location" is empty if the matched Registry source containers a wildcard, only add mirrors in this case
// caller has unconditionally configured attachments for ClusterImagePolicy scopes in registriesDockerConfig[policyScope].
// If a dummyPrefix was added to process a wildcard Registry entry, we only want to configure the mirror endpoints, if any;
// we do not want to add a new entry for exactly dummyPrefix + originalScope.
if s.Endpoint.Location == "" {
continue
}
registriesDockerConfig[endpoint] = sigstoreAttachment
}
return nil
}
Loading