Repository: kubernetes-sigs/security-profiles-operator
Pull request #339 (2021-03-03 23:32:01)

Log enricher running unprivileged and extends supported audit line types
#### What type of PR is this?

/kind feature

#### What this PR does / why we need it:

- Extends log enrichment support to `type=AVC` (SELinux) and `type=SECCOMP` audit lines 
- Removes dependency on `/dev/kmsg` device.

#### Which issue(s) this PR fixes:

Relates to: https://github.com/kubernetes-sigs/security-profiles-operator/issues/244

#### Does this PR have test?

Yes. But E2E tests will be added on follow-up PRs.

#### Special notes for your reviewer:

Different distros may log seccomp audit lines differently, I have tested this based on log lines from Ubuntu (`type=1326`) and Fedora (`type=SECCOMP`).

The `/dev/kmsg` ended up being a dead-end as it cannot be executed in a least privilege mode. A follow-up PR will cater for the automatic creation of a symlink `/var/log/spo.log` pointing to the supported kernel log in the given distribution. In the interim the symlink needs to be manually created.

#### Does this PR introduce a user-facing change?

```release-note
Log enricher now supports SELinux log lines and runs unprivileged.
```


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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/339#" title="Author self-approved">pjbgf</a>*

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).

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/kubernetes-sigs/security-profiles-operator/blob/master/OWNERS)~~ [pjbgf]

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":[]} -->
k8s-ci-robot: @pjbgf: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | 4525dc03ea68d98bd3ed6e8fe5a53d6083181ece | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/339/pull-security-profiles-operator-verify/1367256482722091008) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=339). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Apjbgf). 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 -->
k8s-ci-robot: @pjbgf: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | 3d087b9cbef12e1fc33450f247463d1da7149bbd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/339/pull-security-profiles-operator-verify/1367277359253688320) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=339). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Apjbgf). 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-io: # [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=h1) Report
> Merging [#339](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=desc) (8207398) into [master](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/commit/65ad696befe7523cde2ae2e06747f79387828c91?el=desc) (65ad696) will **increase** coverage by `2.32%`.
> The diff coverage is `40.84%`.

[![Impacted file tree graph](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339/graphs/tree.svg?width=650&height=150&src=pr&token=37VIWSZ1ZT)](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #339      +/-   ##
==========================================
+ Coverage   27.34%   29.66%   +2.32%     
==========================================
  Files           9        9              
  Lines         673      691      +18     
==========================================
+ Hits          184      205      +21     
+ Misses        470      467       -3     
  Partials       19       19              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `29.66% <40.84%> (+2.32%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/pkg/daemon/enricher/main.go](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGtnL2RhZW1vbi9lbnJpY2hlci9tYWluLmdv) | `0.00% <0.00%> (ø)` | |
| [internal/pkg/daemon/enricher/container.go](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGtnL2RhZW1vbi9lbnJpY2hlci9jb250YWluZXIuZ28=) | `11.90% <26.31%> (+11.90%)` | :arrow_up: |
| [internal/pkg/daemon/enricher/audit.go](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGtnL2RhZW1vbi9lbnJpY2hlci9hdWRpdC5nbw==) | `90.32% <88.88%> (+4.60%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=footer). Last update [65ad696...8207398](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/339?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/doc/user-stories.md b/doc/user-stories.md
index 5678dc78d..ee07e251f 100644
--- a/doc/user-stories.md
+++ b/doc/user-stories.md
@@ -30,6 +30,16 @@ in a certain profile. E.g. for SELinux a missing parenthesis or something of the
 sort… This is considered for SELinux in
 [an issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues/223)
 
+### As an application developer, Ming would like to be able to automatically generate initial security profiles that are specific to the application.
+
+Details:
+
+* For improved profiles, users could define a process to be triggered during the profiling process (i.e. execution of E2E tests).
+* Avoid potential knowledge short falls by priming essential requirements 
+(e.g. blocked essential syscalls may cause 
+[issues](https://github.com/kubernetes/kubernetes/issues/85191) in seccomp)
+* The initial profile will need to be "manually" refined over time.
+
 ### As an application SRE, Rajesh needs to be able to see the state of the installed profile(s) and verify that it has indeed been installed on the system.
 
 ### As an application SRE, Rajesh needs to be able to do long runs of a security profile in “complain-mode” to easily identify the impact of a profile being enforced without impacting the application.
diff --git a/go.mod b/go.mod
index 6b2c8aff1..1df112e81 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,6 @@ module sigs.k8s.io/security-profiles-operator
 go 1.15
 
 require (
-	github.com/cirocosta/dmesg_exporter v0.0.0-20190515130104-6e3e42fc8d7b
 	github.com/containers/common v0.35.2
 	github.com/crossplane/crossplane-runtime v0.13.0
 	github.com/go-logr/logr v0.4.0
@@ -14,7 +13,7 @@ require (
 	k8s.io/api v0.20.4
 	k8s.io/apimachinery v0.20.4
 	k8s.io/client-go v0.20.4
-	k8s.io/klog/v2 v2.5.0
+	k8s.io/klog/v2 v2.6.0
 	k8s.io/release v0.7.0
 	sigs.k8s.io/controller-runtime v0.8.2
 	sigs.k8s.io/controller-tools v0.5.0
diff --git a/go.sum b/go.sum
index 440914aa4..d0856e433 100644
--- a/go.sum
+++ b/go.sum
@@ -139,8 +139,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
 github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
 github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s=
 github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
-github.com/cirocosta/dmesg_exporter v0.0.0-20190515130104-6e3e42fc8d7b h1:jk36mztUq5PHmR1J6H5FQg/vur4rrAowTuCPFamr5wQ=
-github.com/cirocosta/dmesg_exporter v0.0.0-20190515130104-6e3e42fc8d7b/go.mod h1:WZ6urbJg8HKOXim+GvWEB2mm2qhOpXWb8ILiIYjYyns=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -637,7 +635,6 @@ github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
 github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
 github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
@@ -687,7 +684,6 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
 github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
-github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -700,7 +696,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -709,7 +704,6 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+
 github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -1029,7 +1023,6 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180920110915-d641721ec2de/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1412,8 +1405,8 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/klog/v2 v2.5.0 h1:8mOnjf1RmUPW6KRqQCfYSZq/K20Unmp3IhuZUhxl8KI=
-k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
+k8s.io/klog/v2 v2.6.0 h1:c1wFxejFMBkp/VxCdc6kYdgrBkC2gzmcl6afuJAkJyU=
+k8s.io/klog/v2 v2.6.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
 k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
diff --git a/internal/pkg/daemon/enricher/audit.go b/internal/pkg/daemon/enricher/audit.go
index c4c1e58e5..43266bed1 100644
--- a/internal/pkg/daemon/enricher/audit.go
+++ b/internal/pkg/daemon/enricher/audit.go
@@ -23,37 +23,74 @@ import (
 	"github.com/pkg/errors"
 )
 
-// Initially only seccomp logs  are supported
 // type IDs are defined at https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/audit.h
-var auditRegex = regexp.MustCompile(`audit:.+type=1326.+audit\((.+)\).+pid=(\b\d+\b).+exe="(.+)".+syscall=(\b\d+\b).*`)
+var (
+	//nolint:lll
+	seccompLineRegex = regexp.MustCompile(`(type=SECCOMP|audit:.+type=1326).+audit\((.+)\).+pid=(\b\d+\b).+exe="(.+)".+syscall=(\b\d+\b).*`)
+	selinuxLineRegex = regexp.MustCompile(`type=AVC.+audit\((.+)\).+pid=(\b\d+\b).*`)
+)
 
-// minimum numbers of captures expected on a supported log line.
-var minCapturesExpected = 5
+var (
+	minSeccompCapturesExpected = 5
+	minSelinuxCapturesExpected = 3
+)
 
 // isAuditLine checks whether logLine is a supported audit line.
 func isAuditLine(logLine string) bool {
-	captures := auditRegex.FindStringSubmatch(logLine)
+	captures := seccompLineRegex.FindStringSubmatch(logLine)
+	if len(captures) >= minSeccompCapturesExpected {
+		return true
+	}
 
-	return len(captures) > 1
+	captures = selinuxLineRegex.FindStringSubmatch(logLine)
+	return len(captures) >= minSelinuxCapturesExpected
 }
 
 // extractAuditLine extracts an auditline from logLine.
 func extractAuditLine(logLine string) (*auditLine, error) {
-	captures := auditRegex.FindStringSubmatch(logLine)
-	if len(captures) < minCapturesExpected {
-		return nil, errors.Wrap(errUnsupportedLogLine, logLine)
+	if seccomp := extractSeccompLine(logLine); seccomp != nil {
+		return seccomp, nil
+	}
+
+	if selinux := extractSelinuxLine(logLine); selinux != nil {
+		return selinux, nil
+	}
+
+	return nil, errors.Wrap(errUnsupportedLogLine, logLine)
+}
+
+func extractSeccompLine(logLine string) *auditLine {
+	captures := seccompLineRegex.FindStringSubmatch(logLine)
+	if len(captures) < minSeccompCapturesExpected {
+		return nil
 	}
 
 	line := auditLine{}
 	line.Type = "seccomp"
-	line.TimestampID = captures[1]
-	line.Executable = captures[3]
-	if v, err := strconv.Atoi(captures[2]); err == nil {
+	line.TimestampID = captures[2]
+	line.Executable = captures[4]
+	if v, err := strconv.Atoi(captures[3]); err == nil {
 		line.ProcessID = v
 	}
-	if v, err := strconv.Atoi(captures[4]); err == nil {
+	if v, err := strconv.Atoi(captures[5]); err == nil {
 		line.SystemCallID = v
 	}
 
-	return &line, nil
+	return &line
+}
+
+func extractSelinuxLine(logLine string) *auditLine {
+	captures := selinuxLineRegex.FindStringSubmatch(logLine)
+	if len(captures) < minSelinuxCapturesExpected {
+		return nil
+	}
+
+	line := auditLine{}
+	line.Type = "selinux"
+	line.TimestampID = captures[1]
+	if v, err := strconv.Atoi(captures[2]); err == nil {
+		line.ProcessID = v
+	}
+
+	return &line
 }
diff --git a/internal/pkg/daemon/enricher/container.go b/internal/pkg/daemon/enricher/container.go
index 1ad7bed43..b44c9c60d 100644
--- a/internal/pkg/daemon/enricher/container.go
+++ b/internal/pkg/daemon/enricher/container.go
@@ -17,8 +17,12 @@ limitations under the License.
 package enricher
 
 import (
+	"bufio"
 	"context"
 	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
 	"strings"
 
 	"github.com/pkg/errors"
@@ -28,7 +32,8 @@ import (
 )
 
 const (
-	crioPrefix = "cri-o://"
+	crioCgroupRegex = `\/.+-([a-f0-9]+)`
+	crioPrefix      = "cri-o://"
 )
 
 // NOTE(jaosorior): Should this actually be namespace-scoped?
@@ -80,3 +85,38 @@ func containerIDRaw(containerID string) (string, error) {
 
 	return "", errors.Wrap(errUnsupportedContainerRuntime, containerID)
 }
+
+func getContainerID(processID int) string {
+	cgroupFile := fmt.Sprintf("/proc/%d/cgroup", processID)
+	file, err := os.Open(filepath.Clean(cgroupFile))
+	if err != nil {
+		logger.Error(nil, "could not open cgroup", "process-id", processID)
+		return ""
+	}
+	defer func() {
+		cerr := file.Close()
+		if err == nil {
+			err = cerr
+		}
+	}()
+
+	scanner := bufio.NewScanner(file)
+	scanner.Split(bufio.ScanLines)
+	for scanner.Scan() {
+		if containerID := extractID(scanner.Text()); containerID != "" {
+			return containerID
+		}
+	}
+
+	return ""
+}
+
+func extractID(cgroup string) string {
+	containerIDRegex := regexp.MustCompile(crioCgroupRegex)
+	capture := containerIDRegex.FindStringSubmatch(cgroup)
+	if len(capture) > 1 {
+		return capture[1]
+	}
+
+	return ""
+}
diff --git a/internal/pkg/daemon/enricher/main.go b/internal/pkg/daemon/enricher/main.go
index 6cb83d6d7..e321dde69 100644
--- a/internal/pkg/daemon/enricher/main.go
+++ b/internal/pkg/daemon/enricher/main.go
@@ -17,23 +17,18 @@ limitations under the License.
 package enricher
 
 import (
-	"bufio"
-	"context"
 	"fmt"
 	"io"
 	"os"
-	"os/signal"
 	"path/filepath"
-	"regexp"
-	"syscall"
+	"time"
 
-	"github.com/cirocosta/dmesg_exporter/kmsg"
-	"github.com/cirocosta/dmesg_exporter/reader"
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 )
 
 var (
-	logFile = "/dev/kmsg"
+	logFile = "/var/log/spo.log"
 	logger  logr.Logger
 )
 
@@ -50,10 +45,17 @@ func Run(l logr.Logger) {
 	logger.V(1).Info("starting log-exporter on node: ", nodeName)
 
 	auditLines := make(chan string)
-	go tailDevice(logFile, auditLines)
+	tailErrors := make(chan error)
+	go tailFile(logFile, auditLines, tailErrors)
 
 	for {
-		line := <-auditLines
+		var line string
+		select {
+		case err := <-tailErrors:
+			logger.Error(err, "tail audit log")
+			os.Exit(1)
+		case line = <-auditLines:
+		}
 
 		if !isAuditLine(line) {
 			continue
@@ -85,84 +87,38 @@ func Run(l logr.Logger) {
 	}
 }
 
-func blockAndCancelOnSignal(cancel context.CancelFunc) {
-	sigChan := make(chan os.Signal, 1)
-	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
-	<-sigChan
-
-	cancel()
-}
-
-func tailDevice(device string, msgs chan string) {
-	file, err := os.Open(device)
+func tailFile(filePath string, lines chan string, errChan chan error) {
+	file, err := os.Open(filepath.Clean(filePath))
 	if err != nil {
+		errChan <- errors.Wrap(err, "open audit log file")
 		return
 	}
-	defer file.Close()
+	defer func() {
+		err := file.Close()
+		if err != nil {
+			errChan <- errors.Wrap(err, "close audit log file")
+		}
+	}()
 
-	// seek to the end of device
-	_, err = file.Seek(0, io.SeekEnd)
+	offset, err := file.Seek(0, io.SeekEnd)
 	if err != nil {
+		errChan <- errors.Wrap(err, "seek end audit log")
 		return
 	}
 
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	go blockAndCancelOnSignal(cancel)
-
-	var (
-		r        = reader.NewReader(file)
-		messages = make(chan *kmsg.Message, 1)
-	)
-
-	kmsgErrorsChan := r.Listen(ctx, messages)
-
+	buffer := make([]byte, 1024)
 	for {
-		select {
-		case <-kmsgErrorsChan:
-			return
-		case <-ctx.Done():
-			return
-		case message := <-messages:
-			if message == nil {
+		readBytes, err := file.ReadAt(buffer, offset)
+		if err != nil {
+			if !errors.Is(err, io.EOF) {
+				errChan <- errors.Wrap(err, "read audit log buffer")
 				return
 			}
-
-			if message.Facility != kmsg.FacilityKern {
-				continue
-			}
-
-			msgs <- message.Message
-		}
-	}
-}
-
-func getContainerID(processID int) string {
-	cgroupFile := fmt.Sprintf("/proc/%d/cgroup", processID)
-	file, err := os.Open(filepath.Clean(cgroupFile))
-	if err != nil {
-		logger.Error(nil, "could not open cgroup", "process-id", processID)
-		return ""
-	}
-	defer func() {
-		cerr := file.Close()
-		if err == nil {
-			err = cerr
 		}
-	}()
-
-	// extracts crio format from cgroup:
-	// 0::/system.slice/crio-conmon-5819a498721cf8bb7e334809c9e48aa310bfc98801eb8017034ad17fb0749920.scope
-	podIDRegex := regexp.MustCompile(`^0.+-([a-f0-9]+)\.scope$`)
-	scanner := bufio.NewScanner(file)
-	scanner.Split(bufio.ScanLines)
-
-	for scanner.Scan() {
-		capture := podIDRegex.FindStringSubmatch(scanner.Text())
-		if len(capture) > 0 {
-			return capture[1]
+		offset += int64(readBytes)
+		if readBytes != 0 {
+			lines <- string(buffer[:readBytes])
 		}
+		time.Sleep(time.Second)
 	}
-	return ""
 }
diff --git a/internal/pkg/manager/spod/bindata/spod.go b/internal/pkg/manager/spod/bindata/spod.go
index 47af2dbd0..a3eccef3b 100644
--- a/internal/pkg/manager/spod/bindata/spod.go
+++ b/internal/pkg/manager/spod/bindata/spod.go
@@ -30,7 +30,7 @@ var (
 	truly                           = true
 	userRoot                  int64 = 0
 	userRootless                    = int64(config.UserRootless)
-	hostCharDev                     = v1.HostPathCharDev
+	hostPathFile                    = v1.HostPathFile
 	hostPathDirectory               = v1.HostPathDirectory
 	hostPathDirectoryOrCreate       = v1.HostPathDirectoryOrCreate
 )
@@ -40,7 +40,7 @@ const (
 	SelinuxdPrivateDir   = "/var/run/selinuxd"
 	SelinuxdSocketPath   = SelinuxdPrivateDir + "/selinuxd.sock"
 	SelinuxdDBPath       = SelinuxdPrivateDir + "/selinuxd.db"
-	kmsgPath             = "/dev/kmsg"
+	varLogSpoPath        = "/var/log/spo.log"
 )
 
 var Manifest = &appsv1.DaemonSet{
@@ -288,16 +288,13 @@ var Manifest = &appsv1.DaemonSet{
 						ImagePullPolicy: v1.PullAlways,
 						VolumeMounts: []v1.VolumeMount{
 							{
-								Name:      "host-devkmsg-volume",
-								MountPath: kmsgPath,
+								Name:      "host-varlogspo-volume",
+								MountPath: varLogSpoPath,
 								ReadOnly:  true,
 							},
 						},
 						SecurityContext: &v1.SecurityContext{
-							// /dev/kmsg is being used as kernel messages source in order to
-							// support the widest range of distros. However, access to
-							// devices requires the container to run as privileged.
-							Privileged:             &truly,
+							Privileged:             &falsely,
 							ReadOnlyRootFilesystem: &truly,
 							SELinuxOptions: &v1.SELinuxOptions{
 								// TODO(pjbgf): Use a more restricted selinux type
@@ -416,11 +413,11 @@ var Manifest = &appsv1.DaemonSet{
 						},
 					},
 					{
-						Name: "host-devkmsg-volume",
+						Name: "host-varlogspo-volume",
 						VolumeSource: v1.VolumeSource{
 							HostPath: &v1.HostPathVolumeSource{
-								Path: kmsgPath,
-								Type: &hostCharDev,
+								Path: varLogSpoPath,
+								Type: &hostPathFile,
 							},
 						},
 					},
diff --git a/vendor/github.com/cirocosta/dmesg_exporter/LICENSE b/vendor/github.com/cirocosta/dmesg_exporter/LICENSE
deleted file mode 100644
index 701501012..000000000
--- a/vendor/github.com/cirocosta/dmesg_exporter/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 Ciro S. Costa
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/cirocosta/dmesg_exporter/kmsg/kmsg.go b/vendor/github.com/cirocosta/dmesg_exporter/kmsg/kmsg.go
deleted file mode 100644
index f6e62a990..000000000
--- a/vendor/github.com/cirocosta/dmesg_exporter/kmsg/kmsg.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Package kmsg provides a minimal interface for dealing with
-// kmsg messages extracted from `/dev/kmesg`.
-package kmsg
-
-import (
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/pkg/errors"
-)
-
-type Priority uint8
-
-const (
-	PriorityEmerg Priority = iota
-	PriorityAlert
-	PriorityCrit
-	PriorityErr
-	PriorityWarning
-	PriorityNotice
-	PriorityInfo
-	PriorityDebug
-)
-
-func (p Priority) String() (s string) {
-	switch p {
-	case PriorityEmerg:
-		s = "emerg"
-	case PriorityAlert:
-		s = "alert"
-	case PriorityCrit:
-		s = "crit"
-	case PriorityErr:
-		s = "err"
-	case PriorityWarning:
-		s = "warning"
-	case PriorityNotice:
-		s = "notice"
-	case PriorityInfo:
-		s = "info"
-	case PriorityDebug:
-		s = "debug"
-	default:
-		s = "unknown"
-
-	}
-
-	return
-}
-
-type Facility uint8
-
-const (
-	FacilityKern Facility = iota
-	FacilityUser
-	FacilityMail
-	FacilityDaemon
-	FacilityAuth
-	FacilitySyslog
-	FacilityLpr
-	FacilityNews
-
-	FacilityUnknown // custom facility used to delimite those that we know
-)
-
-func (f Facility) String() (s string) {
-	switch f {
-	case FacilityKern:
-		s = "kern"
-	case FacilityUser:
-		s = "user"
-	case FacilityMail:
-		s = "mail"
-	case FacilityDaemon:
-		s = "daemon"
-	case FacilityAuth:
-		s = "auth"
-	case FacilitySyslog:
-		s = "syslog"
-	case FacilityLpr:
-		s = "lpr"
-	case FacilityNews:
-		s = "news"
-	default:
-		s = "unknown"
-	}
-
-	return
-}
-
-type Flag byte
-
-const (
-	FlagUnknown Flag = iota
-
-	FlagDefault           // -: default flag
-	FlagFragment          // c: indicates the fragment of a line
-	FlagFragmentFollowing // +: continuation of a fragment
-
-)
-
-// DecodeFlag decodes the raw byte flag into a type Flag object.
-func DecodeFlag(rawFlag byte) (flag Flag) {
-	switch rawFlag {
-	case '-':
-		flag = FlagDefault
-	case 'c':
-		flag = FlagFragment
-	case '+':
-		flag = FlagFragmentFollowing
-	default:
-		flag = FlagUnknown
-	}
-
-	return
-}
-
-func IsValidFacility(facility uint8) (isValid bool) {
-	isValid = (facility < uint8(FacilityUnknown))
-	return
-}
-
-type Message struct {
-	Facility       Facility
-	Flag           Flag
-	Message        string
-	Priority       Priority
-	SequenceNumber int64
-	Timestamp      time.Time
-}
-
-// DecodePrefix extracts both priority and facility from a given
-// syslog(2) encoded prefix.
-//
-//	   facility    priority
-//      .-----------.  .-----.
-//      |           |  |     |
-//	7  6  5  4  3  2  1  0    bits
-//
-// ps.: the priority does not need to be verified because we're
-//      picking the first 3 bits and there's no way of having a
-//	wrong priority given that the set of possible values has
-//	8 numbers.
-func DecodePrefix(prefix uint8) (priority Priority, facility Facility) {
-	const priortyMask uint8 = (1 << 3) - 1
-
-	facilityNum := prefix >> 3
-
-	if !IsValidFacility(facilityNum) {
-		facility = FacilityUnknown
-	} else {
-		facility = Facility(facilityNum)
-	}
-
-	priority = Priority(prefix & priortyMask)
-
-	return
-}
-
-// Parse takes care of parsing a `kmsg` message acording to the kernel
-// documentation at https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg.
-//
-// REGULAR MESSAGE:
-//
-//                  INFO		              MSG
-//     .------------------------------------------. .------.
-//    |                                            |        |
-//    |	int	int      int      char, <ignore>   | string |
-//    prefix  , seq, timestamp_us,flag[,..........];<message>
-//
-//
-// CONTINUATION:
-//
-//	    | key | value |
-//	/x7F<THIS>=<THATTT>
-//
-func Parse(rawMsg string) (m *Message, err error) {
-	if rawMsg == "" {
-		err = errors.Errorf("msg must not be empty")
-		return
-	}
-
-	splittedMessage := strings.SplitN(rawMsg, ";", 2)
-	if len(splittedMessage) < 2 {
-		err = errors.Errorf("message field not present")
-		return
-	}
-
-	m = new(Message)
-
-	infoSection := splittedMessage[0]
-	m.Message = strings.TrimSpace(splittedMessage[1])
-
-	splittedInfoSection := strings.SplitN(infoSection, ",", 5)
-	if len(splittedInfoSection) < 4 {
-		err = errors.Errorf("info section with not enought fields")
-		return
-	}
-
-	prefix, err := strconv.ParseInt(splittedInfoSection[0], 10, 8)
-	if err != nil {
-		err = errors.Wrapf(err,
-			"couldn't convert priority to int")
-		return
-	}
-
-	m.Priority, m.Facility = DecodePrefix(uint8(prefix))
-
-	m.SequenceNumber, err = strconv.ParseInt(splittedInfoSection[1], 10, 64)
-	if err != nil {
-		err = errors.Wrapf(err,
-			"couldn't convert sequence number to int64")
-		return
-	}
-
-	timestamp, err := strconv.ParseInt(splittedInfoSection[2], 10, 64)
-	if err != nil {
-		err = errors.Wrapf(err,
-			"couldn't convert sequence number to int64")
-		return
-	}
-
-	m.Timestamp = time.Unix(timestamp/int64(time.Millisecond), 0)
-
-	if len(splittedInfoSection[3]) != 1 {
-		err = errors.Errorf("flag must be a single char")
-		return
-	}
-
-	m.Flag = DecodeFlag(splittedInfoSection[3][0])
-
-	return
-}
diff --git a/vendor/github.com/cirocosta/dmesg_exporter/reader/reader.go b/vendor/github.com/cirocosta/dmesg_exporter/reader/reader.go
deleted file mode 100644
index accb40041..000000000
--- a/vendor/github.com/cirocosta/dmesg_exporter/reader/reader.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package reader
-
-import (
-	"context"
-	"io"
-	"syscall"
-
-	"github.com/cirocosta/dmesg_exporter/kmsg"
-	"github.com/pkg/errors"
-)
-
-type Reader interface {
-	Listen(ctx context.Context, messages chan<- *kmsg.Message) (errors chan error)
-}
-
-type kmsgReader struct {
-	file io.Reader
-}
-
-func NewReader(r io.Reader) (reader Reader) {
-	reader = &kmsgReader{
-		file: r,
-	}
-
-	return
-}
-
-const readBufferSize = 4096
-
-func (r *kmsgReader) Listen(ctx context.Context, messages chan<- *kmsg.Message) (errChan chan error) {
-	errChan = make(chan error, 1)
-
-	go func() {
-		defer close(errChan)
-
-		err := r.consumeDevice(ctx, messages)
-		if err != nil {
-			errChan <- err
-			return
-		}
-
-		return
-	}()
-
-	return
-}
-
-func (r *kmsgReader) consumeDevice(ctx context.Context, messages chan<- *kmsg.Message) (err error) {
-	var (
-		buffer  = make([]byte, readBufferSize)
-		n       int
-		message *kmsg.Message
-		done    = make(chan error, 1)
-	)
-
-	defer close(messages)
-
-	go func() {
-		var err error
-
-		defer func() {
-			done <- err
-		}()
-
-		for {
-			n, err = r.file.Read(buffer)
-			if err != nil {
-				if err == io.EOF {
-					err = nil
-					return
-				}
-
-				if err == syscall.EPIPE {
-					err = nil
-					return
-				}
-
-				err = errors.Wrapf(err,
-					"unexpected failure while reading file")
-				return
-			}
-
-			message, err = kmsg.Parse(string(buffer[:n]))
-			if err != nil {
-				err = errors.Wrapf(err,
-					"failed to parse raw message")
-				return
-			}
-
-			messages <- message
-		}
-	}()
-
-	select {
-	case err = <-done:
-	case <-ctx.Done():
-		err = ctx.Err()
-	}
-
-	return
-}
diff --git a/vendor/k8s.io/klog/v2/klog.go b/vendor/k8s.io/klog/v2/klog.go
index fe2b724f6..e850db6b0 100644
--- a/vendor/k8s.io/klog/v2/klog.go
+++ b/vendor/k8s.io/klog/v2/klog.go
@@ -433,7 +433,7 @@ func InitFlags(flagset *flag.FlagSet) {
 	flagset.Var(&logging.verbosity, "v", "number for the log level verbosity")
 	flagset.BoolVar(&logging.addDirHeader, "add_dir_header", logging.addDirHeader, "If true, adds the file directory to the header of the log messages")
 	flagset.BoolVar(&logging.skipHeaders, "skip_headers", logging.skipHeaders, "If true, avoid header prefixes in the log messages")
-	flagset.BoolVar(&logging.oneOutput, "one_output", logging.oneOutput, "If true, only write logs to their native severity level (vs also writing to each lower severity level")
+	flagset.BoolVar(&logging.oneOutput, "one_output", logging.oneOutput, "If true, only write logs to their native severity level (vs also writing to each lower severity level)")
 	flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files")
 	flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
 	flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 95c9de2c7..b6835a630 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -4,10 +4,6 @@ github.com/beorn7/perks/quantile
 github.com/blang/semver
 # github.com/cespare/xxhash/v2 v2.1.1
 github.com/cespare/xxhash/v2
-# github.com/cirocosta/dmesg_exporter v0.0.0-20190515130104-6e3e42fc8d7b
-## explicit
-github.com/cirocosta/dmesg_exporter/kmsg
-github.com/cirocosta/dmesg_exporter/reader
 # github.com/containers/common v0.35.2
 ## explicit
 github.com/containers/common/pkg/seccomp
@@ -404,7 +400,7 @@ k8s.io/client-go/util/workqueue
 # k8s.io/component-base v0.20.2
 k8s.io/component-base/config
 k8s.io/component-base/config/v1alpha1
-# k8s.io/klog/v2 v2.5.0
+# k8s.io/klog/v2 v2.6.0
 ## explicit
 k8s.io/klog/v2
 k8s.io/klog/v2/klogr


Test patch:
diff --git a/internal/pkg/daemon/enricher/audit_test.go b/internal/pkg/daemon/enricher/audit_test.go
index c7e126742..0eaebb275 100644
--- a/internal/pkg/daemon/enricher/audit_test.go
+++ b/internal/pkg/daemon/enricher/audit_test.go
@@ -30,11 +30,17 @@ func Test_isAuditLine(t *testing.T) {
 		want    bool
 	}{
 		{
-			"Should identify seccomp log lines",
+			"Should identify type=1326 log lines",
 			//nolint:lll
 			`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=SECCOMP log lines",
+			//nolint:lll
+			`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
@@ -46,6 +52,12 @@ func Test_isAuditLine(t *testing.T) {
 			`type=1326 syscall=1`,
 			false,
 		},
+		{
+			"Should identify SELinux log lines",
+			//nolint:lll
+			`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,
+		},
 	}
 	for _, tt := range tests {
 		tt := tt
@@ -78,6 +90,32 @@ func Test_extractAuditLine(t *testing.T) {
 			},
 			nil,
 		},
+		{
+			"Should extract seccomp log lines",
+			//nolint:lll
+			`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",
+				TimestampID:  "1613596317.899:6461",
+				SystemCallID: 3,
+				ProcessID:    2039886,
+				Executable:   "/bin/ls",
+			},
+			nil,
+		},
+		{
+			"Should extract selinux log lines",
+			//nolint:lll
+			`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",
+				TimestampID:  "1613173578.156:2945",
+				SystemCallID: 0,
+				ProcessID:    75593,
+				Executable:   "",
+			},
+			nil,
+		},
 	}
 	for _, tt := range tests {
 		tt := tt
diff --git a/internal/pkg/daemon/enricher/container_test.go b/internal/pkg/daemon/enricher/container_test.go
new file mode 100644
index 000000000..ead15c507
--- /dev/null
+++ b/internal/pkg/daemon/enricher/container_test.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2020 The Kubernetes 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.
+*/
+
+package enricher
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestExtractContainerID(t *testing.T) {
+	t.Parallel()
+	tests := []struct {
+		name       string
+		cgroupLine string
+		want       string
+	}{
+		{
+			"Should extract crio ID",
+			//nolint:lll
+			`4:net_cls,net_prio:/kubepods/besteffort/pod26ba375c-2266-4ecc-bf2d-b626db8762af/crio-af208fd68bf39a07a439ed0c9b6609b9ae63ecd8a5f1a2af3e0db48b945b320a`,
+			"af208fd68bf39a07a439ed0c9b6609b9ae63ecd8a5f1a2af3e0db48b945b320a",
+		},
+		{
+			"Should return empty when not found",
+			`0::/system.slice/crio.service`,
+			"",
+		},
+		{
+			"Should extract crio ID ending with .scope",
+			`0::/system.slice/crio-conmon-5819a498721cf8bb7e334809c9e48aa310bfc98801eb8017034ad17fb0749920.scope`,
+			"5819a498721cf8bb7e334809c9e48aa310bfc98801eb8017034ad17fb0749920",
+		},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+			got := extractID(tt.cgroupLine)
+			require.Equal(t, tt.want, got)
+		})
+	}
+}