Skip to content
Open
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
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
k8s_networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
ocp_configv1 "github.com/openshift/api/config/v1"
ocp_configv1alpha1 "github.com/openshift/api/config/v1alpha1"
machineconfig "github.com/openshift/api/machineconfiguration/v1"
ocp_image "github.com/openshift/api/operator/v1alpha1"
routev1 "github.com/openshift/api/route/v1"
Expand Down Expand Up @@ -124,6 +125,7 @@ func init() {
utilruntime.Must(certmgrv1.AddToScheme(scheme))
utilruntime.Must(barbicanv1.AddToScheme(scheme))
utilruntime.Must(ocp_configv1.AddToScheme(scheme))
utilruntime.Must(ocp_configv1alpha1.Install(scheme))
utilruntime.Must(ocp_image.AddToScheme(scheme))
utilruntime.Must(machineconfig.AddToScheme(scheme))
utilruntime.Must(k8s_networkv1.AddToScheme(scheme))
Expand Down
23 changes: 23 additions & 0 deletions internal/dataplane/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,29 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
}
}

// Propagate sigstore verification settings from ClusterImagePolicy to EDPM.
if hasMirrorRegistries {
mirrorScopes, err := util.GetMirrorRegistryScopes(ctx, helper)
if err != nil {
return "", fmt.Errorf("failed to get mirror registries for sigstore verification: %w", err)
}

sigstorePolicy, err := util.GetSigstoreImagePolicy(ctx, helper, mirrorScopes)
if err != nil {
return "", fmt.Errorf("failed to get ClusterImagePolicy for sigstore verification: %w", err)
} else if sigstorePolicy != nil {
nodeSetGroup.Vars["edpm_container_signature_verification"] = true
nodeSetGroup.Vars["edpm_container_signature_mirror_registry"] = sigstorePolicy.MirrorRegistry
nodeSetGroup.Vars["edpm_container_signature_cosign_key_data"] = sigstorePolicy.CosignKeyData
if sigstorePolicy.SignedPrefix != "" {
nodeSetGroup.Vars["edpm_container_signature_signed_prefix"] = sigstorePolicy.SignedPrefix
}
} else {
helper.GetLogger().Info("No sigstore ClusterImagePolicy found for mirror registries. " +
"Continuing without signature verification on dataplane nodes.")
}
}

// add TLS ansible variable
nodeSetGroup.Vars["edpm_tls_certs_enabled"] = instance.Spec.TLSEnabled
if instance.Spec.Tags != nil {
Expand Down
179 changes: 179 additions & 0 deletions internal/dataplane/util/image_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"sort"
"strings"

ocpconfigv1 "github.com/openshift/api/config/v1"
ocpconfigv1alpha1 "github.com/openshift/api/config/v1alpha1"
mc "github.com/openshift/api/machineconfiguration/v1"
ocpicsp "github.com/openshift/api/operator/v1alpha1"
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
Expand Down Expand Up @@ -65,6 +67,70 @@ func HasMirrorRegistries(ctx context.Context, helper *helper.Helper) (bool, erro
return false, nil
}

func collectMirrorScopes(scopes map[string]struct{}, values []string) []string {
for _, value := range values {
scope := normalizeImageScope(value)
if scope != "" {
scopes[scope] = struct{}{}
}
}

if len(scopes) == 0 {
return nil
}

result := make([]string, 0, len(scopes))
for scope := range scopes {
result = append(result, scope)
}
sort.Strings(result)

return result
}

// GetMirrorRegistryScopes returns the configured mirror scopes, preferring IDMS
// and falling back to ICSP only when no IDMS mirror scopes are present.
// The returned values are normalized and de-duplicated for policy matching.
func GetMirrorRegistryScopes(ctx context.Context, helper *helper.Helper) ([]string, error) {
idmsList := &ocpconfigv1.ImageDigestMirrorSetList{}
if err := helper.GetClient().List(ctx, idmsList); err != nil {
if !IsNoMatchError(err) {
return nil, err
}
} else {
scopes := map[string]struct{}{}
for _, idms := range idmsList.Items {
for _, mirrorSet := range idms.Spec.ImageDigestMirrors {
mirrorValues := make([]string, 0, len(mirrorSet.Mirrors))
for _, mirror := range mirrorSet.Mirrors {
mirrorValues = append(mirrorValues, string(mirror))
}
result := collectMirrorScopes(scopes, mirrorValues)
if len(result) > 0 {
return result, nil
}
}
}
}

icspList := &ocpicsp.ImageContentSourcePolicyList{}
if err := helper.GetClient().List(ctx, icspList); err != nil {
if !IsNoMatchError(err) {
return nil, err
}
} else {
scopes := map[string]struct{}{}
for _, icsp := range icspList.Items {
for _, mirrorSet := range icsp.Spec.RepositoryDigestMirrors {
_ = collectMirrorScopes(scopes, mirrorSet.Mirrors)
}
}
return collectMirrorScopes(scopes, nil), nil
}

return nil, nil
}

// IsNoMatchError checks if the error indicates that a CRD/resource type doesn't exist
func IsNoMatchError(err error) bool {
errStr := err.Error()
Expand Down Expand Up @@ -151,6 +217,119 @@ func getMachineConfig(ctx context.Context, helper *helper.Helper) (mc.MachineCon
return masterMachineConfig, nil
}

// SigstorePolicyInfo contains the EDPM-relevant parts of a ClusterImagePolicy.
type SigstorePolicyInfo struct {
MirrorRegistry string
CosignKeyData string
SignedPrefix string
}

func normalizeImageScope(scope string) string {
return strings.TrimSuffix(strings.TrimSpace(scope), "/")
}

func clusterImagePolicyScopeMatchesMirror(policyScope string, mirrorScope string) bool {
policyScope = normalizeImageScope(policyScope)
mirrorScope = normalizeImageScope(mirrorScope)

if policyScope == "" || mirrorScope == "" {
return false
}

if strings.HasPrefix(policyScope, "*.") {
mirrorHost := strings.SplitN(mirrorScope, "/", 2)[0]
suffix := strings.TrimPrefix(policyScope, "*")
return strings.HasSuffix(mirrorHost, suffix)
}

return mirrorScope == policyScope || strings.HasPrefix(mirrorScope, policyScope+"/")
}

// GetSigstoreImagePolicy checks if OCP has a ClusterImagePolicy configured
// with sigstore signature verification for one of the mirror registries in use.
// Returns policy info if a relevant policy is found, nil if no policy exists.
// Returns nil without error if the ClusterImagePolicy CRD is not installed.
func GetSigstoreImagePolicy(ctx context.Context, helper *helper.Helper, mirrorScopes []string) (*SigstorePolicyInfo, error) {
if len(mirrorScopes) == 0 {
return nil, nil
}

policyList := &ocpconfigv1alpha1.ClusterImagePolicyList{}
if err := helper.GetClient().List(ctx, policyList); err != nil {
if IsNoMatchError(err) {
return nil, nil
}
return nil, err
}

var matches []string
var match *SigstorePolicyInfo

for _, policy := range policyList.Items {
if policy.Name == "openshift" {
continue
}

if policy.Spec.Policy.RootOfTrust.PolicyType != ocpconfigv1alpha1.PublicKeyRootOfTrust {
continue
}

if policy.Spec.Policy.RootOfTrust.PublicKey == nil {
continue
}

keyData := policy.Spec.Policy.RootOfTrust.PublicKey.KeyData
if len(keyData) == 0 {
continue
}

if len(policy.Spec.Scopes) == 0 {
continue
}

signedPrefix := ""
if policy.Spec.Policy.SignedIdentity.MatchPolicy == ocpconfigv1alpha1.IdentityMatchPolicyRemapIdentity &&
policy.Spec.Policy.SignedIdentity.PolicyMatchRemapIdentity != nil {
signedPrefix = string(policy.Spec.Policy.SignedIdentity.PolicyMatchRemapIdentity.SignedPrefix)
}

for _, scope := range policy.Spec.Scopes {
policyScope := normalizeImageScope(string(scope))
if policyScope == "" {
continue
}

matchesMirror := false
for _, mirrorScope := range mirrorScopes {
if clusterImagePolicyScopeMatchesMirror(policyScope, mirrorScope) {
matchesMirror = true
break
}
}
if !matchesMirror {
continue
}

matches = append(matches, fmt.Sprintf("%s (%s)", policy.Name, policyScope))
match = &SigstorePolicyInfo{
MirrorRegistry: policyScope,
CosignKeyData: base64.StdEncoding.EncodeToString(keyData),
SignedPrefix: signedPrefix,
}
}
}

if len(matches) > 1 {
sort.Strings(matches)
return nil, fmt.Errorf(
"expected exactly one ClusterImagePolicy matching mirror registries, found %d: %s",
len(matches), strings.Join(matches, ", "),
)
}

return match, nil
}

// GetMirrorRegistryCACerts retrieves CA certificates from image.config.openshift.io/cluster.
// Returns nil without error if:
// - not on OpenShift (Image CRD doesn't exist)
Expand Down
Loading
Loading