Skip to content

Commit 3fc728a

Browse files
Merge pull request openshift#383 from anik120/psa-legacy-catalog-enablement
OCPBUGS-122: Psa legacy catalog enablement
2 parents 7bc5831 + 2e97ba5 commit 3fc728a

File tree

16 files changed

+349
-53
lines changed

16 files changed

+349
-53
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37
1414
github.com/onsi/ginkgo/v2 v2.1.3
1515
github.com/openshift/api v0.0.0-20220525145417-ee5b62754c68
16-
github.com/operator-framework/api v0.16.0
16+
github.com/operator-framework/api v0.17.1
1717
github.com/operator-framework/operator-lifecycle-manager v0.0.0-00010101000000-000000000000
1818
github.com/operator-framework/operator-registry v1.17.5
1919
github.com/sirupsen/logrus v1.8.1

manifests/0000_50_olm_00-catalogsources.crd.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ spec:
8181
priorityClassName:
8282
description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
8383
type: string
84+
securityContextConfig:
85+
description: 'SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource''s pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission(PSA) controller''s `restricted` mode, or doesn''t set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`. If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'''
86+
type: string
87+
default: restricted
88+
enum:
89+
- legacy
90+
- restricted
8491
tolerations:
8592
description: Tolerations are the catalog source's pod's tolerations.
8693
type: array

staging/api/crds/operators.coreos.com_catalogsources.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ spec:
7979
priorityClassName:
8080
description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
8181
type: string
82+
securityContextConfig:
83+
description: 'SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource''s pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission(PSA) controller''s `restricted` mode, or doesn''t set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`. If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'''
84+
type: string
85+
default: restricted
86+
enum:
87+
- legacy
88+
- restricted
8289
tolerations:
8390
description: Tolerations are the catalog source's pod's tolerations.
8491
type: array

staging/api/crds/zz_defs.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/api/pkg/operators/v1alpha1/catalogsource_types.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package v1alpha1
33
import (
44
"encoding/json"
55
"fmt"
6+
"time"
7+
68
"github.com/sirupsen/logrus"
79
corev1 "k8s.io/api/core/v1"
810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911
"k8s.io/apimachinery/pkg/types"
10-
"time"
1112
)
1213

1314
const (
@@ -95,6 +96,13 @@ type CatalogSourceSpec struct {
9596
Icon Icon `json:"icon,omitempty"`
9697
}
9798

99+
type SecurityConfig string
100+
101+
const (
102+
Legacy SecurityConfig = "legacy"
103+
Restricted SecurityConfig = "restricted"
104+
)
105+
98106
// GrpcPodConfig contains configuration specified for a catalog source
99107
type GrpcPodConfig struct {
100108
// NodeSelector is a selector which must be true for the pod to fit on a node.
@@ -111,6 +119,19 @@ type GrpcPodConfig struct {
111119
// default.
112120
// +optional
113121
PriorityClassName *string `json:"priorityClassName,omitempty"`
122+
123+
// SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource's pod is either injected with
124+
// the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in
125+
// Pod Security Admission(PSA) controller's `restricted` mode, or doesn't set these values at all, in which case the pod
126+
// can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`.
127+
// If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a
128+
// validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig
129+
// should be set to `legacy`.
130+
// More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'
131+
// +optional
132+
// +kubebuilder:validation:Enum=legacy;restricted
133+
// +kubebuilder:default:=restricted
134+
SecurityContextConfig SecurityConfig `json:"securityContextConfig,omitempty"`
114135
}
115136

116137
// UpdateStrategy holds all the different types of catalog source update strategies

staging/operator-lifecycle-manager/deploy/chart/crds/0000_50_olm_00-catalogsources.crd.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ spec:
7979
priorityClassName:
8080
description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
8181
type: string
82+
securityContextConfig:
83+
description: 'SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource''s pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission(PSA) controller''s `restricted` mode, or doesn''t set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`. If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'''
84+
type: string
85+
default: restricted
86+
enum:
87+
- legacy
88+
- restricted
8289
tolerations:
8390
description: Tolerations are the catalog source's pod's tolerations.
8491
type: array

staging/operator-lifecycle-manager/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ require (
2424
github.com/onsi/gomega v1.18.1
2525
github.com/openshift/api v0.0.0-20220525145417-ee5b62754c68
2626
github.com/openshift/client-go v0.0.0-20220525160904-9e1acff93e4a
27-
github.com/operator-framework/api v0.16.0
27+
github.com/operator-framework/api v0.17.1
2828
github.com/operator-framework/operator-registry v1.17.5
2929
github.com/otiai10/copy v1.2.0
3030
github.com/pkg/errors v0.9.1

staging/operator-lifecycle-manager/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,8 @@ github.com/openshift/cluster-policy-controller v0.0.0-20220825134653-523e4104074
10801080
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
10811081
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
10821082
github.com/operator-framework/api v0.7.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
1083-
github.com/operator-framework/api v0.16.0 h1:swUOhVv7QDszxBTwYM8QAtyeqI4EQHNVAiKMS+xjakY=
1084-
github.com/operator-framework/api v0.16.0/go.mod h1:kk8xJahHJR3bKqrA+A+1VIrhOTmyV76k+ARv+iV+u1Q=
1083+
github.com/operator-framework/api v0.17.1 h1:J/6+Xj4IEV8C7hcirqUFwOiZAU3PbnJhWvB0/bB51c4=
1084+
github.com/operator-framework/api v0.17.1/go.mod h1:kk8xJahHJR3bKqrA+A+1VIrhOTmyV76k+ARv+iV+u1Q=
10851085
github.com/operator-framework/operator-registry v1.17.5 h1:LR8m1rFz5Gcyje8WK6iYt+gIhtzqo52zMRALdmTYHT0=
10861086
github.com/operator-framework/operator-registry v1.17.5/go.mod h1:sRQIgDMZZdUcmHltzyCnM6RUoDF+WS8Arj1BQIARDS8=
10871087
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=

staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go

+24-13
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,7 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
182182
},
183183
},
184184
SecurityContext: &corev1.SecurityContext{
185-
ReadOnlyRootFilesystem: pointer.Bool(false),
186-
AllowPrivilegeEscalation: pointer.Bool(false),
187-
Capabilities: &corev1.Capabilities{
188-
Drop: []corev1.Capability{"ALL"},
189-
},
185+
ReadOnlyRootFilesystem: pointer.Bool(false),
190186
},
191187
ImagePullPolicy: pullPolicy,
192188
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
@@ -195,19 +191,18 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
195191
NodeSelector: map[string]string{
196192
"kubernetes.io/os": "linux",
197193
},
198-
SecurityContext: &corev1.PodSecurityContext{
199-
SeccompProfile: &corev1.SeccompProfile{
200-
Type: corev1.SeccompProfileTypeRuntimeDefault,
201-
},
202-
},
203194
ServiceAccountName: saName,
204195
},
205196
}
206197

207-
if runAsUser > 0 {
208-
pod.Spec.SecurityContext.RunAsUser = &runAsUser
209-
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
198+
if source.Spec.GrpcPodConfig != nil {
199+
if source.Spec.GrpcPodConfig.SecurityContextConfig == operatorsv1alpha1.Restricted {
200+
addSecurityContext(pod, runAsUser)
201+
}
202+
} else {
203+
addSecurityContext(pod, runAsUser)
210204
}
205+
211206
// Override scheduling options if specified
212207
if source.Spec.GrpcPodConfig != nil {
213208
grpcPodConfig := source.Spec.GrpcPodConfig
@@ -256,3 +251,19 @@ func hashPodSpec(spec corev1.PodSpec) string {
256251
hashutil.DeepHashObject(hasher, &spec)
257252
return rand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
258253
}
254+
255+
func addSecurityContext(pod *corev1.Pod, runAsUser int64) {
256+
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
257+
pod.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
258+
Drop: []corev1.Capability{"ALL"},
259+
}
260+
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
261+
SeccompProfile: &corev1.SeccompProfile{
262+
Type: corev1.SeccompProfileTypeRuntimeDefault,
263+
},
264+
}
265+
if runAsUser > 0 {
266+
pod.Spec.SecurityContext.RunAsUser = &runAsUser
267+
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
268+
}
269+
}

staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go

+77-17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stretchr/testify/require"
77
corev1 "k8s.io/api/core/v1"
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/utils/pointer"
910

1011
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1112
)
@@ -80,26 +81,85 @@ func TestPullPolicy(t *testing.T) {
8081
}
8182

8283
func TestPodContainerSecurityContext(t *testing.T) {
83-
expectedReadOnlyRootFilesystem := false
84-
allowPrivilegeEscalation := false
85-
expectedContainerSecCtx := &corev1.SecurityContext{
86-
ReadOnlyRootFilesystem: &expectedReadOnlyRootFilesystem,
87-
AllowPrivilegeEscalation: &allowPrivilegeEscalation,
88-
Capabilities: &corev1.Capabilities{
89-
Drop: []corev1.Capability{"ALL"},
84+
testcases := []struct {
85+
title string
86+
inputCatsrc *v1alpha1.CatalogSource
87+
expectedSecurityContext *corev1.PodSecurityContext
88+
expectedContainerSecurityContext *corev1.SecurityContext
89+
}{
90+
{
91+
title: "NoSpecDefined/PodContainsSecurityConfigForPSARestricted",
92+
inputCatsrc: &v1alpha1.CatalogSource{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Name: "test",
95+
Namespace: "testns",
96+
},
97+
},
98+
expectedContainerSecurityContext: &corev1.SecurityContext{
99+
ReadOnlyRootFilesystem: pointer.Bool(false),
100+
AllowPrivilegeEscalation: pointer.Bool(false),
101+
Capabilities: &corev1.Capabilities{
102+
Drop: []corev1.Capability{"ALL"},
103+
},
104+
},
105+
expectedSecurityContext: &corev1.PodSecurityContext{
106+
SeccompProfile: &corev1.SeccompProfile{Type: corev1.SeccompProfileTypeRuntimeDefault},
107+
RunAsUser: pointer.Int64(workloadUserID),
108+
RunAsNonRoot: pointer.Bool(true),
109+
},
90110
},
91-
}
92-
93-
catsrc := &v1alpha1.CatalogSource{
94-
ObjectMeta: metav1.ObjectMeta{
95-
Name: "test",
96-
Namespace: "testns",
111+
{
112+
title: "SpecDefined/SecurityContextConfig:Restricted/PodContainsSecurityConfigForPSARestricted",
113+
inputCatsrc: &v1alpha1.CatalogSource{
114+
ObjectMeta: metav1.ObjectMeta{
115+
Name: "test",
116+
Namespace: "testns",
117+
},
118+
Spec: v1alpha1.CatalogSourceSpec{
119+
GrpcPodConfig: &v1alpha1.GrpcPodConfig{
120+
SecurityContextConfig: v1alpha1.Restricted,
121+
},
122+
},
123+
},
124+
expectedContainerSecurityContext: &corev1.SecurityContext{
125+
ReadOnlyRootFilesystem: pointer.Bool(false),
126+
AllowPrivilegeEscalation: pointer.Bool(false),
127+
Capabilities: &corev1.Capabilities{
128+
Drop: []corev1.Capability{"ALL"},
129+
},
130+
},
131+
expectedSecurityContext: &corev1.PodSecurityContext{
132+
SeccompProfile: &corev1.SeccompProfile{Type: corev1.SeccompProfileTypeRuntimeDefault},
133+
RunAsUser: pointer.Int64(workloadUserID),
134+
RunAsNonRoot: pointer.Bool(true),
135+
},
136+
},
137+
{
138+
title: "SpecDefined/SecurityContextConfig:Legacy/PodDoesNotContainsSecurityConfig",
139+
inputCatsrc: &v1alpha1.CatalogSource{
140+
ObjectMeta: metav1.ObjectMeta{
141+
Name: "test",
142+
Namespace: "testns",
143+
},
144+
Spec: v1alpha1.CatalogSourceSpec{
145+
GrpcPodConfig: &v1alpha1.GrpcPodConfig{
146+
SecurityContextConfig: v1alpha1.Legacy,
147+
},
148+
},
149+
},
150+
expectedContainerSecurityContext: nil,
151+
expectedSecurityContext: nil,
97152
},
98153
}
99-
100-
gotPod := Pod(catsrc, "hello", "busybox", "", map[string]string{}, map[string]string{}, int32(0), int32(0), int64(workloadUserID))
101-
gotContainerSecCtx := gotPod.Spec.Containers[0].SecurityContext
102-
require.Equal(t, expectedContainerSecCtx, gotContainerSecCtx)
154+
for _, testcase := range testcases {
155+
outputPod := Pod(testcase.inputCatsrc, "hello", "busybox", "", map[string]string{}, map[string]string{}, int32(0), int32(0), int64(workloadUserID))
156+
if testcase.expectedSecurityContext != nil {
157+
require.Equal(t, testcase.expectedSecurityContext, outputPod.Spec.SecurityContext)
158+
}
159+
if testcase.expectedContainerSecurityContext != nil {
160+
require.Equal(t, testcase.expectedContainerSecurityContext, outputPod.Spec.Containers[0].SecurityContext)
161+
}
162+
}
103163
}
104164

105165
// TestPodAvoidsConcurrentWrite is a regression test for

0 commit comments

Comments
 (0)