Repository: tektoncd/operator
Pull request #1096 (2022-10-10 04:20:16)

Add transformer to fix warnings for PSA
This will add tranformer to fix warnings related
to PSA for openshift components

# Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

- [ ] Run `make test lint` before submitting a PR
- [ ] Includes [tests](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if functionality changed/added)
- [ ] Includes [docs](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if user facing)
- [ ] Commit messages follow [commit message best practices](https://github.com/tektoncd/community/blob/master/standards.md#commit-messages)

_See [the contribution guide](https://github.com/tektoncd/operator/blob/master/CONTRIBUTING.md) for more details._

# Release Notes

```release-note
Add transformer to fix warnings for PSA
```


tekton-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **concaf** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @concaf` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://prow.tekton.dev/command-help?repo=tektoncd%2Foperator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/tektoncd/operator/blob/release-v0.60.x/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["concaf"]} -->
tekton-robot: The following is the coverage report on the affected files.
Say `/test pull-tekton-operator-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/reconciler/openshift/common/transformer.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/1096/pull-tekton-operator-go-coverage/1579325927014273024/artifacts/line-cov.html#file0) | 79.2% | 61.0% | -18.2

piyush-garg: /hold
piyush-garg: /hold cancel
tekton-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/tektoncd/operator/pull/1096#pullrequestreview-1135677201" title="Approved">vdemeester</a>*

The full list of commands accepted by this bot can be found [here](https://prow.tekton.dev/command-help?repo=tektoncd%2Foperator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/tektoncd/operator/blob/release-v0.60.x/OWNERS)~~ [vdemeester]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/pkg/reconciler/openshift/common/transformer.go b/pkg/reconciler/openshift/common/transformer.go
index eca614eee..a6aea416b 100644
--- a/pkg/reconciler/openshift/common/transformer.go
+++ b/pkg/reconciler/openshift/common/transformer.go
@@ -19,6 +19,8 @@ package common
 import (
 	mf "github.com/manifestival/manifestival"
 	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
+	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 )
@@ -115,3 +117,91 @@ func RemoveFsGroup(obj string) mf.Transformer {
 		return nil
 	}
 }
+
+// AddDeploymentRestrictedPSA will add the default restricted spec on Deployment to remove errors/warning
+func AddDeploymentRestrictedPSA() mf.Transformer {
+	return func(u *unstructured.Unstructured) error {
+		runAsNonRoot := true
+		allowPrivilegedEscalation := false
+
+		if u.GetKind() != "Deployment" {
+			return nil
+		}
+
+		d := &appsv1.Deployment{}
+		err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, d)
+		if err != nil {
+			return err
+		}
+
+		if d.Spec.Template.Spec.SecurityContext == nil {
+			d.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{}
+		}
+		d.Spec.Template.Spec.SecurityContext.RunAsNonRoot = &runAsNonRoot
+
+		if d.Spec.Template.Spec.SecurityContext.SeccompProfile == nil {
+			d.Spec.Template.Spec.SecurityContext.SeccompProfile = &corev1.SeccompProfile{}
+		}
+		d.Spec.Template.Spec.SecurityContext.SeccompProfile.Type = corev1.SeccompProfileTypeRuntimeDefault
+
+		for i := range d.Spec.Template.Spec.Containers {
+			c := &d.Spec.Template.Spec.Containers[i]
+			if c.SecurityContext == nil {
+				c.SecurityContext = &corev1.SecurityContext{}
+			}
+			c.SecurityContext.AllowPrivilegeEscalation = &allowPrivilegedEscalation
+			c.SecurityContext.Capabilities = &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}
+		}
+
+		unstrObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(d)
+		if err != nil {
+			return err
+		}
+		u.SetUnstructuredContent(unstrObj)
+		return nil
+	}
+}
+
+// AddJobRestrictedPSA will add the default restricted spec on Job to remove errors/warning
+func AddJobRestrictedPSA() mf.Transformer {
+	return func(u *unstructured.Unstructured) error {
+		runAsNonRoot := true
+		allowPrivilegedEscalation := false
+
+		if u.GetKind() != "Job" {
+			return nil
+		}
+
+		jb := &batchv1.Job{}
+		err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, jb)
+		if err != nil {
+			return err
+		}
+
+		if jb.Spec.Template.Spec.SecurityContext == nil {
+			jb.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{}
+		}
+		jb.Spec.Template.Spec.SecurityContext.RunAsNonRoot = &runAsNonRoot
+
+		if jb.Spec.Template.Spec.SecurityContext.SeccompProfile == nil {
+			jb.Spec.Template.Spec.SecurityContext.SeccompProfile = &corev1.SeccompProfile{}
+		}
+		jb.Spec.Template.Spec.SecurityContext.SeccompProfile.Type = corev1.SeccompProfileTypeRuntimeDefault
+
+		for i := range jb.Spec.Template.Spec.Containers {
+			c := &jb.Spec.Template.Spec.Containers[i]
+			if c.SecurityContext == nil {
+				c.SecurityContext = &corev1.SecurityContext{}
+			}
+			c.SecurityContext.AllowPrivilegeEscalation = &allowPrivilegedEscalation
+			c.SecurityContext.Capabilities = &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}
+		}
+
+		unstrObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(jb)
+		if err != nil {
+			return err
+		}
+		u.SetUnstructuredContent(unstrObj)
+		return nil
+	}
+}
diff --git a/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go b/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go
index f00dad131..10d1bbdf6 100644
--- a/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go
+++ b/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go
@@ -183,6 +183,7 @@ func (r *Reconciler) getManifest(ctx context.Context, ta *v1alpha1.TektonAddon)
 		common.AddConfiguration(ta.Spec.Config),
 		common.ApplyProxySettings,
 		occommon.ApplyCABundles,
+		occommon.AddDeploymentRestrictedPSA(),
 	}
 
 	if err := r.addonTransform(ctx, &pacManifest, ta, tfs...); err != nil {
diff --git a/pkg/reconciler/openshift/tektonchain/extension.go b/pkg/reconciler/openshift/tektonchain/extension.go
index fdd1da6e4..ba7c8ce0d 100644
--- a/pkg/reconciler/openshift/tektonchain/extension.go
+++ b/pkg/reconciler/openshift/tektonchain/extension.go
@@ -42,6 +42,7 @@ func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Tr
 	return []mf.Transformer{
 		occommon.RemoveRunAsUser(),
 		occommon.RemoveRunAsGroup(),
+		occommon.AddDeploymentRestrictedPSA(),
 		occommon.ApplyCABundles,
 	}
 }
diff --git a/pkg/reconciler/openshift/tektonhub/extension.go b/pkg/reconciler/openshift/tektonhub/extension.go
index a1a7b0adb..f98bbcffe 100644
--- a/pkg/reconciler/openshift/tektonhub/extension.go
+++ b/pkg/reconciler/openshift/tektonhub/extension.go
@@ -95,7 +95,12 @@ type openshiftExtension struct {
 }
 
 func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Transformer {
-	return []mf.Transformer{UpdateDbDeployment(), openshiftCommon.RemoveFsGroup(api)}
+	return []mf.Transformer{
+		UpdateDbDeployment(),
+		openshiftCommon.RemoveFsGroup(api),
+		openshiftCommon.AddDeploymentRestrictedPSA(),
+		openshiftCommon.AddJobRestrictedPSA(),
+	}
 }
 
 func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error {
diff --git a/pkg/reconciler/openshift/tektonpipeline/extension.go b/pkg/reconciler/openshift/tektonpipeline/extension.go
index b69063403..7d9c51998 100644
--- a/pkg/reconciler/openshift/tektonpipeline/extension.go
+++ b/pkg/reconciler/openshift/tektonpipeline/extension.go
@@ -100,6 +100,7 @@ func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Tr
 	trns := []mf.Transformer{
 		occommon.ApplyCABundles,
 		occommon.RemoveRunAsUser(),
+		occommon.AddDeploymentRestrictedPSA(),
 	}
 
 	pipeline := comp.(*v1alpha1.TektonPipeline)
diff --git a/pkg/reconciler/openshift/tektontrigger/extension.go b/pkg/reconciler/openshift/tektontrigger/extension.go
index 31e5d498a..adf00f2bd 100644
--- a/pkg/reconciler/openshift/tektontrigger/extension.go
+++ b/pkg/reconciler/openshift/tektontrigger/extension.go
@@ -42,6 +42,7 @@ func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Tr
 	return []mf.Transformer{
 		occommon.RemoveRunAsUser(),
 		occommon.RemoveRunAsGroup(),
+		occommon.AddDeploymentRestrictedPSA(),
 		occommon.ApplyCABundles,
 		replaceDeploymentArgs("-el-security-context", "false"),
 		replaceDeploymentArgs("-el-events", "enable"),


Test patch:
diff --git a/pkg/reconciler/openshift/common/testdata/test-add-psa-expected.yaml b/pkg/reconciler/openshift/common/testdata/test-add-psa-expected.yaml
new file mode 100644
index 000000000..01a289c24
--- /dev/null
+++ b/pkg/reconciler/openshift/common/testdata/test-add-psa-expected.yaml
@@ -0,0 +1,58 @@
+# Copyright © 2022 The Tekton Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: tekton-hub-api
+  labels:
+    app: tekton-hub-api
+spec:
+  selector:
+    matchLabels:
+      app: tekton-hub-api
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: tekton-hub-api
+    spec:
+      securityContext:
+        runAsNonRoot: true
+        seccompProfile:
+          type: RuntimeDefault
+      volumes:
+        - name: catalog-source
+          persistentVolumeClaim:
+            claimName: tekton-hub-api
+        - name: ssh-creds
+          secret:
+            secretName: tekton-hub-api-ssh-crds
+            optional: true
+      containers:
+        - name: tekton-hub-api
+          image: quay.io/tekton-hub/api
+          volumeMounts:
+            - name: catalog-source
+              mountPath: "/tmp/catalog"
+            - name: ssh-creds
+              mountPath: "/home/hub/.ssh"
+          ports:
+            - containerPort: 8000
+            - containerPort: 4200
+          securityContext:
+            allowPrivilegeEscalation: false
+            capabilities:
+              drop:
+                - ALL
diff --git a/pkg/reconciler/openshift/common/testdata/test-add-psa.yaml b/pkg/reconciler/openshift/common/testdata/test-add-psa.yaml
new file mode 100644
index 000000000..480f29d19
--- /dev/null
+++ b/pkg/reconciler/openshift/common/testdata/test-add-psa.yaml
@@ -0,0 +1,49 @@
+# Copyright © 2022 The Tekton Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: tekton-hub-api
+  labels:
+    app: tekton-hub-api
+spec:
+  selector:
+    matchLabels:
+      app: tekton-hub-api
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: tekton-hub-api
+    spec:
+      volumes:
+      - name: catalog-source
+        persistentVolumeClaim:
+          claimName: tekton-hub-api
+      - name: ssh-creds
+        secret:
+          secretName: tekton-hub-api-ssh-crds
+          optional: true
+      containers:
+        - name: tekton-hub-api
+          image: quay.io/tekton-hub/api
+          volumeMounts:
+          - name: catalog-source
+            mountPath: "/tmp/catalog"
+          - name: ssh-creds
+            mountPath: "/home/hub/.ssh"
+          ports:
+            - containerPort: 8000
+            - containerPort: 4200
diff --git a/pkg/reconciler/openshift/common/transformer_test.go b/pkg/reconciler/openshift/common/transformer_test.go
index f0c46fe28..12e7666f9 100644
--- a/pkg/reconciler/openshift/common/transformer_test.go
+++ b/pkg/reconciler/openshift/common/transformer_test.go
@@ -147,3 +147,32 @@ func TestRemoveFsGroup(t *testing.T) {
 		t.Errorf("failed to update deployment %s", diff.PrintWantGot(d))
 	}
 }
+
+func TestAddPSA(t *testing.T) {
+	testData := path.Join("testdata", "test-add-psa.yaml")
+	manifest, err := mf.ManifestFrom(mf.Recursive(testData))
+	assert.NilError(t, err)
+
+	newManifest, err := manifest.Transform(AddDeploymentRestrictedPSA())
+	assert.NilError(t, err)
+
+	got := &appsv1.Deployment{}
+	err = runtime.DefaultUnstructuredConverter.FromUnstructured(newManifest.Resources()[0].Object, got)
+	if err != nil {
+		t.Errorf("failed to load deployment yaml")
+	}
+
+	testData = path.Join("testdata", "test-add-psa-expected.yaml")
+	expectedManifest, err := mf.ManifestFrom(mf.Recursive(testData))
+	assert.NilError(t, err)
+
+	expected := &appsv1.Deployment{}
+	err = runtime.DefaultUnstructuredConverter.FromUnstructured(expectedManifest.Resources()[0].Object, expected)
+	if err != nil {
+		t.Errorf("failed to load deployment yaml")
+	}
+
+	if d := cmp.Diff(expected, got); d != "" {
+		t.Errorf("failed to update deployment %s", diff.PrintWantGot(d))
+	}
+}