Skip to content

Commit 7551a15

Browse files
gangwgropenshift-cherrypick-robot
authored andcommitted
Automate OCP-32383
1 parent 7aabb3c commit 7551a15

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package apiserver
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
g "github.com/onsi/ginkgo/v2"
8+
o "github.com/onsi/gomega"
9+
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
e2e "k8s.io/kubernetes/test/e2e/framework"
13+
admissionapi "k8s.io/pod-security-admission/api"
14+
15+
exutil "github.com/openshift/origin/test/extended/util"
16+
)
17+
18+
var _ = g.Describe("[sig-auth][Feature:ControlPlaneSecurity]", func() {
19+
defer g.GinkgoRecover()
20+
ctx := context.Background()
21+
oc := exutil.NewCLIWithPodSecurityLevel("control-plane-security", admissionapi.LevelPrivileged)
22+
23+
// Verifies that control plane containers have proper securityContext.privileged settings
24+
// This ensures the control plane components can perform necessary privileged operations
25+
// Related issues:
26+
// OCP-32383: Control plane security context verification
27+
//bug 1793694: Init container security context
28+
g.It("should have privileged securityContext for control plane init and main containers", func() {
29+
// Skip on MicroShift clusters
30+
isMicroShift, err := exutil.IsMicroShiftCluster(oc.AdminKubeClient())
31+
o.Expect(err).NotTo(o.HaveOccurred())
32+
if isMicroShift {
33+
g.Skip("MicroShift has different security context requirements and architecture")
34+
}
35+
36+
// Skip on Hypershift clusters (control plane pods run in management cluster)
37+
isHyperShift, err := exutil.IsHypershift(ctx, oc.AdminConfigClient())
38+
o.Expect(err).NotTo(o.HaveOccurred())
39+
if isHyperShift {
40+
g.Skip("Hypershift control plane pods are not accessible from hosted cluster")
41+
}
42+
43+
checkItems := []struct {
44+
namespace string
45+
containerName string
46+
expectedHostPath string
47+
expectHostNetwork bool
48+
requireHostPathMount bool
49+
}{
50+
{
51+
namespace: "openshift-kube-apiserver",
52+
containerName: "kube-apiserver",
53+
expectedHostPath: "/etc/kubernetes",
54+
expectHostNetwork: true,
55+
requireHostPathMount: true,
56+
},
57+
{
58+
namespace: "openshift-apiserver",
59+
containerName: "openshift-apiserver",
60+
expectedHostPath: "",
61+
expectHostNetwork: false,
62+
requireHostPathMount: false,
63+
},
64+
{
65+
namespace: "openshift-oauth-apiserver",
66+
containerName: "oauth-apiserver",
67+
expectedHostPath: "",
68+
expectHostNetwork: false,
69+
requireHostPathMount: false,
70+
},
71+
}
72+
73+
for _, checkItem := range checkItems {
74+
g.By("Getting pods in " + checkItem.namespace)
75+
e2e.Logf("Checking namespace: %s", checkItem.namespace)
76+
77+
podList, err := oc.AdminKubeClient().CoreV1().Pods(checkItem.namespace).List(ctx, metav1.ListOptions{
78+
LabelSelector: "apiserver",
79+
})
80+
o.Expect(err).NotTo(o.HaveOccurred())
81+
o.Expect(podList.Items).NotTo(o.BeEmpty(), "Expected to find at least one pod in %s", checkItem.namespace)
82+
83+
pod := podList.Items[0]
84+
e2e.Logf("Found pod: %s in namespace %s", pod.Name, checkItem.namespace)
85+
86+
g.By("Verifying container securityContext.privileged for " + checkItem.containerName)
87+
88+
// Find the specified container
89+
var targetContainer *corev1.Container
90+
for i := range pod.Spec.Containers {
91+
if pod.Spec.Containers[i].Name == checkItem.containerName {
92+
targetContainer = &pod.Spec.Containers[i]
93+
break
94+
}
95+
}
96+
o.Expect(targetContainer).NotTo(o.BeNil(), "Container %s not found in pod %s", checkItem.containerName, pod.Name)
97+
98+
// Verify the container has securityContext
99+
o.Expect(targetContainer.SecurityContext).NotTo(o.BeNil(),
100+
"Container %s in pod %s does not have securityContext", checkItem.containerName, pod.Name)
101+
102+
o.Expect(targetContainer.SecurityContext.Privileged).NotTo(o.BeNil(),
103+
"Container %s in pod %s does not have securityContext.privileged set", checkItem.containerName, pod.Name)
104+
o.Expect(*targetContainer.SecurityContext.Privileged).To(o.BeTrue(),
105+
"Container %s in pod %s should have securityContext.privileged=true", checkItem.containerName, pod.Name)
106+
e2e.Logf("Container %s has securityContext.privileged=true", checkItem.containerName)
107+
108+
var runAsUser *int64
109+
if targetContainer.SecurityContext.RunAsUser != nil {
110+
runAsUser = targetContainer.SecurityContext.RunAsUser
111+
e2e.Logf("Container %s has container-level runAsUser set", checkItem.containerName)
112+
} else if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.RunAsUser != nil {
113+
runAsUser = pod.Spec.SecurityContext.RunAsUser
114+
e2e.Logf("Container %s inherits pod-level runAsUser", checkItem.containerName)
115+
}
116+
117+
// If runAsUser is explicitly set (either at container or pod level), verify it's 0
118+
// If not set, the container runs as root by default when privileged=true
119+
if runAsUser != nil {
120+
o.Expect(*runAsUser).To(o.Equal(int64(0)),
121+
"Container %s in pod %s should have runAsUser=0 (root), got %d", checkItem.containerName, pod.Name, *runAsUser)
122+
e2e.Logf("Container %s has runAsUser=0 (root)", checkItem.containerName)
123+
} else {
124+
// When privileged=true and runAsUser is not set, container runs as root by default
125+
e2e.Logf("Container %s runs as root (privileged=true, runAsUser not explicitly set)", checkItem.containerName)
126+
}
127+
o.Expect(pod.Spec.HostNetwork).To(o.Equal(checkItem.expectHostNetwork),
128+
"Pod %s should have hostNetwork=%v", pod.Name, checkItem.expectHostNetwork)
129+
e2e.Logf("Pod %s has hostNetwork=%v", pod.Name, checkItem.expectHostNetwork)
130+
131+
// Verify critical hostPath mounts (for static pods only)
132+
// Deployment-based API servers use ConfigMaps/Secrets instead of hostPath mounts
133+
if checkItem.requireHostPathMount {
134+
foundHostPath := false
135+
for _, volMount := range targetContainer.VolumeMounts {
136+
if strings.HasPrefix(volMount.MountPath, checkItem.expectedHostPath) {
137+
foundHostPath = true
138+
e2e.Logf("✓ Container %s mounts %s at %s", checkItem.containerName, checkItem.expectedHostPath, volMount.MountPath)
139+
break
140+
}
141+
}
142+
o.Expect(foundHostPath).To(o.BeTrue(),
143+
"Container %s in pod %s should mount %s hostPath", checkItem.containerName, pod.Name, checkItem.expectedHostPath)
144+
} else {
145+
e2e.Logf("Container %s is a deployment (uses ConfigMaps/Secrets, not hostPath)", checkItem.containerName)
146+
}
147+
148+
g.By("Verifying init container securityContext.privileged")
149+
150+
// Verify all init containers have privileged=true
151+
o.Expect(pod.Spec.InitContainers).NotTo(o.BeEmpty(),
152+
"Expected to find at least one init container in pod %s", pod.Name)
153+
154+
for _, initContainer := range pod.Spec.InitContainers {
155+
o.Expect(initContainer.SecurityContext).NotTo(o.BeNil(),
156+
"Init container %s in pod %s does not have securityContext", initContainer.Name, pod.Name)
157+
o.Expect(initContainer.SecurityContext.Privileged).NotTo(o.BeNil(),
158+
"Init container %s in pod %s does not have securityContext.privileged set", initContainer.Name, pod.Name)
159+
o.Expect(*initContainer.SecurityContext.Privileged).To(o.BeTrue(),
160+
"Init container %s in pod %s should have securityContext.privileged=true", initContainer.Name, pod.Name)
161+
162+
e2e.Logf("Init container %s has securityContext.privileged=true", initContainer.Name)
163+
}
164+
}
165+
})
166+
})

0 commit comments

Comments
 (0)