Skip to content

Commit f56076a

Browse files
committed
Auto-detect sigstore ClusterImagePolicy for EDPM signature verification
When a disconnected environment has a ClusterImagePolicy configured with sigstore (cosign) signature verification for a mirror registry, the openstack-operator now auto-detects it and passes the necessary ansible variables to edpm-ansible for configuring signature verification on EDPM data plane nodes. if ClusterImagePolicy CRD is not installed or no relevant policy exists, the operator continues without enabling signature verification. This maintains backward compatibility. Requires: OCP 4.20+ (sigstore GA) and oc-mirror v2. There would be a follow-up edpm-ansible patch to use these ansible vars. jira: OSPRH-28852 Change-Id: I2cbc4e83884562bd17065ee7158e00e5c9b12160 Signed-off-by: rabi <ramishra@redhat.com>
1 parent 2e0be68 commit f56076a

4 files changed

Lines changed: 149 additions & 0 deletions

File tree

cmd/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
5151
k8s_networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
5252
ocp_configv1 "github.com/openshift/api/config/v1"
53+
ocp_configv1alpha1 "github.com/openshift/api/config/v1alpha1"
5354
machineconfig "github.com/openshift/api/machineconfiguration/v1"
5455
ocp_image "github.com/openshift/api/operator/v1alpha1"
5556
routev1 "github.com/openshift/api/route/v1"
@@ -124,6 +125,7 @@ func init() {
124125
utilruntime.Must(certmgrv1.AddToScheme(scheme))
125126
utilruntime.Must(barbicanv1.AddToScheme(scheme))
126127
utilruntime.Must(ocp_configv1.AddToScheme(scheme))
128+
utilruntime.Must(ocp_configv1alpha1.Install(scheme))
127129
utilruntime.Must(ocp_image.AddToScheme(scheme))
128130
utilruntime.Must(machineconfig.AddToScheme(scheme))
129131
utilruntime.Must(k8s_networkv1.AddToScheme(scheme))

internal/dataplane/inventory.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,24 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
168168
}
169169
}
170170

171+
// Propagate sigstore verification settings from ClusterImagePolicy to EDPM.
172+
if hasMirrorRegistries {
173+
sigstorePolicy, err := util.GetSigstoreImagePolicy(ctx, helper)
174+
if err != nil {
175+
return "", fmt.Errorf("failed to get ClusterImagePolicy for sigstore verification: %w", err)
176+
} else if sigstorePolicy != nil {
177+
nodeSetGroup.Vars["edpm_container_signature_verification"] = true
178+
nodeSetGroup.Vars["edpm_container_signature_mirror_registry"] = sigstorePolicy.MirrorRegistry
179+
nodeSetGroup.Vars["edpm_container_signature_cosign_key_data"] = sigstorePolicy.CosignKeyData
180+
if sigstorePolicy.SignedPrefix != "" {
181+
nodeSetGroup.Vars["edpm_container_signature_signed_prefix"] = sigstorePolicy.SignedPrefix
182+
}
183+
} else {
184+
helper.GetLogger().Info("No sigstore ClusterImagePolicy found for mirror registries. " +
185+
"Continuing without signature verification on dataplane nodes.")
186+
}
187+
}
188+
171189
// add TLS ansible variable
172190
nodeSetGroup.Vars["edpm_tls_certs_enabled"] = instance.Spec.TLSEnabled
173191
if instance.Spec.Tags != nil {

internal/dataplane/util/image_registry.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
ocpconfigv1 "github.com/openshift/api/config/v1"
11+
ocpconfigv1alpha1 "github.com/openshift/api/config/v1alpha1"
1112
mc "github.com/openshift/api/machineconfiguration/v1"
1213
ocpicsp "github.com/openshift/api/operator/v1alpha1"
1314
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
@@ -151,6 +152,65 @@ func getMachineConfig(ctx context.Context, helper *helper.Helper) (mc.MachineCon
151152
return masterMachineConfig, nil
152153
}
153154

155+
// SigstorePolicyInfo contains the EDPM-relevant parts of a ClusterImagePolicy.
156+
type SigstorePolicyInfo struct {
157+
MirrorRegistry string
158+
CosignKeyData string
159+
SignedPrefix string
160+
}
161+
162+
// GetSigstoreImagePolicy checks if OCP has a ClusterImagePolicy configured
163+
// with sigstore signature verification for mirror registries.
164+
// Returns policy info if a relevant policy is found, nil if no policy exists.
165+
// Returns nil without error if the ClusterImagePolicy CRD is not installed.
166+
func GetSigstoreImagePolicy(ctx context.Context, helper *helper.Helper) (*SigstorePolicyInfo, error) {
167+
policyList := &ocpconfigv1alpha1.ClusterImagePolicyList{}
168+
if err := helper.GetClient().List(ctx, policyList); err != nil {
169+
if IsNoMatchError(err) {
170+
return nil, nil
171+
}
172+
return nil, err
173+
}
174+
175+
for _, policy := range policyList.Items {
176+
if policy.Name == "openshift" {
177+
continue
178+
}
179+
180+
if policy.Spec.Policy.RootOfTrust.PolicyType != ocpconfigv1alpha1.PublicKeyRootOfTrust {
181+
continue
182+
}
183+
184+
if policy.Spec.Policy.RootOfTrust.PublicKey == nil {
185+
continue
186+
}
187+
188+
keyData := policy.Spec.Policy.RootOfTrust.PublicKey.KeyData
189+
if len(keyData) == 0 {
190+
continue
191+
}
192+
193+
if len(policy.Spec.Scopes) == 0 {
194+
continue
195+
}
196+
mirrorRegistry := string(policy.Spec.Scopes[0])
197+
198+
signedPrefix := ""
199+
if policy.Spec.Policy.SignedIdentity.MatchPolicy == ocpconfigv1alpha1.IdentityMatchPolicyRemapIdentity &&
200+
policy.Spec.Policy.SignedIdentity.PolicyMatchRemapIdentity != nil {
201+
signedPrefix = string(policy.Spec.Policy.SignedIdentity.PolicyMatchRemapIdentity.SignedPrefix)
202+
}
203+
204+
return &SigstorePolicyInfo{
205+
MirrorRegistry: mirrorRegistry,
206+
CosignKeyData: base64.StdEncoding.EncodeToString(keyData),
207+
SignedPrefix: signedPrefix,
208+
}, nil
209+
}
210+
211+
return nil, nil
212+
}
213+
154214
// GetMirrorRegistryCACerts retrieves CA certificates from image.config.openshift.io/cluster.
155215
// Returns nil without error if:
156216
// - not on OpenShift (Image CRD doesn't exist)

internal/dataplane/util/image_registry_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
. "github.com/onsi/gomega" //revive:disable:dot-imports
2626

2727
ocpidms "github.com/openshift/api/config/v1"
28+
ocpconfigv1alpha1 "github.com/openshift/api/config/v1alpha1"
2829
mc "github.com/openshift/api/machineconfiguration/v1"
2930
ocpicsp "github.com/openshift/api/operator/v1alpha1"
3031
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
@@ -50,6 +51,7 @@ func setupTestHelper(includeOpenShiftCRDs bool, objects ...client.Object) *helpe
5051
if includeOpenShiftCRDs {
5152
_ = ocpicsp.AddToScheme(s)
5253
_ = ocpidms.AddToScheme(s)
54+
_ = ocpconfigv1alpha1.Install(s)
5355
_ = mc.AddToScheme(s)
5456
}
5557

@@ -544,3 +546,70 @@ func TestGetMirrorRegistryCACerts_ConfigMapNotFound(t *testing.T) {
544546
g.Expect(err).ToNot(HaveOccurred())
545547
g.Expect(caCerts).To(BeNil())
546548
}
549+
550+
func TestGetSigstoreImagePolicy_WithRemapIdentity(t *testing.T) {
551+
g := NewWithT(t)
552+
ctx := context.Background()
553+
554+
policy := &ocpconfigv1alpha1.ClusterImagePolicy{
555+
ObjectMeta: metav1.ObjectMeta{Name: "test-policy"},
556+
Spec: ocpconfigv1alpha1.ClusterImagePolicySpec{
557+
Scopes: []ocpconfigv1alpha1.ImageScope{"local-registry.example.com:5000"},
558+
Policy: ocpconfigv1alpha1.Policy{
559+
RootOfTrust: ocpconfigv1alpha1.PolicyRootOfTrust{
560+
PolicyType: ocpconfigv1alpha1.PublicKeyRootOfTrust,
561+
PublicKey: &ocpconfigv1alpha1.PublicKey{
562+
KeyData: []byte("test-public-key"),
563+
},
564+
},
565+
SignedIdentity: ocpconfigv1alpha1.PolicyIdentity{
566+
MatchPolicy: ocpconfigv1alpha1.IdentityMatchPolicyRemapIdentity,
567+
PolicyMatchRemapIdentity: &ocpconfigv1alpha1.PolicyMatchRemapIdentity{
568+
SignedPrefix: "registry.example.com/vendor",
569+
},
570+
},
571+
},
572+
},
573+
}
574+
575+
h := setupTestHelper(true, policy)
576+
577+
result, err := GetSigstoreImagePolicy(ctx, h)
578+
g.Expect(err).ToNot(HaveOccurred())
579+
g.Expect(result).ToNot(BeNil())
580+
g.Expect(result.MirrorRegistry).To(Equal("local-registry.example.com:5000"))
581+
g.Expect(result.CosignKeyData).To(Equal(base64.StdEncoding.EncodeToString([]byte("test-public-key"))))
582+
g.Expect(result.SignedPrefix).To(Equal("registry.example.com/vendor"))
583+
}
584+
585+
func TestGetSigstoreImagePolicy(t *testing.T) {
586+
g := NewWithT(t)
587+
ctx := context.Background()
588+
589+
policy := &ocpconfigv1alpha1.ClusterImagePolicy{
590+
ObjectMeta: metav1.ObjectMeta{Name: "test-policy"},
591+
Spec: ocpconfigv1alpha1.ClusterImagePolicySpec{
592+
Scopes: []ocpconfigv1alpha1.ImageScope{"local-registry.example.com:5000"},
593+
Policy: ocpconfigv1alpha1.Policy{
594+
RootOfTrust: ocpconfigv1alpha1.PolicyRootOfTrust{
595+
PolicyType: ocpconfigv1alpha1.PublicKeyRootOfTrust,
596+
PublicKey: &ocpconfigv1alpha1.PublicKey{
597+
KeyData: []byte("test-public-key"),
598+
},
599+
},
600+
SignedIdentity: ocpconfigv1alpha1.PolicyIdentity{
601+
MatchPolicy: ocpconfigv1alpha1.IdentityMatchPolicyMatchRepoDigestOrExact,
602+
},
603+
},
604+
},
605+
}
606+
607+
h := setupTestHelper(true, policy)
608+
609+
result, err := GetSigstoreImagePolicy(ctx, h)
610+
g.Expect(err).ToNot(HaveOccurred())
611+
g.Expect(result).ToNot(BeNil())
612+
g.Expect(result.MirrorRegistry).To(Equal("local-registry.example.com:5000"))
613+
g.Expect(result.CosignKeyData).To(Equal(base64.StdEncoding.EncodeToString([]byte("test-public-key"))))
614+
g.Expect(result.SignedPrefix).To(BeEmpty())
615+
}

0 commit comments

Comments
 (0)