Repository: kubernetes-sigs/security-profiles-operator
Pull request #424 (2021-04-28 12:06:30)

Add RBAC proxy to metrics
#### What type of PR is this?

/kind feature


#### What this PR does / why we need it:
We now use the kube-rbac-proxy to provide two metrics endpoints. Those
get exposed through the `metrics-spod` and `metrics-controller-runtime`
services, serving an authorized self-signed TLS connection.

To be able to retrieve the metrics, it is now required to deploy a
`ClusterRole` and `ClusterRoleBinding` for a `ServiceAccount`. For
example:

```yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: spo-metrics-client
rules:
- nonResourceURLs:
  - /metrics
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: spo-metrics-client
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: spo-metrics-client
subjects:
- kind: ServiceAccount
  name: default
  namespace: security-profiles-operator
```

The above example is also part of the default deployment, and can be
tested via `kubectl run` within the `security-profiles-operator`
namespace by running:

```
> kubectl run --rm -it pod --image=alpine -- sh -c \
    'apk update && apk add bash curl &&
     curl -vsk -H "Authorization: Bearer `cat /var/run/secrets/kubernetes.io/serviceaccount/token`"
     https://metrics-spod/metrics'
…
*   Trying 10.0.0.222:443...
* Connected to metrics-spod (10.0.0.222) port 443 (#0)
…
> authorization: Bearer xxx
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; version=0.0.4; charset=utf-8
< date: Wed, 28 Apr 2021 12:00:46 GMT
…
security_profiles_operator_seccomp_profile{operation="update"} 1
```

Documentation and e2e testing will follow in further patches.
#### 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
-->
Refers to #422 
#### Does this PR have test?

<!--
If tests aren't applicable just write N/A.
-->
None
#### Special notes for your reviewer:
None
#### 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
Deploying kube-rbac-proxy sidecar in SPOD for exposing metrics via the new `metrics-spod` and `metrics-controller-runtime` services.
```


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/424#" title="Author self-approved">saschagrunert</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)~~ [saschagrunert]

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: @saschagrunert: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 005be9811caa24525633ffbf796ed719c08c3cb2 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/424/pull-security-profiles-operator-test-e2e/1387377690390040576) | `/test pull-security-profiles-operator-test-e2e`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=424). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Asaschagrunert). 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 -->
saschagrunert: > the rbac-proxy isn't using TLS, is it?

It is using a self-signed one right now and we also can provide an external one.
k8s-ci-robot: @saschagrunert: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 329eddbf8c9efabc2c861da95773290059cba9c1 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/424/pull-security-profiles-operator-test-e2e/1387388346245844992) | `/test pull-security-profiles-operator-test-e2e`

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

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | f22692de12948fa18c71afa5d3971a1246351308 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/424/pull-security-profiles-operator-test-e2e/1387392449369870336) | `/test pull-security-profiles-operator-test-e2e`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=424). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Asaschagrunert). 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 -->
saschagrunert: /retest 

Patch:
diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go
index 0fbbe2f5e..1d0be15dd 100644
--- a/cmd/security-profiles-operator/main.go
+++ b/cmd/security-profiles-operator/main.go
@@ -44,13 +44,13 @@ import (
 	spodv1alpha1 "sigs.k8s.io/security-profiles-operator/api/spod/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/profilerecorder"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/seccompprofile"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/selinuxprofile"
 	nodestatus "sigs.k8s.io/security-profiles-operator/internal/pkg/manager/nodestatus"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/manager/spod"
 	wa "sigs.k8s.io/security-profiles-operator/internal/pkg/manager/workloadannotator"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/metrics"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/nonrootenabler"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/version"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/webhooks/binding"
diff --git a/dependencies.yaml b/dependencies.yaml
index 416dc1a38..04a805b99 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -56,7 +56,7 @@ dependencies:
       match: config\.vm\.box = "fedora/(0|[1-9]\d*)-cloud-base"
 
   - name: debian-base
-    version: 1.4.0
+    version: 1.6.0
     refPaths:
     - path: hack/build-image/Dockerfile
       match: FROM k8s.gcr.io/build-image/debian-base:buster-v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?
@@ -66,3 +66,9 @@ dependencies:
     refPaths:
     - path: hack/build-image/Dockerfile
       match: ARG NIX_VERSION=(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?
+
+  - name: kube-rbac-proxy
+    version: 0.9.0
+    refPaths:
+    - path: internal/pkg/manager/spod/bindata/spod.go
+      match: MetricsImage\s+= "quay.io/brancz/kube-rbac-proxy:v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?"
diff --git a/deploy/base/kustomization.yaml b/deploy/base/kustomization.yaml
index 10079a29c..94ea1a21c 100644
--- a/deploy/base/kustomization.yaml
+++ b/deploy/base/kustomization.yaml
@@ -45,6 +45,7 @@ resources:
   - mutatingwebhookconfig.yaml
   - certificate.yaml
   - service.yaml
+  - metrics_client.yaml
 
 configMapGenerator:
   - name: security-profiles-operator-profile
diff --git a/deploy/base/metrics_client.yaml b/deploy/base/metrics_client.yaml
new file mode 100644
index 000000000..0cd01a5de
--- /dev/null
+++ b/deploy/base/metrics_client.yaml
@@ -0,0 +1,23 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: spo-metrics-client
+rules:
+- nonResourceURLs:
+  - /metrics
+  verbs:
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: spo-metrics-client
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: spo-metrics-client
+subjects:
+- kind: ServiceAccount
+  name: default
+  namespace: security-profiles-operator
diff --git a/deploy/base/role.yaml b/deploy/base/role.yaml
index d9f61c15b..a7f4dc2d3 100644
--- a/deploy/base/role.yaml
+++ b/deploy/base/role.yaml
@@ -184,6 +184,19 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - authentication.k8s.io
+  resources:
+  - subjectaccessreviews
+  - tokenreviews
+  verbs:
+  - create
+- apiGroups:
+  - authorization.k8s.io
+  resources:
+  - subjectaccessreviews
+  verbs:
+  - create
 - apiGroups:
   - ""
   resources:
diff --git a/deploy/base/service.yaml b/deploy/base/service.yaml
index 08b31d2a3..88983bd19 100644
--- a/deploy/base/service.yaml
+++ b/deploy/base/service.yaml
@@ -14,11 +14,25 @@ spec:
 apiVersion: v1
 kind: Service
 metadata:
-  name: metrics
+  name: metrics-controller-runtime
   namespace: security-profiles-operator
 spec:
   ports:
-    - port: 80
-      targetPort: 8081
+  - name: https
+    port: 443
+    targetPort: 8442
   selector:
-    app: spod
+    name: spod
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: metrics-spod
+  namespace: security-profiles-operator
+spec:
+  ports:
+  - name: https
+    port: 443
+    targetPort: 8443
+  selector:
+    name: spod
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index dadb89391..fe378c6c9 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -983,6 +983,19 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - authentication.k8s.io
+  resources:
+  - subjectaccessreviews
+  - tokenreviews
+  verbs:
+  - create
+- apiGroups:
+  - authorization.k8s.io
+  resources:
+  - subjectaccessreviews
+  verbs:
+  - create
 - apiGroups:
   - ""
   resources:
@@ -1533,14 +1546,59 @@ kind: Service
 metadata:
   labels:
     app: security-profiles-operator
-  name: metrics
+  name: metrics-controller-runtime
+  namespace: security-profiles-operator
+spec:
+  ports:
+  - name: https
+    port: 443
+    targetPort: 8442
+  selector:
+    app: security-profiles-operator
+    name: spod
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: metrics-spod
   namespace: security-profiles-operator
 spec:
   ports:
-  - port: 80
-    targetPort: 8081
+  - name: https
+    port: 443
+    targetPort: 8443
   selector:
     app: security-profiles-operator
+    name: spod
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: spo-metrics-client
+rules:
+- nonResourceURLs:
+  - /metrics
+  verbs:
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: spo-metrics-client
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: spo-metrics-client
+subjects:
+- kind: ServiceAccount
+  name: default
+  namespace: security-profiles-operator
 ---
 apiVersion: v1
 data:
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index 56da8c876..7aed03a74 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -983,6 +983,19 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - authentication.k8s.io
+  resources:
+  - subjectaccessreviews
+  - tokenreviews
+  verbs:
+  - create
+- apiGroups:
+  - authorization.k8s.io
+  resources:
+  - subjectaccessreviews
+  verbs:
+  - create
 - apiGroups:
   - ""
   resources:
@@ -1531,14 +1544,59 @@ kind: Service
 metadata:
   labels:
     app: security-profiles-operator
-  name: metrics
+  name: metrics-controller-runtime
+  namespace: security-profiles-operator
+spec:
+  ports:
+  - name: https
+    port: 443
+    targetPort: 8442
+  selector:
+    app: security-profiles-operator
+    name: spod
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: metrics-spod
   namespace: security-profiles-operator
 spec:
   ports:
-  - port: 80
-    targetPort: 8081
+  - name: https
+    port: 443
+    targetPort: 8443
   selector:
     app: security-profiles-operator
+    name: spod
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: spo-metrics-client
+rules:
+- nonResourceURLs:
+  - /metrics
+  verbs:
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  labels:
+    app: security-profiles-operator
+  name: spo-metrics-client
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: spo-metrics-client
+subjects:
+- kind: ServiceAccount
+  name: default
+  namespace: security-profiles-operator
 ---
 apiVersion: v1
 data:
diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile
index eed9631ec..c7a6ae241 100644
--- a/hack/build-image/Dockerfile
+++ b/hack/build-image/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM k8s.gcr.io/build-image/debian-base:buster-v1.4.0
+FROM k8s.gcr.io/build-image/debian-base:buster-v1.6.0
 WORKDIR /work
 
 RUN apt-get update && apt-get install -y wget xz-utils
diff --git a/internal/pkg/metrics/impl.go b/internal/pkg/daemon/metrics/impl.go
similarity index 100%
rename from internal/pkg/metrics/impl.go
rename to internal/pkg/daemon/metrics/impl.go
diff --git a/internal/pkg/metrics/metrics.go b/internal/pkg/daemon/metrics/metrics.go
similarity index 92%
rename from internal/pkg/metrics/metrics.go
rename to internal/pkg/daemon/metrics/metrics.go
index 2a2bd9134..71f6cd2b0 100644
--- a/internal/pkg/metrics/metrics.go
+++ b/internal/pkg/daemon/metrics/metrics.go
@@ -27,6 +27,10 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 )
 
+// Metrics proxy required permissions
+// +kubebuilder:rbac:groups=authentication.k8s.io,resources=tokenreviews;subjectaccessreviews,verbs=create
+// +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create
+
 const (
 	metricNamespace                      = "security_profiles_operator"
 	metricNameSeccompProfile             = "seccomp_profile"
diff --git a/internal/pkg/metrics/metricsfakes/fake_impl.go b/internal/pkg/daemon/metrics/metricsfakes/fake_impl.go
similarity index 100%
rename from internal/pkg/metrics/metricsfakes/fake_impl.go
rename to internal/pkg/daemon/metrics/metricsfakes/fake_impl.go
diff --git a/internal/pkg/daemon/profilerecorder/profilerecorder.go b/internal/pkg/daemon/profilerecorder/profilerecorder.go
index d6cbf78d5..fb9958871 100644
--- a/internal/pkg/daemon/profilerecorder/profilerecorder.go
+++ b/internal/pkg/daemon/profilerecorder/profilerecorder.go
@@ -44,7 +44,7 @@ import (
 
 	"sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/metrics"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 )
 
 const (
diff --git a/internal/pkg/daemon/seccompprofile/seccompprofile.go b/internal/pkg/daemon/seccompprofile/seccompprofile.go
index 94773491f..c6c0647c8 100644
--- a/internal/pkg/daemon/seccompprofile/seccompprofile.go
+++ b/internal/pkg/daemon/seccompprofile/seccompprofile.go
@@ -38,7 +38,7 @@ import (
 	"sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1alpha1"
 	statusv1alpha1 "sigs.k8s.io/security-profiles-operator/api/secprofnodestatus/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/metrics"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/nodestatus"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 )
diff --git a/internal/pkg/daemon/selinuxprofile/setup.go b/internal/pkg/daemon/selinuxprofile/setup.go
index 77944959e..34676fc95 100644
--- a/internal/pkg/daemon/selinuxprofile/setup.go
+++ b/internal/pkg/daemon/selinuxprofile/setup.go
@@ -27,7 +27,7 @@ import (
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 
 	spov1alpha1 "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha1"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/metrics"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 )
 
 var log = logf.Log.WithName("selinuxprofile")
diff --git a/internal/pkg/manager/spod/bindata/spod.go b/internal/pkg/manager/spod/bindata/spod.go
index fc7c39c60..81549092c 100644
--- a/internal/pkg/manager/spod/bindata/spod.go
+++ b/internal/pkg/manager/spod/bindata/spod.go
@@ -41,6 +41,7 @@ const (
 	SelinuxdSocketPath   = SelinuxdPrivateDir + "/selinuxd.sock"
 	SelinuxdDBPath       = SelinuxdPrivateDir + "/selinuxd.db"
 	varLogSpoPath        = "/var/log/spo.log"
+	MetricsImage         = "quay.io/brancz/kube-rbac-proxy:v0.9.0"
 )
 
 var Manifest = &appsv1.DaemonSet{
@@ -50,7 +51,10 @@ var Manifest = &appsv1.DaemonSet{
 	},
 	Spec: appsv1.DaemonSetSpec{
 		Selector: &metav1.LabelSelector{
-			MatchLabels: map[string]string{"app": "spod"},
+			MatchLabels: map[string]string{
+				"app":  config.OperatorName,
+				"name": "spod",
+			},
 		},
 		Template: v1.PodTemplateSpec{
 			ObjectMeta: metav1.ObjectMeta{
@@ -60,7 +64,8 @@ var Manifest = &appsv1.DaemonSet{
 					v1.SeccompContainerAnnotationKeyPrefix + config.OperatorName: "localhost/security-profiles-operator.json",
 				},
 				Labels: map[string]string{
-					"app": "spod",
+					"app":  config.OperatorName,
+					"name": "spod",
 				},
 			},
 			Spec: v1.PodSpec{
@@ -352,6 +357,62 @@ semodule -i /opt/spo-profiles/selinuxd.cil
 							},
 						},
 					},
+					{
+						Name:            "metrics-proxy-spod",
+						Image:           MetricsImage,
+						ImagePullPolicy: v1.PullIfNotPresent,
+						Args: []string{
+							"--secure-listen-address=0.0.0.0:8443",
+							"--upstream=http://127.0.0.1:8081/",
+							"--v=10",
+						},
+						Resources: v1.ResourceRequirements{
+							Requests: v1.ResourceList{
+								v1.ResourceMemory:           resource.MustParse("32Mi"),
+								v1.ResourceCPU:              resource.MustParse("50m"),
+								v1.ResourceEphemeralStorage: resource.MustParse("10Mi"),
+							},
+							Limits: v1.ResourceList{
+								v1.ResourceMemory:           resource.MustParse("128Mi"),
+								v1.ResourceCPU:              resource.MustParse("150m"),
+								v1.ResourceEphemeralStorage: resource.MustParse("20Mi"),
+							},
+						},
+						SecurityContext: &v1.SecurityContext{
+							AllowPrivilegeEscalation: &falsely,
+						},
+						Ports: []v1.ContainerPort{
+							{Name: "https", ContainerPort: 8443}, // nolint: gomnd
+						},
+					},
+					{
+						Name:            "metrics-controller-runtime",
+						Image:           MetricsImage,
+						ImagePullPolicy: v1.PullIfNotPresent,
+						Args: []string{
+							"--secure-listen-address=0.0.0.0:8442",
+							"--upstream=http://127.0.0.1:8080/",
+							"--v=10",
+						},
+						Resources: v1.ResourceRequirements{
+							Requests: v1.ResourceList{
+								v1.ResourceMemory:           resource.MustParse("32Mi"),
+								v1.ResourceCPU:              resource.MustParse("50m"),
+								v1.ResourceEphemeralStorage: resource.MustParse("10Mi"),
+							},
+							Limits: v1.ResourceList{
+								v1.ResourceMemory:           resource.MustParse("128Mi"),
+								v1.ResourceCPU:              resource.MustParse("150m"),
+								v1.ResourceEphemeralStorage: resource.MustParse("20Mi"),
+							},
+						},
+						SecurityContext: &v1.SecurityContext{
+							AllowPrivilegeEscalation: &falsely,
+						},
+						Ports: []v1.ContainerPort{
+							{Name: "https", ContainerPort: 8442}, // nolint: gomnd
+						},
+					},
 				},
 				Volumes: []v1.Volume{
 					// /var/lib is used as symlinks cannot be created across
diff --git a/internal/pkg/manager/spod/spod_controller.go b/internal/pkg/manager/spod/spod_controller.go
index ea4fe1899..56ffdbbbb 100644
--- a/internal/pkg/manager/spod/spod_controller.go
+++ b/internal/pkg/manager/spod/spod_controller.go
@@ -358,12 +358,23 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 		templateSpec.HostPID = true
 	}
 
+	// Metrics parameters
+	templateSpec.Containers = append(
+		templateSpec.Containers,
+		r.baseSPOd.Spec.Template.Spec.Containers[3],
+		r.baseSPOd.Spec.Template.Spec.Containers[4],
+	)
+
 	// Set image pull policy
 	for i := range templateSpec.InitContainers {
 		templateSpec.InitContainers[i].ImagePullPolicy = pullPolicy
 	}
 
 	for i := range templateSpec.Containers {
+		// The metrics image should be pulled always as IfNotPresent
+		if templateSpec.Containers[i].Image == bindata.MetricsImage {
+			continue
+		}
 		templateSpec.Containers[i].ImagePullPolicy = pullPolicy
 	}
 


Test patch:
diff --git a/internal/pkg/metrics/metrics_test.go b/internal/pkg/daemon/metrics/metrics_test.go
similarity index 97%
rename from internal/pkg/metrics/metrics_test.go
rename to internal/pkg/daemon/metrics/metrics_test.go
index 956e24f0e..1a9989980 100644
--- a/internal/pkg/metrics/metrics_test.go
+++ b/internal/pkg/daemon/metrics/metrics_test.go
@@ -24,7 +24,7 @@ import (
 	dto "github.com/prometheus/client_model/go"
 	"github.com/stretchr/testify/require"
 
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/metrics/metricsfakes"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics/metricsfakes"
 )
 
 var errTest = errors.New("")
diff --git a/test/e2e_test.go b/test/e2e_test.go
index 2737fb74f..adf8192c9 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -244,8 +244,8 @@ func (e *e2e) deployOperator(manifest string) {
 	e.waitInOperatorNSFor("condition=ready", "pod", "-l", "app=security-profiles-operator")
 	// Wait for all pods in DaemonSet
 	time.Sleep(defaultWaitTime)
-	e.waitInOperatorNSFor("condition=initialized", "pod", "-l", "app=spod")
-	e.waitInOperatorNSFor("condition=ready", "pod", "-l", "app=spod")
+	e.waitInOperatorNSFor("condition=initialized", "pod", "-l", "name=spod")
+	e.waitInOperatorNSFor("condition=ready", "pod", "-l", "name=spod")
 }
 
 func (e *e2e) cleanupOperator(manifest string) {