Skip to content

Commit 1bc0454

Browse files
committed
Migrate OCP-59552: enable image signature verification for RHEL registries
1 parent 76ed5a8 commit 1bc0454

4 files changed

Lines changed: 206 additions & 2 deletions

File tree

test/extended/node/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ This directory contains OpenShift end-to-end tests for node-related features.
88

99
- **kubeletconfig_features.go** - Tests applying KubeletConfig to custom machine config pools, requires node reboots
1010
- **kubelet_secret_pulled_images.go** - Tests kubelet credential verification for image pulls (`KubeletEnsureSecretPulledImages` feature gate). Covers multi-tenancy isolation, credential rotation, ImagePullPolicy behavior, credential verification policy (NeverVerify/AlwaysVerify), and registry availability scenarios. Requires `TechPreviewNoUpgrade` or `CustomNoUpgrade` FeatureSet.
11-
- **node_e2e/image_registry_config.go** - Container registry config change (OCP-44820) - Verifies search registry update triggers MCO rollout and lands on nodes [Disruptive]
11+
- **node_e2e/image_registry_config.go** - Container registry config change (OCP-44820) - Verifies search registry update triggers MCO rollout and lands on nodes \[Disruptive\]
12+
- **node_e2e/image_signature.go** - Image signature verification - Verifies image signature verification for Red Hat Container Registries \[Disruptive\]\[Serial\]\[OTP\]
1213

1314
### Suite: openshift/usernamespace
1415

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package node
2+
3+
import (
4+
"context"
5+
"path/filepath"
6+
"strings"
7+
"time"
8+
9+
g "github.com/onsi/ginkgo/v2"
10+
o "github.com/onsi/gomega"
11+
ote "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo"
12+
13+
"k8s.io/apimachinery/pkg/util/wait"
14+
e2e "k8s.io/kubernetes/test/e2e/framework"
15+
16+
nodeutils "github.com/openshift/origin/test/extended/node"
17+
exutil "github.com/openshift/origin/test/extended/util"
18+
)
19+
20+
var _ = g.Describe("[Suite:openshift/disruptive-longrunning][sig-node][Disruptive][Serial] Image signature verification", func() {
21+
var (
22+
oc = exutil.NewCLIWithoutNamespace("image-sig")
23+
nodeE2EBaseDir = exutil.FixturePath("testdata", "node", "node_e2e")
24+
imgSignatureYAML = filepath.Join(nodeE2EBaseDir, "machineconfig-image-signature.yaml")
25+
)
26+
27+
// Skip all tests on MicroShift clusters as MachineConfig resources are not available
28+
g.BeforeEach(func() {
29+
isMicroShift, err := exutil.IsMicroShiftCluster(oc.AdminKubeClient())
30+
if err != nil {
31+
e2e.Logf("Error checking if cluster is MicroShift: %v", err)
32+
g.Skip("Cannot determine cluster type")
33+
}
34+
if isMicroShift {
35+
g.Skip("Skipping test on MicroShift cluster - MachineConfig resources are not available")
36+
}
37+
})
38+
39+
//author: bgudi@redhat.com
40+
g.It("[OTP] Enable image signature verification for Red Hat Container Registries [OCP-59552]", ote.Informing(), func() {
41+
ctx := context.Background()
42+
43+
g.By("Check if mcp worker exists in current cluster")
44+
machineCount, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("mcp", "worker", "-o=jsonpath={.status.machineCount}").Output()
45+
if err != nil || machineCount == "0" {
46+
g.Skip("Skipping test: mcp worker does not exist in this cluster")
47+
}
48+
e2e.Logf("Worker MCP machine count: %s", machineCount)
49+
50+
g.By("Apply a machine config to set image signature policy for worker nodes")
51+
err = oc.AsAdmin().WithoutNamespace().Run("create").Args("-f", imgSignatureYAML).Execute()
52+
o.Expect(err).NotTo(o.HaveOccurred(), "failed to create MachineConfig")
53+
54+
g.DeferCleanup(func(ctx context.Context) {
55+
g.By("Delete the MachineConfig")
56+
oc.AsAdmin().WithoutNamespace().Run("delete").Args("-f", imgSignatureYAML, "--ignore-not-found").Execute()
57+
58+
g.By("Wait for MCP to finish rolling back")
59+
err := waitForMCPUpdate(ctx, oc, "worker", 30*time.Minute)
60+
if err != nil {
61+
e2e.Logf("Warning: MCP did not finish rolling back: %v", err)
62+
}
63+
}, ctx)
64+
65+
g.By("Wait for MCP to finish updating")
66+
err = waitForMCPUpdate(ctx, oc, "worker", 30*time.Minute)
67+
o.Expect(err).NotTo(o.HaveOccurred(), "MCP worker did not finish updating")
68+
69+
g.By("Verify the signature configuration in /etc/containers/policy.json")
70+
err = checkImageSignature(oc)
71+
o.Expect(err).NotTo(o.HaveOccurred(), "image signature configuration verification failed")
72+
})
73+
})
74+
75+
// waitForMCPUpdate waits for the MachineConfigPool to finish updating.
76+
// It checks the Updated condition to become True (which means update is complete).
77+
// Returns nil when the MCP is updated, or an error if it times out.
78+
// This is a helper function and does not contain assertions.
79+
func waitForMCPUpdate(ctx context.Context, oc *exutil.CLI, mcpName string, timeout time.Duration) error {
80+
g.GinkgoHelper()
81+
return wait.PollUntilContextTimeout(ctx, 30*time.Second, timeout, false, func(ctx context.Context) (bool, error) {
82+
// Check the Updated condition instead of Updating
83+
// Updated=True means the MCP has finished updating
84+
updatedStatus, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("mcp", mcpName, "-o=jsonpath={.status.conditions[?(@.type=='Updated')].status}").Output()
85+
if err != nil {
86+
e2e.Logf("Error getting MCP Updated status: %v", err)
87+
return false, nil
88+
}
89+
90+
// Check that machine counts match (all machines have the desired config)
91+
machineCount, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("mcp", mcpName, "-o=jsonpath={.status.machineCount}").Output()
92+
if err != nil {
93+
e2e.Logf("Error getting machine count: %v", err)
94+
return false, nil
95+
}
96+
updatedMachineCount, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("mcp", mcpName, "-o=jsonpath={.status.updatedMachineCount}").Output()
97+
if err != nil {
98+
e2e.Logf("Error getting updated machine count: %v", err)
99+
return false, nil
100+
}
101+
102+
e2e.Logf("MCP %s: Updated=%s, machines=%s, updatedMachines=%s", mcpName, updatedStatus, machineCount, updatedMachineCount)
103+
104+
if strings.Contains(updatedStatus, "True") && machineCount == updatedMachineCount {
105+
e2e.Logf("MCP %s updated successfully", mcpName)
106+
return true, nil
107+
}
108+
e2e.Logf("MCP %s is still updating", mcpName)
109+
return false, nil
110+
})
111+
}
112+
113+
// checkImageSignature verifies that the image signature policy is correctly configured on worker nodes.
114+
// It checks for required entries in /etc/containers/policy.json for Red Hat registries.
115+
// This is a helper function and does not contain assertions.
116+
func checkImageSignature(oc *exutil.CLI) error {
117+
g.GinkgoHelper()
118+
return wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
119+
workerNode := nodeutils.GetFirstReadyWorkerNode(oc)
120+
policyJSON, err := nodeutils.ExecOnNodeWithChroot(oc, workerNode, "cat", "/etc/containers/policy.json")
121+
if err != nil {
122+
e2e.Logf("Error reading policy.json: %v", err)
123+
return false, nil
124+
}
125+
126+
e2e.Logf("Checking policy.json content from node %s", workerNode)
127+
128+
// Check for required entries in the policy.json
129+
requiredEntries := []string{
130+
"registry.access.redhat.com",
131+
"signedBy",
132+
"GPGKeys",
133+
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
134+
"registry.redhat.io",
135+
}
136+
137+
for _, entry := range requiredEntries {
138+
if !strings.Contains(policyJSON, entry) {
139+
e2e.Logf("Missing required entry in policy.json: %s", entry)
140+
return false, nil
141+
}
142+
}
143+
144+
e2e.Logf("Image signature policy verified successfully")
145+
return true, nil
146+
})
147+
}

test/extended/testdata/bindata.go

Lines changed: 39 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Butane; do not edit
2+
apiVersion: machineconfiguration.openshift.io/v1
3+
kind: MachineConfig
4+
metadata:
5+
labels:
6+
machineconfiguration.openshift.io/role: worker
7+
name: 51-worker-rh-registry-trust
8+
spec:
9+
config:
10+
ignition:
11+
version: 3.2.0
12+
storage:
13+
files:
14+
- contents:
15+
source: data:;base64,ewogICJkZWZhdWx0IjogWwogICAgewogICAgICAidHlwZSI6ICJpbnNlY3VyZUFjY2VwdEFueXRoaW5nIgogICAgfQogIF0sCiAgInRyYW5zcG9ydHMiOiB7CiAgICAiZG9ja2VyIjogewogICAgICAicmVnaXN0cnkuYWNjZXNzLnJlZGhhdC5jb20iOiBbCiAgICAgICAgewogICAgICAgICAgInR5cGUiOiAic2lnbmVkQnkiLAogICAgICAgICAgImtleVR5cGUiOiAiR1BHS2V5cyIsCiAgICAgICAgICAia2V5UGF0aCI6ICIvZXRjL3BraS9ycG0tZ3BnL1JQTS1HUEctS0VZLXJlZGhhdC1yZWxlYXNlIgogICAgICAgIH0KICAgICAgXSwKICAgICAgInJlZ2lzdHJ5LnJlZGhhdC5pbyI6IFsKICAgICAgICB7CiAgICAgICAgICAidHlwZSI6ICJzaWduZWRCeSIsCiAgICAgICAgICAia2V5VHlwZSI6ICJHUEdLZXlzIiwKICAgICAgICAgICJrZXlQYXRoIjogIi9ldGMvcGtpL3JwbS1ncGcvUlBNLUdQRy1LRVktcmVkaGF0LXJlbGVhc2UiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImRvY2tlci1kYWVtb24iOiB7CiAgICAgICIiOiBbCiAgICAgICAgewogICAgICAgICAgInR5cGUiOiAiaW5zZWN1cmVBY2NlcHRBbnl0aGluZyIKICAgICAgICB9CiAgICAgIF0KICAgIH0KICB9Cn0K
16+
mode: 420
17+
overwrite: true
18+
path: /etc/containers/policy.json

0 commit comments

Comments
 (0)