Repository: kubernetes-sigs/security-profiles-operator
Pull request #851 (2022-03-21 14:22:30)

feat: make the selinux type configurable in the SPOD CRD
Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com>

<!--  Thanks for sending a pull request!  Here are some tips for you:

- If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
- Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
- If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
- If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?

This extends the SPOD CRD with a field which allow to configure the SELinux type tag used in the security context of SPOd deployment. This is helpful to change the default `spc_t` SELinux type to either a more restrictive type or to `unconfined_t` when SELinux is not enforced. For instance `unconfined_t` can be used with the Flatcar Linux distribution. 

See other solutions proposed in:
https://github.com/kubernetes-sigs/security-profiles-operator/pull/850
https://github.com/kubernetes-sigs/security-profiles-operator/pull/828
https://github.com/kubernetes-sigs/security-profiles-operator/pull/826

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

/kind feature

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #

or

None
-->

#### Does this PR have test?

<!--
If tests aren't applicable just write N/A.
-->

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note

Add a new field selinuxTypeTag in the SPOD CRD which allows to configure the SELinux type in the SPOd deployment

```


k8s-ci-robot: @ccojocar: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: Hi @ccojocar. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/851#" title="Author self-approved">ccojocar</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **jaormx** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

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

- **[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/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":["jaormx"]} -->
k8s-ci-robot: @ccojocar: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-security-profiles-operator-verify | 9e5b06e849c62277b1c8035fc33cfa2a371aa706 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/851/pull-security-profiles-operator-verify/1505918806113914880) | true | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=851). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Accojocar). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
codecov-commenter: # [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/851?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#851](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/851?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (9e5b06e) into [main](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/commit/14f1ee09a8840de3c86b19593fc4707fba7cf268?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (14f1ee0) will **decrease** coverage by `0.08%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head 9e5b06e differs from pull request most recent head dc271ec. Consider uploading reports for the commit dc271ec to get more accurate results


```diff
@@            Coverage Diff             @@
##             main     #851      +/-   ##
==========================================
- Coverage   52.60%   52.51%   -0.09%     
==========================================
  Files          42       42              
  Lines        4268     4275       +7     
==========================================
  Hits         2245     2245              
- Misses       1952     1959       +7     
  Partials       71       71              
```

ccojocar: @saschagrunert @JAORMX I fixed all review comments and the tests passed. Please could you have another look? Thanks

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index c81c329e7..87e3fe63d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -102,7 +102,10 @@ linters-settings:
   nestif:
     min-complexity: 15
   gci:
-    local-prefixes: sigs.k8s.io/security-profiles-operator
+    sections:
+      - standard
+      - default
+      - prefix(sigs.k8s.io/security-profiles-operator)
   errcheck:
     check-type-assertions: true
     check-blank: true
@@ -116,31 +119,57 @@ linters-settings:
       # Diagnostic
       - appendAssign
       - argOrder
+      - badCall
       - badCond
+      - badLock
+      - badRegexp
+      - badSorting
+      - builtinShadowDecl
       - caseOrder
       - codegenComment
       - commentedOutCode
+      - deferInLoop
       - deprecatedComment
       - dupArg
       - dupBranchBody
       - dupCase
       - dupSubExpr
+      - dynamicFmtString
+      - emptyDecl
+      - evalOrder
       - exitAfterDefer
+      - externalErrorReassign
+      - filepathJoin
       - flagDeref
       - flagName
+      - mapKey
       - nilValReturn
       - offBy1
+      - regexpPattern
+      - returnAfterHttpError
       - sloppyReassign
+      - sloppyTypeAssert
+      - sortSlice
+      - sprintfQuotedString
+      - sqlQuery
+      - syncMapLoadAndDelete
+      - truncateCmp
+      - unnecessaryDefer
       - weakCond
-      - octalLiteral
 
       # Performance
       - appendCombine
       - equalFold
       - hugeParam
       - indexAlloc
+      - preferDecodeRune
+      - preferFprint
+      - preferStringWriter
+      - preferWriteByte
       - rangeExprCopy
       - rangeValCopy
+      - sliceClear
+      - stringXbytes
 
       # Style
       - assignOp
@@ -149,25 +178,40 @@ linters-settings:
       - commentFormatting
       - commentedOutImport
       - defaultCaseOrder
+      - deferUnlambda
       - docStub
+      - dupImport
       - elseif
       - emptyFallthrough
       - emptyStringTest
+      - exposedSyncMutex
       - hexLiteral
+      - httpNoBody
       - ifElseChain
+      - ioutilDeprecated
       - methodExprCall
+      - newDeref
+      - octalLiteral
+      - preferFilepathJoin
+      - redundantSprint
       - regexpMust
+      - regexpSimplify
+      - ruleguard
       - singleCaseSwitch
       - sloppyLen
-      - stringXbytes
+      - stringConcatSimplify
       - switchTrue
+      - timeExprSimplify
+      - tooManyResultsChecker
       - typeAssertChain
+      - typeDefFirst
       - typeSwitchVar
       - underef
       - unlabelStmt
       - unlambda
       - unslice
       - valSwap
+      - whyNoLint
       - wrapperFunc
       - yodaStyleExpr
 
diff --git a/api/apparmorprofile/v1alpha1/apparmorprofile_types.go b/api/apparmorprofile/v1alpha1/apparmorprofile_types.go
index 0dd9ff45e..247427937 100644
--- a/api/apparmorprofile/v1alpha1/apparmorprofile_types.go
+++ b/api/apparmorprofile/v1alpha1/apparmorprofile_types.go
@@ -73,7 +73,7 @@ type AppArmorProfileList struct {
 	Items           []AppArmorProfile `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init the scheme
 	SchemeBuilder.Register(&AppArmorProfile{}, &AppArmorProfileList{})
 }
 
diff --git a/api/profilebinding/v1alpha1/profilebinding_types.go b/api/profilebinding/v1alpha1/profilebinding_types.go
index 1c1a98cdc..8e555e657 100644
--- a/api/profilebinding/v1alpha1/profilebinding_types.go
+++ b/api/profilebinding/v1alpha1/profilebinding_types.go
@@ -69,6 +69,6 @@ type ProfileBindingList struct {
 	Items           []ProfileBinding `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to register the scheme
 	SchemeBuilder.Register(&ProfileBinding{}, &ProfileBindingList{})
 }
diff --git a/api/profilerecording/v1alpha1/profilerecording_types.go b/api/profilerecording/v1alpha1/profilerecording_types.go
index cebf8dca0..5aa1803e1 100644
--- a/api/profilerecording/v1alpha1/profilerecording_types.go
+++ b/api/profilerecording/v1alpha1/profilerecording_types.go
@@ -183,6 +183,6 @@ type ProfileRecordingList struct {
 	Items           []ProfileRecording `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init the scheme
 	SchemeBuilder.Register(&ProfileRecording{}, &ProfileRecordingList{})
 }
diff --git a/api/seccompprofile/v1beta1/seccompprofile_types.go b/api/seccompprofile/v1beta1/seccompprofile_types.go
index dc3ad2020..87697c5cc 100644
--- a/api/seccompprofile/v1beta1/seccompprofile_types.go
+++ b/api/seccompprofile/v1beta1/seccompprofile_types.go
@@ -43,7 +43,7 @@ type SeccompProfileSpec struct {
 	// Properties from containers/common/pkg/seccomp.Seccomp type
 
 	// the default action for seccomp
-	//nolint:lll
+	//nolint:lll // required for kubebuilder
 	// +kubebuilder:validation:Enum=SCMP_ACT_KILL;SCMP_ACT_KILL_PROCESS;SCMP_ACT_KILL_THREAD;SCMP_ACT_TRAP;SCMP_ACT_ERRNO;SCMP_ACT_TRACE;SCMP_ACT_ALLOW;SCMP_ACT_LOG;SCMP_ACT_NOTIFY
 	DefaultAction seccomp.Action `json:"defaultAction"`
 	// the architecture used for system calls
@@ -64,7 +64,7 @@ type SeccompProfileSpec struct {
 	Flags []*Flag `json:"flags,omitempty"`
 }
 
-//nolint:lll
+//nolint:lll // required for kubebuilder
 // +kubebuilder:validation:Enum=SCMP_ARCH_NATIVE;SCMP_ARCH_X86;SCMP_ARCH_X86_64;SCMP_ARCH_X32;SCMP_ARCH_ARM;SCMP_ARCH_AARCH64;SCMP_ARCH_MIPS;SCMP_ARCH_MIPS64;SCMP_ARCH_MIPS64N32;SCMP_ARCH_MIPSEL;SCMP_ARCH_MIPSEL64;SCMP_ARCH_MIPSEL64N32;SCMP_ARCH_PPC;SCMP_ARCH_PPC64;SCMP_ARCH_PPC64LE;SCMP_ARCH_S390;SCMP_ARCH_S390X;SCMP_ARCH_PARISC;SCMP_ARCH_PARISC64;SCMP_ARCH_RISCV64
 type Arch string
 
@@ -76,7 +76,7 @@ type Syscall struct {
 	// the names of the syscalls
 	Names []string `json:"names"`
 	// the action for seccomp rules
-	//nolint:lll
+	//nolint:lll // required for kubebuilder
 	// +kubebuilder:validation:Enum=SCMP_ACT_KILL;SCMP_ACT_KILL_PROCESS;SCMP_ACT_KILL_THREAD;SCMP_ACT_TRAP;SCMP_ACT_ERRNO;SCMP_ACT_TRACE;SCMP_ACT_ALLOW;SCMP_ACT_LOG;SCMP_ACT_NOTIFY
 	Action seccomp.Action `json:"action"`
 	// the errno return code to use. Some actions like SCMP_ACT_ERRNO and
@@ -99,7 +99,7 @@ type Arg struct {
 	// +kubebuilder:validation:Minimum=0
 	ValueTwo uint64 `json:"valueTwo,omitempty"`
 	// the operator for syscall arguments in seccomp
-	//nolint:lll
+	//nolint:lll // required for kubebuilder
 	// +kubebuilder:validation:Enum=SCMP_CMP_NE;SCMP_CMP_LT;SCMP_CMP_LE;SCMP_CMP_EQ;SCMP_CMP_GE;SCMP_CMP_GT;SCMP_CMP_MASKED_EQ
 	Op seccomp.Operator `json:"op"`
 }
@@ -165,6 +165,6 @@ type SeccompProfileList struct {
 	Items           []SeccompProfile `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init scheme
 	SchemeBuilder.Register(&SeccompProfile{}, &SeccompProfileList{})
 }
diff --git a/api/secprofnodestatus/v1alpha1/secprofnodestatus_types.go b/api/secprofnodestatus/v1alpha1/secprofnodestatus_types.go
index 99b91cd20..19a5515ae 100644
--- a/api/secprofnodestatus/v1alpha1/secprofnodestatus_types.go
+++ b/api/secprofnodestatus/v1alpha1/secprofnodestatus_types.go
@@ -97,6 +97,6 @@ type SecurityProfileNodeStatusList struct {
 	Items           []SecurityProfileNodeStatus `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init the scheme
 	SchemeBuilder.Register(&SecurityProfileNodeStatus{}, &SecurityProfileNodeStatusList{})
 }
diff --git a/api/selinuxprofile/v1alpha2/rawselinuxprofile_types.go b/api/selinuxprofile/v1alpha2/rawselinuxprofile_types.go
index c5c35f98c..bfe066798 100644
--- a/api/selinuxprofile/v1alpha2/rawselinuxprofile_types.go
+++ b/api/selinuxprofile/v1alpha2/rawselinuxprofile_types.go
@@ -78,6 +78,6 @@ type RawSelinuxProfileList struct {
 	Items           []SelinuxProfile `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init the scheme
 	SchemeBuilder.Register(&RawSelinuxProfile{}, &RawSelinuxProfileList{})
 }
diff --git a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
index a08382737..7a39c4616 100644
--- a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
+++ b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
@@ -125,6 +125,6 @@ type SelinuxProfileList struct {
 	Items           []SelinuxProfile `json:"items"`
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init scheme
 	SchemeBuilder.Register(&SelinuxProfile{}, &SelinuxProfileList{})
 }
diff --git a/api/spod/v1alpha1/spod_types.go b/api/spod/v1alpha1/spod_types.go
index ac10db8cc..a501e24f8 100644
--- a/api/spod/v1alpha1/spod_types.go
+++ b/api/spod/v1alpha1/spod_types.go
@@ -43,6 +43,10 @@ type SPODSpec struct {
 	// tells the operator whether or not to enable SELinux support for this
 	// SPOD instance.
 	EnableSelinux *bool `json:"enableSelinux,omitempty"`
+	// If specified, the SELinux type tag applied to the security context of SPOD.
+	// +optional
+	// +kubebuilder:default="spc_t"
+	SelinuxTypeTag string `json:"selinuxTypeTag,omitempty"`
 	// tells the operator whether or not to enable log enrichment support for this
 	// SPOD instance.
 	EnableLogEnricher bool `json:"enableLogEnricher,omitempty"`
@@ -142,6 +146,6 @@ func (s *SPODStatus) StateRunning() {
 	s.ConditionedStatus.SetConditions(rcommonv1.Available())
 }
 
-func init() { //nolint:gochecknoinits
+func init() { // nolint:gochecknoinits // required to init the scheme
 	SchemeBuilder.Register(&SecurityProfilesOperatorDaemon{}, &SecurityProfilesOperatorDaemonList{})
 }
diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go
index 67da7878f..35430bbe7 100644
--- a/cmd/security-profiles-operator/main.go
+++ b/cmd/security-profiles-operator/main.go
@@ -21,7 +21,7 @@ import (
 	"flag"
 	"fmt"
 	"net/http"
-	_ "net/http/pprof" // nolint: gosec
+	_ "net/http/pprof" // nolint:gosec // required for profiling
 	"os"
 	"strings"
 	"time"
diff --git a/deploy/base/crds/securityprofilesoperatordaemon.yaml b/deploy/base/crds/securityprofilesoperatordaemon.yaml
index 6eeb2162d..17cb0e83a 100644
--- a/deploy/base/crds/securityprofilesoperatordaemon.yaml
+++ b/deploy/base/crds/securityprofilesoperatordaemon.yaml
@@ -82,6 +82,11 @@ spec:
                       type: string
                     type: array
                 type: object
+              selinuxTypeTag:
+                default: spc_t
+                description: If specified, the SELinux type tag applied to the security
+                  context of SPOD.
+                type: string
               tolerations:
                 description: If specified, the SPOD's tolerations.
                 items:
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index c5e2008f7..cf5fea966 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -621,6 +621,11 @@ spec:
                       type: string
                     type: array
                 type: object
+              selinuxTypeTag:
+                default: spc_t
+                description: If specified, the SELinux type tag applied to the security
+                  context of SPOD.
+                type: string
               tolerations:
                 description: If specified, the SPOD's tolerations.
                 items:
diff --git a/deploy/openshift-dev.yaml b/deploy/openshift-dev.yaml
index 17e4fb5d6..ada3034c9 100644
--- a/deploy/openshift-dev.yaml
+++ b/deploy/openshift-dev.yaml
@@ -621,6 +621,11 @@ spec:
                       type: string
                     type: array
                 type: object
+              selinuxTypeTag:
+                default: spc_t
+                description: If specified, the SELinux type tag applied to the security
+                  context of SPOD.
+                type: string
               tolerations:
                 description: If specified, the SPOD's tolerations.
                 items:
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index f78e76cfe..92f0c7bb3 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -621,6 +621,11 @@ spec:
                       type: string
                     type: array
                 type: object
+              selinuxTypeTag:
+                default: spc_t
+                description: If specified, the SELinux type tag applied to the security
+                  context of SPOD.
+                type: string
               tolerations:
                 description: If specified, the SPOD's tolerations.
                 items:
diff --git a/installation-usage.md b/installation-usage.md
index f66a7276d..b3ba25112 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -1,11 +1,11 @@
 # Installation and Usage
 
 <!-- toc -->
-
 - [Features](#features)
 - [Tutorials and Demos](#tutorials-and-demos)
 - [Install operator](#install-operator)
 - [Set logging verbosity](#set-logging-verbosity)
+- [Configure the SELinux type](#configure-the-selinux-type)
 - [Create Profile](#create-profile)
   - [Apply profile to pod](#apply-profile-to-pod)
   - [Base syscalls for a container runtime](#base-syscalls-for-a-container-runtime)
@@ -80,6 +80,33 @@ The daemon should now indicate that it's using the new logging verbosity:
 I1111 15:13:16.942837       1 main.go:182]  "msg"="Set logging verbosity to 1"
 ```
 
+## Configure the SELinux type
+
+The operator uses by default the `spc_t` SELinux type in the security context of the daemon pod. This can be easily 
+changed to a different SELinux type by patching the spod config as follows:
+
+```
+> kubectl -n security-profiles-operator patch spod spod --type=merge -p '{"spec":{"selinuxTypeTag":"unconfined_t"}}'
+securityprofilesoperatordaemon.security-profiles-operator.x-k8s.io/spod patched
+```
+
+The `ds/spod` should now be updated by the manager with the new SELinux type, and all daemon pods recreated:
+
+```
+ kubectl get ds spod -o yaml | grep unconfined_t -B2
+          runAsUser: 65535
+          seLinuxOptions:
+            type: unconfined_t
+--
+          runAsUser: 0
+          seLinuxOptions:
+            type: unconfined_t
+--
+          runAsUser: 0
+          seLinuxOptions:
+            type: unconfined_t
+```
+
 ## Create Profile
 
 Use the `SeccompProfile` kind to create profiles. Example:
diff --git a/internal/pkg/daemon/apparmorprofile/apparmor_supported.go b/internal/pkg/daemon/apparmorprofile/apparmor_supported.go
index 260aa6bd2..389cb7b3b 100644
--- a/internal/pkg/daemon/apparmorprofile/apparmor_supported.go
+++ b/internal/pkg/daemon/apparmorprofile/apparmor_supported.go
@@ -85,7 +85,7 @@ func loadProfile(logger logr.Logger, name, content string) (bool, error) {
 	err := mount.Do(func() error {
 		// TODO: force profile content's to align with profile's name
 		path := filepath.Join(targetProfileDir, name)
-		if err := os.WriteFile(path, []byte(content), 0o644); err != nil { //nolint
+		if err := os.WriteFile(path, []byte(content), 0o644); err != nil { // nolint // file permissions are fine
 			return err
 		}
 
diff --git a/internal/pkg/daemon/apparmorprofile/apparmorprofile.go b/internal/pkg/daemon/apparmorprofile/apparmorprofile.go
index 656f36685..4895562b4 100644
--- a/internal/pkg/daemon/apparmorprofile/apparmorprofile.go
+++ b/internal/pkg/daemon/apparmorprofile/apparmorprofile.go
@@ -94,7 +94,7 @@ func (r *Reconciler) Healthz(*http.Request) error {
 }
 
 // Security Profiles Operator RBAC permissions to manage AppArmorProfile
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=apparmorprofiles,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=apparmorprofiles/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=apparmorprofiles/finalizers,verbs=delete;get;update;patch
@@ -105,7 +105,7 @@ func (r *Reconciler) Healthz(*http.Request) error {
 // +kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch
 
 // OpenShift ... This is ignored in other distros
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security.openshift.io,namespace="security-profiles-operator",resources=securitycontextconstraints,verbs=use
 
 // Reconcile reconciles a AppArmorProfile.
diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
index 48ec167d4..0e293883a 100644
--- a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
+++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
@@ -110,7 +110,7 @@ func (b *BpfRecorder) Run() error {
 			return errors.Wrap(err, "set cache timeout")
 		}
 		cache.SetCacheSizeLimit(maxCacheItems)
-		defer cache.Close()
+		defer cache.Close() // nolint:gocritic // this is intentional
 	}
 
 	b.nodeName = b.Getenv(config.NodeNameEnvKey)
@@ -159,7 +159,11 @@ func (b *BpfRecorder) Run() error {
 		defer cancel()
 	}
 	if conn != nil {
-		defer b.CloseGRPC(conn) // nolint: errcheck
+		defer func() {
+			if err := b.CloseGRPC(conn); err != nil {
+				b.logger.Error(err, "unable to close GRPC connection")
+			}
+		}()
 	}
 
 	b.systemMountNamespace, err = b.findSystemMountNamespace()
diff --git a/internal/pkg/daemon/bpfrecorder/generate/main.go b/internal/pkg/daemon/bpfrecorder/generate/main.go
index 0a06e3f25..c41c86705 100644
--- a/internal/pkg/daemon/bpfrecorder/generate/main.go
+++ b/internal/pkg/daemon/bpfrecorder/generate/main.go
@@ -21,7 +21,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/fs"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -83,8 +82,9 @@ func run() error {
 		return errors.Wrap(err, "generate btf")
 	}
 
-	// nolint: gosec, gomnd
-	if err := ioutil.WriteFile(
+	// nolint:gosec // permissions are fine
+	if err := os.WriteFile(
+		// nolint:gomnd // filemode is fine
 		generatedGo, []byte(builder.String()), 0o644,
 	); err != nil {
 		return errors.Wrap(err, "writing generated object")
@@ -102,7 +102,7 @@ func generateBpfObj(builder *strings.Builder) error {
 	for _, arch := range []string{"amd64", "arm64"} {
 		builder.WriteString(fmt.Sprintf("%q: {\n", arch))
 
-		file, err := ioutil.ReadFile(filepath.Join(buildDir, bpfObj+"."+arch))
+		file, err := os.ReadFile(filepath.Join(buildDir, bpfObj+"."+arch))
 		if err != nil {
 			return errors.Wrap(err, "read bpf object path")
 		}
@@ -152,7 +152,7 @@ func generateBtf(builder *strings.Builder) error {
 			return errors.Errorf("invalid btf path: %s (len = %d)", path, len(pathSplit))
 		}
 
-		btfBytes, err := ioutil.ReadFile(path)
+		btfBytes, err := os.ReadFile(path)
 		if err != nil {
 			return errors.Wrap(err, "read btf file")
 		}
@@ -168,15 +168,15 @@ func generateBtf(builder *strings.Builder) error {
 		}
 		if _, ok := btfs[os][osVersion]; !ok {
 			btfs[os][osVersion] = map[types.Arch]map[types.Kernel][]byte{}
-			fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 2), osVersion) // nolint: gomnd
+			fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 2), osVersion) // nolint:gomnd // indent
 		}
 		if _, ok := btfs[os][osVersion][arch]; !ok {
 			btfs[os][osVersion][arch] = map[types.Kernel][]byte{}
-			fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 4), arch) // nolint: gomnd
+			fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 4), arch) // nolint:gomnd // indent
 		}
 
 		btfs[os][osVersion][arch][kernel] = btfBytes
-		fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 6), kernel) // nolint: gomnd
+		fmt.Fprintf(docs, "%s- %s\n", strings.Repeat(" ", 6), kernel) // nolint:gomnd // indent
 		kernels++
 
 		return nil
@@ -192,7 +192,7 @@ func generateBtf(builder *strings.Builder) error {
 
 	fmt.Fprintf(docs, "\nSum: %d\n", kernels)
 	if err := os.WriteFile(
-		docsFile, docs.Bytes(), fs.FileMode(0o644), // nolint: gomnd
+		docsFile, docs.Bytes(), fs.FileMode(0o644), // nolint:gomnd // filemode is fine
 	); err != nil {
 		return errors.Wrap(err, "write docs file")
 	}
diff --git a/internal/pkg/daemon/bpfrecorder/impl.go b/internal/pkg/daemon/bpfrecorder/impl.go
index 621ddd3fc..45df062f6 100644
--- a/internal/pkg/daemon/bpfrecorder/impl.go
+++ b/internal/pkg/daemon/bpfrecorder/impl.go
@@ -22,7 +22,6 @@ package bpfrecorder
 import (
 	"context"
 	"encoding/json"
-	"io/ioutil"
 	"net"
 	"os"
 	"runtime"
@@ -155,7 +154,7 @@ func (d *defaultImpl) Uname(buf *syscall.Utsname) error {
 }
 
 func (d *defaultImpl) TempFile(dir, pattern string) (*os.File, error) {
-	return ioutil.TempFile(dir, pattern)
+	return os.CreateTemp(dir, pattern)
 }
 
 func (d *defaultImpl) Write(file *os.File, b []byte) (n int, err error) {
diff --git a/internal/pkg/daemon/enricher/enricher.go b/internal/pkg/daemon/enricher/enricher.go
index 31e947bc8..7956629c6 100644
--- a/internal/pkg/daemon/enricher/enricher.go
+++ b/internal/pkg/daemon/enricher/enricher.go
@@ -89,7 +89,7 @@ func (e *Enricher) Run() error {
 			return errors.Wrap(err, "set cache timeout")
 		}
 		cache.SetCacheSizeLimit(maxCacheItems)
-		defer cache.Close()
+		defer cache.Close() // nolint:gocritic // this is intentional
 	}
 
 	// For the audit line cache we don't want to increase the TTL on Gets
diff --git a/internal/pkg/daemon/profilerecorder/impl.go b/internal/pkg/daemon/profilerecorder/impl.go
index e9d3a20dc..8f2285d03 100644
--- a/internal/pkg/daemon/profilerecorder/impl.go
+++ b/internal/pkg/daemon/profilerecorder/impl.go
@@ -18,7 +18,7 @@ package profilerecorder
 
 import (
 	"context"
-	"io/ioutil"
+	"os"
 
 	"github.com/containers/common/pkg/seccomp"
 	"github.com/crossplane/crossplane-runtime/pkg/resource"
@@ -174,7 +174,7 @@ func (*defaultImpl) GoArchToSeccompArch(goArch string) (seccomp.Arch, error) {
 }
 
 func (*defaultImpl) ReadFile(filename string) ([]byte, error) {
-	return ioutil.ReadFile(filename)
+	return os.ReadFile(filename)
 }
 
 func (*defaultImpl) Syscalls(
diff --git a/internal/pkg/daemon/profilerecorder/profilerecorder.go b/internal/pkg/daemon/profilerecorder/profilerecorder.go
index bab99926f..a13147566 100644
--- a/internal/pkg/daemon/profilerecorder/profilerecorder.go
+++ b/internal/pkg/daemon/profilerecorder/profilerecorder.go
@@ -105,7 +105,7 @@ func (r *RecorderReconciler) SchemeBuilder() *scheme.Builder {
 	return profilerecording1alpha1.SchemeBuilder
 }
 
-// nolint: lll
+// nolint:lll // required for kubebuilder
 //
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilesoperatordaemons,verbs=get;list;watch
 // +kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch
@@ -252,7 +252,7 @@ func (r *RecorderReconciler) Reconcile(_ context.Context, req reconcile.Request)
 
 		var profiles []profileToCollect
 		var recorder profilerecording1alpha1.ProfileRecorder
-		if len(hookProfiles) > 0 { // nolint: gocritic
+		if len(hookProfiles) > 0 { // nolint:gocritic
 			profiles = hookProfiles
 			recorder = profilerecording1alpha1.ProfileRecorderHook
 		} else if len(logProfiles) > 0 {
@@ -769,7 +769,7 @@ func parseLogAnnotations(annotations map[string]string) (res []profileToCollect,
 	for key, profile := range annotations {
 		var collectProfile profileToCollect
 
-		// nolint: gocritic
+		// nolint:gocritic
 		if strings.HasPrefix(key, config.SeccompProfileRecordLogsAnnotationKey) {
 			collectProfile.kind = profilerecording1alpha1.ProfileRecordingKindSeccompProfile
 		} else if strings.HasPrefix(key, config.SelinuxProfileRecordLogsAnnotationKey) {
diff --git a/internal/pkg/daemon/seccompprofile/seccompprofile.go b/internal/pkg/daemon/seccompprofile/seccompprofile.go
index 31e3b6fe2..36baeff38 100644
--- a/internal/pkg/daemon/seccompprofile/seccompprofile.go
+++ b/internal/pkg/daemon/seccompprofile/seccompprofile.go
@@ -21,7 +21,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"os"
 	"path"
@@ -140,7 +139,7 @@ func (r *Reconciler) checkSeccomp() error {
 }
 
 // Security Profiles Operator RBAC permissions to manage SeccompProfile
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles/finalizers,verbs=delete;get;update;patch
@@ -151,7 +150,7 @@ func (r *Reconciler) checkSeccomp() error {
 // +kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch
 
 // OpenShift ... This is ignored in other distros
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security.openshift.io,namespace="security-profiles-operator",resources=securitycontextconstraints,verbs=use
 
 // Reconcile reconciles a SeccompProfile.
@@ -398,12 +397,12 @@ func saveProfileOnDisk(fileName string, content []byte) (updated bool, err error
 		return false, errors.Wrap(err, errCreatingOperatorDir)
 	}
 
-	existingContent, err := ioutil.ReadFile(fileName)
+	existingContent, err := os.ReadFile(fileName)
 	if err == nil && bytes.Equal(existingContent, content) {
 		return false, nil
 	}
 
-	if err := ioutil.WriteFile(fileName, content, filePermissionMode); err != nil {
+	if err := os.WriteFile(fileName, content, filePermissionMode); err != nil {
 		return false, errors.Wrap(err, errSavingProfile)
 	}
 
diff --git a/internal/pkg/daemon/selinuxprofile/common_controller.go b/internal/pkg/daemon/selinuxprofile/common_controller.go
index bafe52ba9..4de93fa8b 100644
--- a/internal/pkg/daemon/selinuxprofile/common_controller.go
+++ b/internal/pkg/daemon/selinuxprofile/common_controller.go
@@ -21,7 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net"
 	"net/http"
 	"os"
@@ -136,7 +136,7 @@ func (r *ReconcileSelinux) Healthz(*http.Request) error {
 	return nil
 }
 
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles/finalizers,verbs=delete;get;update;patch
@@ -449,7 +449,7 @@ func selinuxdGetRequest(url string) (*http.Response, error) {
 		},
 	}
 
-	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create a request to selinuxd")
 	}
@@ -468,13 +468,13 @@ func writeFileIfDiffers(filePath string, contents []byte) error {
 	file, err := os.OpenFile(filePath, os.O_RDONLY, filePermissions)
 	if os.IsNotExist(err) {
 		file.Close()
-		return ioutil.WriteFile(filePath, contents, filePermissions)
+		return os.WriteFile(filePath, contents, filePermissions)
 	} else if err != nil {
 		return errors.Wrap(err, "could not open for reading"+filePath)
 	}
 	defer file.Close()
 
-	existing, err := ioutil.ReadAll(file)
+	existing, err := io.ReadAll(file)
 	if err != nil {
 		return errors.Wrap(err, "reading file "+filePath)
 	}
@@ -483,5 +483,5 @@ func writeFileIfDiffers(filePath string, contents []byte) error {
 		return nil
 	}
 
-	return ioutil.WriteFile(filePath, contents, filePermissions)
+	return os.WriteFile(filePath, contents, filePermissions)
 }
diff --git a/internal/pkg/daemon/selinuxprofile/rawselinuxprofile.go b/internal/pkg/daemon/selinuxprofile/rawselinuxprofile.go
index 0181c50f4..4861f96a0 100644
--- a/internal/pkg/daemon/selinuxprofile/rawselinuxprofile.go
+++ b/internal/pkg/daemon/selinuxprofile/rawselinuxprofile.go
@@ -109,8 +109,7 @@ func newRawSelinuxProfileHandler(
 	key types.NamespacedName,
 ) (SelinuxObjectHandler, error) {
 	// Create template to wrap policies
-	// We ignore the error as the wrapper is static
-	// nolint:error
+	// nolint:error // We ignore the error as the wrapper is static
 	tmpl, tmplerr := template.New("profileWrapper").Parse(profileWrapper)
 	if tmplerr != nil {
 		return nil, tmplerr
@@ -119,5 +118,6 @@ func newRawSelinuxProfileHandler(
 		rsp:            &selxv1alpha2.RawSelinuxProfile{},
 		policyTemplate: tmpl,
 	}
-	return oh, oh.Init(ctx, cli, key)
+	err := oh.Init(ctx, cli, key)
+	return oh, err
 }
diff --git a/internal/pkg/daemon/selinuxprofile/selinuxprofile.go b/internal/pkg/daemon/selinuxprofile/selinuxprofile.go
index 3b5018761..d2c131ed1 100644
--- a/internal/pkg/daemon/selinuxprofile/selinuxprofile.go
+++ b/internal/pkg/daemon/selinuxprofile/selinuxprofile.go
@@ -222,5 +222,6 @@ func newSelinuxProfileHandler(
 		objInherits:    make([]selxv1alpha2.SelinuxProfileObject, 0),
 	}
 
-	return oh, oh.Init(ctx, cli, key)
+	err := oh.Init(ctx, cli, key)
+	return oh, err
 }
diff --git a/internal/pkg/manager/nodestatus/nodestatus.go b/internal/pkg/manager/nodestatus/nodestatus.go
index 285947e2e..5303fb63b 100644
--- a/internal/pkg/manager/nodestatus/nodestatus.go
+++ b/internal/pkg/manager/nodestatus/nodestatus.go
@@ -89,7 +89,7 @@ func NewStatusReconciler(cli client.Client, log logr.Logger, record event.Record
 }
 
 // Security Profiles Operator RBAC permissions to manage SelinuxProfile
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles/finalizers,verbs=delete;get;update;patch
@@ -98,13 +98,13 @@ func NewStatusReconciler(cli client.Client, log logr.Logger, record event.Record
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=rawselinuxprofiles/finalizers,verbs=delete;get;update;patch
 
 // Security Profiles Operator RBAC permissions to manage SeccompProfile
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles/finalizers,verbs=delete;get;update;patch
 
 // Security Profiles Operator RBAC permissions to manage Node Statuses
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilenodestatuses,verbs=get;list;watch
 
 // Reconcile reconciles a NodeStatus.
diff --git a/internal/pkg/manager/spod/bindata/spod.go b/internal/pkg/manager/spod/bindata/spod.go
index ad0991140..077b5d5e6 100644
--- a/internal/pkg/manager/spod/bindata/spod.go
+++ b/internal/pkg/manager/spod/bindata/spod.go
@@ -343,8 +343,8 @@ semodule -i /opt/spo-profiles/selinuxrecording.cil
 								Port:   intstr.FromString("liveness-port"),
 								Scheme: corev1.URISchemeHTTP,
 							}},
-							FailureThreshold: 10, // nolint: gomnd
-							PeriodSeconds:    3,  // nolint: gomnd
+							FailureThreshold: 10, // nolint:gomnd // test number
+							PeriodSeconds:    3,  // nolint:gomnd // test number
 							TimeoutSeconds:   1,
 							SuccessThreshold: 1,
 						},
@@ -355,7 +355,7 @@ semodule -i /opt/spo-profiles/selinuxrecording.cil
 								Scheme: corev1.URISchemeHTTP,
 							}},
 							FailureThreshold: 1,
-							PeriodSeconds:    10, // nolint: gomnd
+							PeriodSeconds:    10, // nolint:gomnd // test number
 							TimeoutSeconds:   1,
 							SuccessThreshold: 1,
 						},
diff --git a/internal/pkg/manager/spod/spod_controller.go b/internal/pkg/manager/spod/spod_controller.go
index 12672b9fa..af2f29422 100644
--- a/internal/pkg/manager/spod/spod_controller.go
+++ b/internal/pkg/manager/spod/spod_controller.go
@@ -85,7 +85,7 @@ func (r *ReconcileSPOd) Healthz(*http.Request) error {
 }
 
 // Security Profiles Operator RBAC permissions to manage its own configuration
-// nolint:lll
+// nolint:lll // required for kubebuilder
 //
 // Used for leader election:
 // +kubebuilder:rbac:groups=core,resources=configmaps;events,verbs=get;list;watch;create;update;patch
@@ -326,7 +326,7 @@ func (r *ReconcileSPOd) handleCreate(
 	if err := r.client.Create(
 		ctx, bindata.ServiceMonitor(),
 	); err != nil {
-		// nolint: gocritic
+		// nolint:gocritic
 		if runtime.IsNotRegisteredError(err) || meta.IsNoMatchError(err) {
 			r.log.Info("Service monitor resource does not seem to exist, ignoring")
 		} else if kerrors.IsAlreadyExists(err) {
@@ -550,6 +550,11 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 
 		// Set the logging verbosity
 		templateSpec.InitContainers[i].Env = append(templateSpec.InitContainers[i].Env, verbosityEnv(cfg.Spec.Verbosity))
+
+		// Update the SELinux type tag when is defined in the configuration
+		if cfg.Spec.SelinuxTypeTag != "" {
+			templateSpec.InitContainers[i].SecurityContext.SELinuxOptions.Type = cfg.Spec.SelinuxTypeTag
+		}
 	}
 
 	for i := range templateSpec.Containers {
@@ -567,6 +572,10 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 		if cfg.Spec.EnableProfiling {
 			enableContainerProfiling(templateSpec, i)
 		}
+		// Update the SELinux type tag when is defined in the configuration
+		if cfg.Spec.SelinuxTypeTag != "" {
+			templateSpec.Containers[i].SecurityContext.SELinuxOptions.Type = cfg.Spec.SelinuxTypeTag
+		}
 	}
 
 	templateSpec.Tolerations = cfg.Spec.Tolerations
diff --git a/internal/pkg/util/names.go b/internal/pkg/util/names.go
index 29df282c5..72595a31f 100644
--- a/internal/pkg/util/names.go
+++ b/internal/pkg/util/names.go
@@ -72,9 +72,9 @@ func lengthName(maxLen int, hashPrefix, format string, a ...interface{}) (string
 func dnsLengthName(hashPrefix, format string, a ...interface{}) string {
 	const maxDNSLen = 63
 
-	// (jhrozek): I think it makes sense to make the utility function return
-	// error, but here I think it's OK to just ignore
-	// nolint:errcheck
+	// nolint:errcheck // (jhrozek): I think it makes sense to make the utility
+	// 					  function return error, but here I think it's OK to
+	// 					  just ignore
 	name, _ := lengthName(maxDNSLen, hashPrefix, format, a...)
 	return name
 }
diff --git a/internal/pkg/webhooks/binding/binding.go b/internal/pkg/webhooks/binding/binding.go
index 0872d6fd8..6d3516b32 100644
--- a/internal/pkg/webhooks/binding/binding.go
+++ b/internal/pkg/webhooks/binding/binding.go
@@ -87,7 +87,7 @@ func initContainerMap(m *sync.Map, spec *corev1.PodSpec) {
 }
 
 // Security Profiles Operator Webhook RBAC permissions
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilebindings,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilebindings/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilebindings/finalizers,verbs=delete;get;update;patch
@@ -95,12 +95,12 @@ func initContainerMap(m *sync.Map, spec *corev1.PodSpec) {
 // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
 
 // Leader election
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // +kubebuilder:rbac:groups=core,namespace="security-profiles-operator",resources=configmaps,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=coordination.k8s.io,namespace="security-profiles-operator",resources=leases,verbs=create;get;update;
 
-// nolint: gocritic
+// nolint:gocritic
 func (p *podSeccompBinder) Handle(ctx context.Context, req admission.Request) admission.Response {
 	profileBindings, err := p.ListProfileBindings(ctx, client.InNamespace(req.Namespace))
 	if err != nil {
@@ -189,8 +189,7 @@ func (p *podSeccompBinder) getSeccompProfile(
 		}, func(inErr error) bool {
 			return errors.Is(inErr, ErrProfWithoutStatus) || kerrors.IsNotFound(inErr)
 		})
-	// nolint:wrapcheck
-	// error is already wrapped
+	// nolint:wrapcheck // already wrapped
 	return seccompProfile, err
 }
 
diff --git a/internal/pkg/webhooks/binding/impl.go b/internal/pkg/webhooks/binding/impl.go
index 0d3536277..54decb687 100644
--- a/internal/pkg/webhooks/binding/impl.go
+++ b/internal/pkg/webhooks/binding/impl.go
@@ -77,7 +77,7 @@ func (d *defaultImpl) SetDecoder(decoder *admission.Decoder) {
 	d.decoder = decoder
 }
 
-// nolint: gocritic
+// nolint:gocritic
 func (d *defaultImpl) DecodePod(req admission.Request) (*corev1.Pod, error) {
 	pod := &corev1.Pod{}
 	err := d.decoder.Decode(req, pod)
diff --git a/internal/pkg/webhooks/recording/impl.go b/internal/pkg/webhooks/recording/impl.go
index ba3250f4c..0b779fae4 100644
--- a/internal/pkg/webhooks/recording/impl.go
+++ b/internal/pkg/webhooks/recording/impl.go
@@ -73,7 +73,7 @@ func (d *defaultImpl) GetOperatorNamespace() string {
 	return config.GetOperatorNamespace()
 }
 
-// nolint: gocritic
+// nolint:gocritic
 func (d *defaultImpl) DecodePod(req admission.Request) (*corev1.Pod, error) {
 	pod := &corev1.Pod{}
 	err := d.decoder.Decode(req, pod)
diff --git a/internal/pkg/webhooks/recording/recording.go b/internal/pkg/webhooks/recording/recording.go
index b7ef367c2..72e7f0dca 100644
--- a/internal/pkg/webhooks/recording/recording.go
+++ b/internal/pkg/webhooks/recording/recording.go
@@ -61,14 +61,14 @@ func RegisterWebhook(server *webhook.Server, c client.Client) {
 	)
 }
 
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // Security Profiles Operator Webhook RBAC permissions
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilerecordings,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilerecordings/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilerecordings/finalizers,verbs=delete;get;update;patch
 // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
 
-// nolint:lll
+// nolint:lll // required for kubebuilder
 // Leader election
 // +kubebuilder:rbac:groups=core,namespace="security-profiles-operator",resources=configmaps,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch


Test patch:
diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
index 398ac46c2..c37937014 100644
--- a/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
+++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
@@ -23,7 +23,7 @@ import (
 	"context"
 	"encoding/binary"
 	"encoding/json"
-	"io/ioutil"
+	"os"
 	"sync"
 	"syscall"
 	"testing"
@@ -298,7 +298,7 @@ func TestRun(t *testing.T) {
 					copy(res.Release[:], release)
 					return nil
 				})
-				mock.TempFileCalls(ioutil.TempFile)
+				mock.TempFileCalls(os.CreateTemp)
 			},
 			assert: func(err error) {
 				require.NotNil(t, err)
diff --git a/internal/pkg/daemon/enricher/audit_test.go b/internal/pkg/daemon/enricher/audit_test.go
index ca2d4ce61..43d2b3ccf 100644
--- a/internal/pkg/daemon/enricher/audit_test.go
+++ b/internal/pkg/daemon/enricher/audit_test.go
@@ -31,25 +31,25 @@ func Test_isAuditLine(t *testing.T) {
 	}{
 		{
 			"Should identify type=1326 log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`audit: type=1326 audit(1611996299.149:466250): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=615549 comm="sh" exe="/bin/busybox" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7f61a81c5923 code=0x7ffc0000`,
 			true,
 		},
 		{
 			"Should identify type=1326 lines with timestamp",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`Jul  8 10:31:23 ubuntu2004 kernel: [  270.853767] audit: type=1326 audit(1625740283.502:574): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=4709 comm="sh" exe="/bin/busybox" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7f3c012e467b code=0x7ffc0000`,
 			true,
 		},
 		{
 			"Should identify type=SECCOMP log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`type=SECCOMP msg=audit(1613596317.899:6461): auid=4294967295 uid=0 gid=0 ses=4294967295 subj=system_u:system_r:spc_t:s0:c284,c594 pid=2039886 comm="ls" exe="/bin/ls" sig=0 arch=c000003e syscall=3 compat=0 ip=0x7f62dce3d4c7 code=0x7ffc0000AUID="unset" UID="root" GID="root" ARCH=x86_64 SYSCALL=close`,
 			true,
 		},
 		{
 			"Should ignore unsupported log types",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`audit: type=1016 audit(1611996299.149:466250): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=615549 comm="sh" exe="/bin/busybox" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7f61a81c5923 code=0x7ffc0000`,
 			false,
 		},
@@ -60,7 +60,7 @@ func Test_isAuditLine(t *testing.T) {
 		},
 		{
 			"Should identify SELinux log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`type=AVC msg=audit(1613173578.156:2945): avc:  denied  { read } for  pid=75593 comm="security-profil" name="token" dev="tmpfs" ino=612459 scontext=system_u:system_r:container_t:s0:c4,c808 tcontext=system_u:object_r:var_lib_t:s0 tclass=lnk_file permissive=0`,
 			true,
 		},
@@ -85,7 +85,7 @@ func Test_extractAuditLine(t *testing.T) {
 	}{
 		{
 			"Should extract seccomp log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`audit: type=1326 audit(1612299677.115:549067): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=3109464 comm="sh" exe="/bin/busybox" sig=0 arch=c000003e syscall=0 compat=0 ip=0x7fce771ae923 code=0x7ffc0000`,
 			&auditLine{
 				type_:        "seccomp",
@@ -98,7 +98,7 @@ func Test_extractAuditLine(t *testing.T) {
 		},
 		{
 			"Should extract seccomp log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`type=SECCOMP msg=audit(1613596317.899:6461): auid=4294967295 uid=0 gid=0 ses=4294967295 subj=system_u:system_r:spc_t:s0:c284,c594 pid=2039886 comm="ls" exe="/bin/ls" sig=0 arch=c000003e syscall=3 compat=0 ip=0x7f62dce3d4c7 code=0x7ffc0000AUID="unset" UID="root" GID="root" ARCH=x86_64 SYSCALL=close`,
 			&auditLine{
 				type_:        "seccomp",
@@ -111,7 +111,7 @@ func Test_extractAuditLine(t *testing.T) {
 		},
 		{
 			"Should extract selinux log lines",
-			//nolint:lll
+			// nolint:lll // no need to wrap
 			`type=AVC msg=audit(1613173578.156:2945): avc:  denied  { read } for  pid=75593 comm="security-profil" name="token" dev="tmpfs" ino=612459 scontext=system_u:system_r:container_t:s0:c4,c808 tcontext=system_u:object_r:var_lib_t:s0 tclass=lnk_file permissive=0`,
 			&auditLine{
 				type_:        "selinux",
diff --git a/internal/pkg/daemon/profilerecorder/profilerecorder_test.go b/internal/pkg/daemon/profilerecorder/profilerecorder_test.go
index 645082277..42611be90 100644
--- a/internal/pkg/daemon/profilerecorder/profilerecorder_test.go
+++ b/internal/pkg/daemon/profilerecorder/profilerecorder_test.go
@@ -246,7 +246,7 @@ func TestReconcile(t *testing.T) {
 				assert.Nil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// BPF GoArchToSeccompArch fails
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -285,7 +285,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// BPF CreateOrUpdate fails
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -324,7 +324,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// BPF DialBpfRecorder fails
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -465,7 +465,7 @@ func TestReconcile(t *testing.T) {
 				assert.Nil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// BPF SyscallsForProfile fails
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -699,7 +699,7 @@ func TestReconcile(t *testing.T) {
 				emptySyncMap(&sut.podsToWatch)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// log seccomp success record
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				mock.GetPodReturns(&corev1.Pod{
@@ -726,7 +726,7 @@ func TestReconcile(t *testing.T) {
 				assert.Nil(t, retryErr)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// log seccomp success record
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				mock.GetPodReturns(&corev1.Pod{
@@ -980,7 +980,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// logs seccomp failed CreateOrUpdate
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -1016,7 +1016,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// logs seccomp failed GoArchToSeccompArch
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -1052,7 +1052,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// logs seccomp failed Syscalls
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -1148,7 +1148,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// logs seccomp DialEnricher fails
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
@@ -1387,7 +1387,7 @@ func TestReconcile(t *testing.T) {
 				assert.NotNil(t, err)
 			},
 		},
-		{ // nolint: dupl
+		{ // nolint:dupl // test duplicates are fine
 			// logs selinux failed Avcs
 			prepare: func(sut *RecorderReconciler, mock *profilerecorderfakes.FakeImpl) {
 				profileName := fmt.Sprintf("profile-%d", time.Now().Unix())
diff --git a/internal/pkg/daemon/seccompprofile/seccompprofile_test.go b/internal/pkg/daemon/seccompprofile/seccompprofile_test.go
index bff7f3ca8..ff16d5617 100644
--- a/internal/pkg/daemon/seccompprofile/seccompprofile_test.go
+++ b/internal/pkg/daemon/seccompprofile/seccompprofile_test.go
@@ -18,7 +18,6 @@ package seccompprofile
 
 import (
 	"context"
-	"io/ioutil"
 	"os"
 	"path"
 	"sort"
@@ -132,7 +131,7 @@ func TestSaveProfileOnDisk(t *testing.T) {
 	if os.Getuid() == 0 {
 		t.Skip("Test does not work as root user")
 	}
-	dir, err := ioutil.TempDir("", config.OperatorName)
+	dir, err := os.MkdirTemp("", config.OperatorName)
 	if err != nil {
 		t.Error(errors.Wrap(err, "error creating temp file for tests"))
 	}
@@ -188,13 +187,15 @@ func TestSaveProfileOnDisk(t *testing.T) {
 			}
 
 			_, gotErr := saveProfileOnDisk(tc.fileName, []byte(tc.contents))
-			file, _ := os.Stat(tc.fileName) // nolint: errcheck
+			file, statErr := os.Stat(tc.fileName)
 			gotFileCreated := file != nil
 
 			if tc.wantErr == "" {
 				require.NoError(t, gotErr)
+				require.NoError(t, statErr)
 			} else {
 				require.Equal(t, tc.wantErr, gotErr.Error())
+				require.Error(t, statErr)
 			}
 			require.Equal(t, tc.fileCreated, gotFileCreated, "was file created?")
 		})
diff --git a/internal/pkg/daemon/selinuxprofile/selinuxprofile_test.go b/internal/pkg/daemon/selinuxprofile/selinuxprofile_test.go
index 521eb944c..a495a2965 100644
--- a/internal/pkg/daemon/selinuxprofile/selinuxprofile_test.go
+++ b/internal/pkg/daemon/selinuxprofile/selinuxprofile_test.go
@@ -33,11 +33,11 @@ import (
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/manager/spod/bindata"
 )
 
-// nolint:gocognit
+// nolint:gocognit // complexity is fine for a test
 func Test_selinuxProfileHandler(t *testing.T) {
 	t.Parallel()
 	ns := "security-profiles-operator"
-	// nolint:tenv
+	// nolint:tenv // we want to set the env here
 	os.Setenv("OPERATOR_NAMESPACE", ns)
 	schemeInstance := scheme.Scheme
 	if err := spodv1alpha1.AddToScheme(schemeInstance); err != nil {
diff --git a/internal/pkg/webhooks/binding/binding_test.go b/internal/pkg/webhooks/binding/binding_test.go
index 009f1bbf9..f6e12a9cf 100644
--- a/internal/pkg/webhooks/binding/binding_test.go
+++ b/internal/pkg/webhooks/binding/binding_test.go
@@ -231,7 +231,7 @@ func TestHandle(t *testing.T) {
 				require.Equal(t, http.StatusInternalServerError, int(resp.Result.Code))
 			},
 		},
-		// nolint: dupl
+		// nolint:dupl // test duplicates are fine
 		{ // failure on UpdateResource
 			prepare: func(mock *bindingfakes.FakeImpl) {
 				mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
@@ -270,7 +270,7 @@ func TestHandle(t *testing.T) {
 				require.Equal(t, http.StatusInternalServerError, int(resp.Result.Code))
 			},
 		},
-		// nolint: dupl
+		// nolint:dupl // test duplicates are fine
 		{ // failure on UpdateResourceStatus
 			prepare: func(mock *bindingfakes.FakeImpl) {
 				mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
diff --git a/test/e2e_test.go b/test/e2e_test.go
index f3d844580..c88b85ffc 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -19,7 +19,6 @@ package e2e_test
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"regexp"
 	"strings"
@@ -226,9 +225,9 @@ spec:
     name: test-selfsigned
 `
 
-	file, err := ioutil.TempFile(os.TempDir(), "test-resource*.yaml")
+	file, err := os.CreateTemp("", "test-resource*.yaml")
 	e.Nil(err)
-	_, err = file.Write([]byte(certManifest))
+	_, err = file.WriteString(certManifest)
 	e.Nil(err)
 	defer os.Remove(file.Name())
 	for i := 0; i < tries; i++ {
@@ -247,11 +246,11 @@ spec:
 }
 
 func (e *e2e) updateManifest(path, src, repl string) {
-	content, err := ioutil.ReadFile(path)
+	content, err := os.ReadFile(path)
 	e.Nil(err)
 	re := regexp.MustCompile(src)
 	result := re.ReplaceAllString(string(content), repl)
-	err = ioutil.WriteFile(path, []byte(result), 0o600)
+	err = os.WriteFile(path, []byte(result), 0o600)
 	e.Nil(err)
 }
 
@@ -355,10 +354,10 @@ func (e *e2e) getCurrentContextNamespace(alt string) string {
 }
 
 func (e *e2e) writeAndCreate(manifest, filePattern string) func() {
-	file, err := ioutil.TempFile(os.TempDir(), filePattern)
+	file, err := os.CreateTemp("", filePattern)
 	fileName := file.Name()
 	e.Nil(err)
-	_, err = file.Write([]byte(manifest))
+	_, err = file.WriteString(manifest)
 	e.Nil(err)
 	err = file.Close()
 	e.Nil(err)
diff --git a/test/suite_test.go b/test/suite_test.go
index 861d1d799..4c06eedce 100644
--- a/test/suite_test.go
+++ b/test/suite_test.go
@@ -40,8 +40,8 @@ import (
 const (
 	kindVersion      = "v0.12.0"
 	kindImage        = "kindest/node:v1.23.4@sha256:0e34f0d0fd448aa2f2819cfd74e99fe5793a6e4938b328f657c8e3f81ee0dfb9"
-	kindDarwinSHA512 = "7aaeee7543e13c9919d9a14d62c275ac5d9f5abf15243490a83cf99eae7d30aef1682105a8f3f02641621617515c908136d4ae6c82ef06a84479d6468559fce7" // nolint: lll
-	kindLinuxSHA512  = "0d64b9bebbe23c8b9e0b8852c5f57b73b09989145b1e9d7913c524f9ab5feeb0f55d491e35e784fef282aa8496af4c12001dbd61a1d91144332ddbad0be6857c" // nolint: lll
+	kindDarwinSHA512 = "7aaeee7543e13c9919d9a14d62c275ac5d9f5abf15243490a83cf99eae7d30aef1682105a8f3f02641621617515c908136d4ae6c82ef06a84479d6468559fce7" // nolint:lll // full length SHA
+	kindLinuxSHA512  = "0d64b9bebbe23c8b9e0b8852c5f57b73b09989145b1e9d7913c524f9ab5feeb0f55d491e35e784fef282aa8496af4c12001dbd61a1d91144332ddbad0be6857c" // nolint:lll // full length SHA
 )
 
 var (
@@ -102,7 +102,7 @@ type vanilla struct {
 // We're unable to use parallel tests because of our usage of testify/suite.
 // See https://github.com/stretchr/testify/issues/187
 //
-// nolint:paralleltest
+// nolint:paralleltest // should not run in parallel
 func TestSuite(t *testing.T) {
 	fmt.Printf("cluster-type: %s\n", clusterType)
 	fmt.Printf("container-runtime: %s\n", containerRuntime)
@@ -235,7 +235,7 @@ func (e *kinde2e) SetupTest() {
 	e.logf("Deploying the cluster")
 	e.clusterName = fmt.Sprintf("spo-e2e-%d", time.Now().Unix())
 
-	cmd := exec.Command( // nolint: gosec
+	cmd := exec.Command( // nolint:gosec // fine in tests
 		e.kindPath, "create", "cluster",
 		"--name="+e.clusterName,
 		"--image="+kindImage,
@@ -499,7 +499,7 @@ func (e *e2e) getSpodMetrics() string {
 	letters := []rune("abcdefghijklmnopqrstuvwxyz")
 	b := make([]rune, 10)
 	for i := range b {
-		b[i] = letters[rand.Intn(len(letters))] // nolint: gosec
+		b[i] = letters[rand.Intn(len(letters))] // nolint:gosec // fine in tests
 	}
 	// Sometimes the metrics command does not output anything in CI. We fix
 	// that by retrying the metrics retrieval several times.
diff --git a/test/tc_base_profiles_test.go b/test/tc_base_profiles_test.go
index 9561c910a..2721c181a 100644
--- a/test/tc_base_profiles_test.go
+++ b/test/tc_base_profiles_test.go
@@ -18,7 +18,6 @@ package e2e_test
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"strings"
 	"time"
@@ -70,10 +69,10 @@ spec:
 	defer e.kubectl("delete", "-f", baseProfilePath)
 
 	e.logf("Creating hello profile")
-	helloProfileFile, err := ioutil.TempFile(os.TempDir(), "hello-profile*.yaml")
+	helloProfileFile, err := os.CreateTemp("", "hello-profile*.yaml")
 	e.Nil(err)
 	defer os.Remove(helloProfileFile.Name())
-	_, err = helloProfileFile.Write([]byte(helloProfile))
+	_, err = helloProfileFile.WriteString(helloProfile)
 	e.Nil(err)
 	err = helloProfileFile.Close()
 	e.Nil(err)
@@ -84,12 +83,12 @@ spec:
 	e.waitFor("condition=ready", "sp", "hello")
 
 	e.logf("Creating hello-world pod")
-	helloPodFile, err := ioutil.TempFile(os.TempDir(), "hello-pod*.yaml")
+	helloPodFile, err := os.CreateTemp("", "hello-pod*.yaml")
 	e.Nil(err)
 	defer os.Remove(helloPodFile.Name())
 
 	namespace := e.getCurrentContextNamespace(defaultNamespace)
-	_, err = helloPodFile.Write([]byte(fmt.Sprintf(helloPod, namespace)))
+	_, err = helloPodFile.WriteString(fmt.Sprintf(helloPod, namespace))
 	e.Nil(err)
 	err = helloPodFile.Close()
 	e.Nil(err)
diff --git a/test/tc_bpf_recorder_test.go b/test/tc_bpf_recorder_test.go
index 819d9ee90..ce7ce397d 100644
--- a/test/tc_bpf_recorder_test.go
+++ b/test/tc_bpf_recorder_test.go
@@ -18,7 +18,6 @@ package e2e_test
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"regexp"
 	"time"
@@ -144,9 +143,9 @@ spec:
       - name: nginx
         image: quay.io/security-profiles-operator/test-nginx:1.19.1
 `
-	testFile, err := ioutil.TempFile(os.TempDir(), "recording-deployment*.yaml")
+	testFile, err := os.CreateTemp("", "recording-deployment*.yaml")
 	e.Nil(err)
-	_, err = testFile.Write([]byte(testDeployment))
+	_, err = testFile.WriteString(testDeployment)
 	e.Nil(err)
 	err = testFile.Close()
 	e.Nil(err)
@@ -231,9 +230,9 @@ spec:
   restartPolicy: Never
 `, podName, i, image)
 
-		testPodFile, err := ioutil.TempFile(os.TempDir(), "recording-pod*.yaml")
+		testPodFile, err := os.CreateTemp("", "recording-pod*.yaml")
 		e.Nil(err)
-		_, err = testPodFile.Write([]byte(testPod))
+		_, err = testPodFile.WriteString(testPod)
 		e.Nil(err)
 		err = testPodFile.Close()
 		e.Nil(err)
diff --git a/test/tc_delete_profiles_test.go b/test/tc_delete_profiles_test.go
index 7d424edff..ee0936bae 100644
--- a/test/tc_delete_profiles_test.go
+++ b/test/tc_delete_profiles_test.go
@@ -143,10 +143,10 @@ spec:
 	} {
 		e.logf("> > Running test case for deleted profiles and pods: %s", testCase.description)
 		profileCleanup := e.writeAndCreate(deleteProfile, "delete-profile*.yaml")
-		defer profileCleanup()
+		defer profileCleanup() // nolint:gocritic // TODO: is this intentional?
 		e.waitFor("condition=ready", "seccompprofile", deleteProfileName)
 		podCleanup := e.writeAndCreate(fmt.Sprintf(testCase.podManifest, namespace), "delete-pod*.yaml")
-		defer podCleanup()
+		defer podCleanup() // nolint:gocritic // TODO: is this intention?
 		e.waitFor("condition=ready", "pod", deletePodName)
 		e.logf("Ensuring profile cannot be deleted while pod is active")
 		e.kubectl("delete", "seccompprofile", deleteProfileName, "--wait=0")
diff --git a/test/tc_profilebindings_test.go b/test/tc_profilebindings_test.go
index 1f67e1f90..de5cfe7f8 100644
--- a/test/tc_profilebindings_test.go
+++ b/test/tc_profilebindings_test.go
@@ -18,7 +18,6 @@ package e2e_test
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"strings"
 )
@@ -55,10 +54,10 @@ spec:
 	defer e.kubectl("delete", "-f", exampleProfilePath)
 
 	e.logf("Creating test profile binding")
-	testBindingFile, err := ioutil.TempFile(os.TempDir(), "hello-binding*.yaml")
+	testBindingFile, err := os.CreateTemp("", "hello-binding*.yaml")
 	e.Nil(err)
 	defer os.Remove(testBindingFile.Name())
-	_, err = testBindingFile.Write([]byte(testBinding))
+	_, err = testBindingFile.WriteString(testBinding)
 	e.Nil(err)
 	err = testBindingFile.Close()
 	e.Nil(err)
@@ -66,11 +65,11 @@ spec:
 	defer e.kubectl("delete", "-f", testBindingFile.Name())
 
 	e.logf("Creating test pod")
-	testPodFile, err := ioutil.TempFile(os.TempDir(), "hello-pod*.yaml")
+	testPodFile, err := os.CreateTemp("", "hello-pod*.yaml")
 	e.Nil(err)
 	defer os.Remove(testPodFile.Name())
 
-	_, err = testPodFile.Write([]byte(testPod))
+	_, err = testPodFile.WriteString(testPod)
 	e.Nil(err)
 	err = testPodFile.Close()
 	e.Nil(err)
diff --git a/test/tc_profilerecordings_test.go b/test/tc_profilerecordings_test.go
index e868d25b5..79c0ffc9f 100644
--- a/test/tc_profilerecordings_test.go
+++ b/test/tc_profilerecordings_test.go
@@ -18,7 +18,6 @@ package e2e_test
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"regexp"
 	"time"
@@ -348,9 +347,9 @@ spec:
         image: quay.io/security-profiles-operator/test-nginx:1.19.1
 `
 
-	testFile, err := ioutil.TempFile(os.TempDir(), "recording-deployment*.yaml")
+	testFile, err := os.CreateTemp("", "recording-deployment*.yaml")
 	e.Nil(err)
-	_, err = testFile.Write([]byte(testDeployment))
+	_, err = testFile.WriteString(testDeployment)
 	e.Nil(err)
 	err = testFile.Close()
 	e.Nil(err)
@@ -400,9 +399,9 @@ spec:
     name: nginx
   restartPolicy: Never
 `
-	testPodFile, err := ioutil.TempFile(os.TempDir(), "recording-pod*.yaml")
+	testPodFile, err := os.CreateTemp("", "recording-pod*.yaml")
 	e.Nil(err)
-	_, err = testPodFile.Write([]byte(testPod))
+	_, err = testPodFile.WriteString(testPod)
 	e.Nil(err)
 	err = testPodFile.Close()
 	e.Nil(err)
@@ -437,9 +436,9 @@ spec:
     image: quay.io/security-profiles-operator/redis:6.2.1
   restartPolicy: Never
 `
-	testPodFile, err := ioutil.TempFile(os.TempDir(), "recording-pod*.yaml")
+	testPodFile, err := os.CreateTemp("", "recording-pod*.yaml")
 	e.Nil(err)
-	_, err = testPodFile.Write([]byte(testPod))
+	_, err = testPodFile.WriteString(testPod)
 	e.Nil(err)
 	err = testPodFile.Close()
 	e.Nil(err)
diff --git a/test/tc_selinux_base_usage_test.go b/test/tc_selinux_base_usage_test.go
index 1f1c1702d..1d0506478 100644
--- a/test/tc_selinux_base_usage_test.go
+++ b/test/tc_selinux_base_usage_test.go
@@ -61,7 +61,7 @@ spec:
         - open
 `
 
-	// nolint:lll
+	// nolint:lll // full yaml
 	podWithPolicyFmt = `
 apiVersion: v1
 kind: Pod