Repository: kubernetes-sigs/security-profiles-operator
Pull request #1232 (2022-10-14 14:53:03)

spod: Make the webhook's ObjectSelector configurable
<!--  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?
/kind feature
/kind api-change

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind design

Optionally add one or more of the following kinds if applicable:
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
Implements a suggestion from code review of the PR that made webhooks
opt-in. I wanted to document a way to make the objectSelector
configurable and to my surprise found out that this is not possible
because we don't implement configurable objectSelectors.

#### Which issue(s) this PR fixes:
None

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #

or

-->

#### Does this PR have test?
Yes.

<!--
If tests aren't applicable just write N/A.
-->

#### Special notes for your reviewer:
I don't think we absulutely need this for 0.5.0, meaning that I wouldn't
delay the release.

#### 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
The spod CR now has a new attribute objectSelector that allows to configure
which objects would SPO's webhooks match on. By default, the selector matches
all, but setting the selector to include e.g. only certain labels might be a way
to further ensure that possible bugs in the webhooks don't affect the rest of
the cluster.
```


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/1232#" title="Author self-approved">jhrozek</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/main/OWNERS)~~ [jhrozek]

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":[]} -->
jhrozek: In PR #1207, @ccojocar suggested that we should we document setting the objectSelector to further restrict the objects that the webhooks match. I was surprised to find out that we don't expose objectSelector, but only namespaceSelector, so I just went ahead and exposed the objectSelector as well.

I don't think we need to delay the 0.5.0 release over this, I just didn't want Cosmin's suggestion to get forgotten because I agree that in general it's a good idea to not overuse webhooks in a cluster.
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/1232#" title="Author self-approved">jhrozek</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/1232#pullrequestreview-1143618989" title="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/main/OWNERS)~~ [jhrozek,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":[]} -->

Patch:
diff --git a/api/spod/v1alpha1/spod_types.go b/api/spod/v1alpha1/spod_types.go
index 2c073c9c3..c818fbd18 100644
--- a/api/spod/v1alpha1/spod_types.go
+++ b/api/spod/v1alpha1/spod_types.go
@@ -44,6 +44,9 @@ type WebhookOptions struct {
 	// NamespaceSelector sets webhook's namespace selector
 	// +optional
 	NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
+	// ObjectSelector sets webhook's object selector
+	// +optional
+	ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty"`
 }
 
 // SPODStatus defines the desired state of SPOD.
@@ -85,7 +88,6 @@ type SPODSpec struct {
 	// not create or update the webhook configuration and its related resources.
 	// +optional
 	StaticWebhookConfig bool `json:"staticWebhookConfig"`
-
 	// WebhookOpts set custom namespace selectors and failure mode for
 	// SPO's webhooks
 	// +optional
@@ -97,9 +99,13 @@ type SPODSpec struct {
 	// AllowedSeccompActions if specified, a list of allowed seccomp actions.
 	// +optional
 	AllowedSeccompActions []seccomp.Action `json:"allowedSeccompActions"`
-	// If specified, the SPOD's affinity.
+	// Affinity if specified, the SPOD's affinity.
 	// +optional
 	Affinity *corev1.Affinity `json:"affinity,omitempty"`
+	// ImagePullSecrets if defined, list of references to secrets in the security-profiles-operator's
+	// namespace to use for pulling the images from SPOD pod from a private registry.
+	// +optional
+	ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
 }
 
 // SPODState defines the state that the spod is in.
diff --git a/api/spod/v1alpha1/zz_generated.deepcopy.go b/api/spod/v1alpha1/zz_generated.deepcopy.go
index d6460a1f8..8bb885a3e 100644
--- a/api/spod/v1alpha1/zz_generated.deepcopy.go
+++ b/api/spod/v1alpha1/zz_generated.deepcopy.go
@@ -67,6 +67,11 @@ func (in *SPODSpec) DeepCopyInto(out *SPODSpec) {
 		*out = new(corev1.Affinity)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.ImagePullSecrets != nil {
+		in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
+		*out = make([]corev1.LocalObjectReference, len(*in))
+		copy(*out, *in)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SPODSpec.
@@ -187,6 +192,11 @@ func (in *WebhookOptions) DeepCopyInto(out *WebhookOptions) {
 		*out = new(metav1.LabelSelector)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.ObjectSelector != nil {
+		in, out := &in.ObjectSelector, &out.ObjectSelector
+		*out = new(metav1.LabelSelector)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookOptions.
diff --git a/bundle/manifests/security-profiles-operator.x-k8s.io_securityprofilesoperatordaemons.yaml b/bundle/manifests/security-profiles-operator.x-k8s.io_securityprofilesoperatordaemons.yaml
index b5f21e554..c7c718af4 100644
--- a/bundle/manifests/security-profiles-operator.x-k8s.io_securityprofilesoperatordaemons.yaml
+++ b/bundle/manifests/security-profiles-operator.x-k8s.io_securityprofilesoperatordaemons.yaml
@@ -44,7 +44,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -910,6 +910,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1035,6 +1050,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/base-crds/crds/securityprofilesoperatordaemon.yaml b/deploy/base-crds/crds/securityprofilesoperatordaemon.yaml
index 13dec5975..a43564f50 100644
--- a/deploy/base-crds/crds/securityprofilesoperatordaemon.yaml
+++ b/deploy/base-crds/crds/securityprofilesoperatordaemon.yaml
@@ -42,7 +42,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -908,6 +908,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1033,6 +1048,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/helm/crds/crds.yaml b/deploy/helm/crds/crds.yaml
index 28f813268..342539858 100644
--- a/deploy/helm/crds/crds.yaml
+++ b/deploy/helm/crds/crds.yaml
@@ -569,7 +569,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -1435,6 +1435,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1560,6 +1575,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index e67aa7314..4e184753d 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -569,7 +569,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -1435,6 +1435,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1560,6 +1575,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/openshift-dev.yaml b/deploy/openshift-dev.yaml
index f005b5759..2ef97f631 100644
--- a/deploy/openshift-dev.yaml
+++ b/deploy/openshift-dev.yaml
@@ -569,7 +569,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -1435,6 +1435,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1560,6 +1575,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index dc68760f0..1fded4ba5 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -569,7 +569,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -1435,6 +1435,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1560,6 +1575,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/deploy/webhook-operator.yaml b/deploy/webhook-operator.yaml
index 3c96c3d74..5b3dcfe45 100644
--- a/deploy/webhook-operator.yaml
+++ b/deploy/webhook-operator.yaml
@@ -745,7 +745,7 @@ spec:
             description: SPODStatus defines the desired state of SPOD.
             properties:
               affinity:
-                description: If specified, the SPOD's affinity.
+                description: Affinity if specified, the SPOD's affinity.
                 properties:
                   nodeAffinity:
                     description: Describes node affinity scheduling rules for the
@@ -1611,6 +1611,21 @@ spec:
                   as bpf-recorder to retrieve the container ID for a process ID. This
                   can be helpful for nested environments, for example when using "kind".
                 type: string
+              imagePullSecrets:
+                description: ImagePullSecrets if defined, list of references to secrets
+                  in the security-profiles-operator's namespace to use for pulling
+                  the images from SPOD pod from a private registry.
+                items:
+                  description: LocalObjectReference contains enough information to
+                    let you locate the referenced object inside the same namespace.
+                  properties:
+                    name:
+                      description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                        TODO: Add other useful fields. apiVersion, kind, uid?'
+                      type: string
+                  type: object
+                  x-kubernetes-map-type: atomic
+                type: array
               selinuxOptions:
                 description: Defines options specific to the SELinux functionality
                   of the SecurityProfilesOperator
@@ -1736,6 +1751,51 @@ spec:
                           type: object
                       type: object
                       x-kubernetes-map-type: atomic
+                    objectSelector:
+                      description: ObjectSelector sets webhook's object selector
+                      properties:
+                        matchExpressions:
+                          description: matchExpressions is a list of label selector
+                            requirements. The requirements are ANDed.
+                          items:
+                            description: A label selector requirement is a selector
+                              that contains values, a key, and an operator that relates
+                              the key and values.
+                            properties:
+                              key:
+                                description: key is the label key that the selector
+                                  applies to.
+                                type: string
+                              operator:
+                                description: operator represents a key's relationship
+                                  to a set of values. Valid operators are In, NotIn,
+                                  Exists and DoesNotExist.
+                                type: string
+                              values:
+                                description: values is an array of string values.
+                                  If the operator is In or NotIn, the values array
+                                  must be non-empty. If the operator is Exists or
+                                  DoesNotExist, the values array must be empty. This
+                                  array is replaced during a strategic merge patch.
+                                items:
+                                  type: string
+                                type: array
+                            required:
+                            - key
+                            - operator
+                            type: object
+                          type: array
+                        matchLabels:
+                          additionalProperties:
+                            type: string
+                          description: matchLabels is a map of {key,value} pairs.
+                            A single {key,value} in the matchLabels map is equivalent
+                            to an element of matchExpressions, whose key field is
+                            "key", the operator is "In", and the values array contains
+                            only "value". The requirements are ANDed.
+                          type: object
+                      type: object
+                      x-kubernetes-map-type: atomic
                   type: object
                 type: array
             type: object
diff --git a/go.mod b/go.mod
index fff0a9765..0d7b593a7 100644
--- a/go.mod
+++ b/go.mod
@@ -24,12 +24,12 @@ require (
 	github.com/urfave/cli/v2 v2.19.2
 	golang.org/x/net v0.0.0-20220909164309-bea034e7d591
 	golang.org/x/sync v0.0.0-20220907140024-f12130a52804
-	google.golang.org/grpc v1.50.0
+	google.golang.org/grpc v1.50.1
 	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
 	google.golang.org/protobuf v1.28.1
 	k8s.io/api v0.25.3
 	k8s.io/apimachinery v0.25.3
-	k8s.io/client-go v0.25.2
+	k8s.io/client-go v0.25.3
 	k8s.io/klog/v2 v2.80.1
 	sigs.k8s.io/controller-runtime v0.12.3
 	sigs.k8s.io/controller-tools v0.10.0
diff --git a/go.sum b/go.sum
index 473268e89..f182e7627 100644
--- a/go.sum
+++ b/go.sum
@@ -2073,8 +2073,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
 google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU=
-google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
+google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
+google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
@@ -2188,8 +2188,8 @@ k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
 k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU=
 k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
 k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=
-k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo=
-k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4=
+k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0=
+k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA=
 k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
 k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
 k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
diff --git a/installation-usage.md b/installation-usage.md
index 1317167b2..8ee41aa35 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -11,6 +11,7 @@
   - [Installation using helm](#installation-using-helm)
   - [Installation on AKS](#installation-on-aks)
 - [Set logging verbosity](#set-logging-verbosity)
+- [Pull images from private registry](#pull-images-from-private-registry)
 - [Configure the SELinux type](#configure-the-selinux-type)
 - [Restrict the allowed syscalls in seccomp profiles](#restrict-the-allowed-syscalls-in-seccomp-profiles)
 - [Constrain spod scheduling](#constrain-spod-scheduling)
@@ -194,6 +195,11 @@ 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"
 ```
 
+## Pull images from private registry
+
+The container images from spod pod can be pulled from a private registry. This can be achived by defining the `imagePullSecrets`
+inside of the SPOD configuration.
+
 ## 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
@@ -1161,16 +1167,20 @@ security_profiles_operator_seccomp_profile_audit_total{container="log-container"
 Both profile binding and profile recording make use of webhooks. Their configuration (an instance of
 `MutatingWebhookConfiguration` CR) is managed by SPO itself and not part of the deployed YAML manifests.
 While the defaults should be acceptable for the majority of users and the webhooks do nothing unless an
-instance of either `ProfileBinding` or `ProfileRecording` exists in a namespace, it might still be useful
-to configure the webhooks.
-
-In order to change webhook's configuration, the `spod` CR exposes an object `webhookOptions` that allows
-the `failurePolicy` and the `namespaceSelector` to be set. This way you can set the webhooks to "soft-fail"
-or restrict them to a subset of a namespaces so that even if the webhooks had a bug that would prevent them
-from running at all, other namespaces or resources wouldn't be affected.
+instance of either `ProfileBinding` or `ProfileRecording` exists in a namespace and in addition the
+namespace must be labeled with either `spo.x-k8s.io/enable-binding` or `spo.x-k8s.io/enable-recording`
+respectively by default, it might still be useful to configure the webhooks.
+
+In order to change webhook's configuration, the `spod` CR exposes an object
+`webhookOptions` that allows the `failurePolicy`, `namespaceSelector`
+and `objectSelector` to be set. This way you can set the webhooks to
+"soft-fail" or restrict them to a subset of a namespaces and inside those namespaces
+select only a subset of object matching the `objectSelector` so that even
+if the webhooks had a bug that would prevent them from running at all,
+other namespaces or resources wouldn't be affected.
 
 For example, to set the `binding.spo.io` webhook's configuration to ignore errors as well as restrict it
-to a subset of namespaces labeled with `spo.x-k8s.io/enable-binding=true`, create a following patch file:
+to a subset of namespaces labeled with `spo.x-k8s.io/bind-here=true`, create a following patch file:
 
 ```yaml
 spec:
@@ -1179,7 +1189,7 @@ spec:
       failurePolicy: Ignore
       namespaceSelector:
         matchExpressions:
-          - key: spo.x-k8s.io/enable-binding
+          - key: spo.x-k8s.io/bind-here
             operator: In
             values:
               - "true"
diff --git a/internal/pkg/manager/spod/bindata/webhook.go b/internal/pkg/manager/spod/bindata/webhook.go
index b4a1b668e..b704aef0b 100644
--- a/internal/pkg/manager/spod/bindata/webhook.go
+++ b/internal/pkg/manager/spod/bindata/webhook.go
@@ -95,6 +95,7 @@ func GetWebhook(
 	pullPolicy corev1.PullPolicy,
 	caInjectType CAInjectType,
 	tolerations []corev1.Toleration,
+	imagePullSecrets []corev1.LocalObjectReference,
 ) *Webhook {
 	deployment := webhookDeployment.DeepCopy()
 	deployment.Namespace = namespace
@@ -102,6 +103,10 @@ func GetWebhook(
 		deployment.Spec.Template.Spec.Tolerations = tolerations
 	}
 
+	if len(imagePullSecrets) > 0 {
+		deployment.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets
+	}
+
 	ctr := &deployment.Spec.Template.Spec.Containers[0]
 	ctr.Image = image
 	ctr.ImagePullPolicy = pullPolicy
@@ -180,6 +185,10 @@ func applyWebhookOptions(cfg *admissionregv1.MutatingWebhookConfiguration, opts
 			if userOpt.NamespaceSelector != nil {
 				hook.NamespaceSelector = userOpt.NamespaceSelector
 			}
+
+			if userOpt.ObjectSelector != nil {
+				hook.ObjectSelector = userOpt.ObjectSelector
+			}
 		}
 	}
 }
@@ -242,6 +251,18 @@ func webhookNeedsUpdate(existing, configured *admissionregv1.MutatingWebhook) bo
 		return true
 	}
 
+	if existing.ObjectSelector == nil && configured.ObjectSelector != nil ||
+		existing.ObjectSelector != nil && configured.ObjectSelector == nil {
+		// comparing pointers, not values
+		return true
+	}
+
+	if existing.ObjectSelector != nil &&
+		configured.ObjectSelector != nil &&
+		!reflect.DeepEqual(*existing.ObjectSelector, *configured.ObjectSelector) {
+		return true
+	}
+
 	return false
 }
 
diff --git a/internal/pkg/manager/spod/spod_controller.go b/internal/pkg/manager/spod/spod_controller.go
index abb39f129..11068d4ee 100644
--- a/internal/pkg/manager/spod/spod_controller.go
+++ b/internal/pkg/manager/spod/spod_controller.go
@@ -158,7 +158,7 @@ func (r *ReconcileSPOd) Reconcile(_ context.Context, req reconcile.Request) (rec
 	configuredSPOd := r.getConfiguredSPOd(spod, image, pullPolicy, caInjectType)
 
 	webhook := bindata.GetWebhook(r.log, r.namespace, spod.Spec.WebhookOpts, image,
-		pullPolicy, caInjectType, spod.Spec.Tolerations)
+		pullPolicy, caInjectType, spod.Spec.Tolerations, spod.Spec.ImagePullSecrets)
 	metricsService := bindata.GetMetricsService(r.namespace, caInjectType)
 
 	var certManagerResources *bindata.CertManagerResources
@@ -587,6 +587,7 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 
 	templateSpec.Tolerations = cfg.Spec.Tolerations
 	templateSpec.Affinity = cfg.Spec.Affinity
+	templateSpec.ImagePullSecrets = cfg.Spec.ImagePullSecrets
 
 	return newSPOd
 }
diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go
index 60403bc16..9372dc322 100644
--- a/vendor/google.golang.org/grpc/dialoptions.go
+++ b/vendor/google.golang.org/grpc/dialoptions.go
@@ -29,6 +29,7 @@ import (
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/internal"
 	internalbackoff "google.golang.org/grpc/internal/backoff"
+	"google.golang.org/grpc/internal/binarylog"
 	"google.golang.org/grpc/internal/transport"
 	"google.golang.org/grpc/keepalive"
 	"google.golang.org/grpc/resolver"
@@ -36,12 +37,13 @@ import (
 )
 
 func init() {
-	internal.AddExtraDialOptions = func(opt ...DialOption) {
+	internal.AddGlobalDialOptions = func(opt ...DialOption) {
 		extraDialOptions = append(extraDialOptions, opt...)
 	}
-	internal.ClearExtraDialOptions = func() {
+	internal.ClearGlobalDialOptions = func() {
 		extraDialOptions = nil
 	}
+	internal.WithBinaryLogger = withBinaryLogger
 }
 
 // dialOptions configure a Dial call. dialOptions are set by the DialOption
@@ -61,6 +63,7 @@ type dialOptions struct {
 	timeout                     time.Duration
 	scChan                      <-chan ServiceConfig
 	authority                   string
+	binaryLogger                binarylog.Logger
 	copts                       transport.ConnectOptions
 	callOptions                 []CallOption
 	channelzParentID            *channelz.Identifier
@@ -401,6 +404,14 @@ func WithStatsHandler(h stats.Handler) DialOption {
 	})
 }
 
+// withBinaryLogger returns a DialOption that specifies the binary logger for
+// this ClientConn.
+func withBinaryLogger(bl binarylog.Logger) DialOption {
+	return newFuncDialOption(func(o *dialOptions) {
+		o.binaryLogger = bl
+	})
+}
+
 // FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on
 // non-temporary dial errors. If f is true, and dialer returns a non-temporary
 // error, gRPC will fail the connection to the network address and won't try to
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
index e3dfe204f..809d73cca 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
@@ -37,7 +37,7 @@ type Logger interface {
 // binLogger is the global binary logger for the binary. One of this should be
 // built at init time from the configuration (environment variable or flags).
 //
-// It is used to get a methodLogger for each individual method.
+// It is used to get a MethodLogger for each individual method.
 var binLogger Logger
 
 var grpclogLogger = grpclog.Component("binarylog")
@@ -56,11 +56,11 @@ func GetLogger() Logger {
 	return binLogger
 }
 
-// GetMethodLogger returns the methodLogger for the given methodName.
+// GetMethodLogger returns the MethodLogger for the given methodName.
 //
 // methodName should be in the format of "/service/method".
 //
-// Each methodLogger returned by this method is a new instance. This is to
+// Each MethodLogger returned by this method is a new instance. This is to
 // generate sequence id within the call.
 func GetMethodLogger(methodName string) MethodLogger {
 	if binLogger == nil {
@@ -117,7 +117,7 @@ func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error {
 
 // Set method logger for "service/*".
 //
-// New methodLogger with same service overrides the old one.
+// New MethodLogger with same service overrides the old one.
 func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig) error {
 	if _, ok := l.config.Services[service]; ok {
 		return fmt.Errorf("conflicting service rules for service %v found", service)
@@ -131,7 +131,7 @@ func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig)
 
 // Set method logger for "service/method".
 //
-// New methodLogger with same method overrides the old one.
+// New MethodLogger with same method overrides the old one.
 func (l *logger) setMethodMethodLogger(method string, ml *MethodLoggerConfig) error {
 	if _, ok := l.config.Blacklist[method]; ok {
 		return fmt.Errorf("conflicting blacklist rules for method %v found", method)
@@ -161,11 +161,11 @@ func (l *logger) setBlacklist(method string) error {
 	return nil
 }
 
-// getMethodLogger returns the methodLogger for the given methodName.
+// getMethodLogger returns the MethodLogger for the given methodName.
 //
 // methodName should be in the format of "/service/method".
 //
-// Each methodLogger returned by this method is a new instance. This is to
+// Each MethodLogger returned by this method is a new instance. This is to
 // generate sequence id within the call.
 func (l *logger) GetMethodLogger(methodName string) MethodLogger {
 	s, m, err := grpcutil.ParseMethod(methodName)
@@ -174,16 +174,16 @@ func (l *logger) GetMethodLogger(methodName string) MethodLogger {
 		return nil
 	}
 	if ml, ok := l.config.Methods[s+"/"+m]; ok {
-		return newMethodLogger(ml.Header, ml.Message)
+		return NewTruncatingMethodLogger(ml.Header, ml.Message)
 	}
 	if _, ok := l.config.Blacklist[s+"/"+m]; ok {
 		return nil
 	}
 	if ml, ok := l.config.Services[s]; ok {
-		return newMethodLogger(ml.Header, ml.Message)
+		return NewTruncatingMethodLogger(ml.Header, ml.Message)
 	}
 	if l.config.All == nil {
 		return nil
 	}
-	return newMethodLogger(l.config.All.Header, l.config.All.Message)
+	return NewTruncatingMethodLogger(l.config.All.Header, l.config.All.Message)
 }
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
index ab589a76b..c5579e650 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
@@ -57,7 +57,7 @@ func NewLoggerFromConfigString(s string) Logger {
 	return l
 }
 
-// fillMethodLoggerWithConfigString parses config, creates methodLogger and adds
+// fillMethodLoggerWithConfigString parses config, creates TruncatingMethodLogger and adds
 // it to the right map in the logger.
 func (l *logger) fillMethodLoggerWithConfigString(config string) error {
 	// "" is invalid.
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
index 24df0a1a0..179f4a26d 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
@@ -52,7 +52,9 @@ type MethodLogger interface {
 	Log(LogEntryConfig)
 }
 
-type methodLogger struct {
+// TruncatingMethodLogger is a method logger that truncates headers and messages
+// based on configured fields.
+type TruncatingMethodLogger struct {
 	headerMaxLen, messageMaxLen uint64
 
 	callID          uint64
@@ -61,8 +63,9 @@ type methodLogger struct {
 	sink Sink // TODO(blog): make this plugable.
 }
 
-func newMethodLogger(h, m uint64) *methodLogger {
-	return &methodLogger{
+// NewTruncatingMethodLogger returns a new truncating method logger.
+func NewTruncatingMethodLogger(h, m uint64) *TruncatingMethodLogger {
+	return &TruncatingMethodLogger{
 		headerMaxLen:  h,
 		messageMaxLen: m,
 
@@ -75,8 +78,8 @@ func newMethodLogger(h, m uint64) *methodLogger {
 
 // Build is an internal only method for building the proto message out of the
 // input event. It's made public to enable other library to reuse as much logic
-// in methodLogger as possible.
-func (ml *methodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
+// in TruncatingMethodLogger as possible.
+func (ml *TruncatingMethodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
 	m := c.toProto()
 	timestamp, _ := ptypes.TimestampProto(time.Now())
 	m.Timestamp = timestamp
@@ -95,11 +98,11 @@ func (ml *methodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
 }
 
 // Log creates a proto binary log entry, and logs it to the sink.
-func (ml *methodLogger) Log(c LogEntryConfig) {
+func (ml *TruncatingMethodLogger) Log(c LogEntryConfig) {
 	ml.sink.Write(ml.Build(c))
 }
 
-func (ml *methodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
+func (ml *TruncatingMethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
 	if ml.headerMaxLen == maxUInt {
 		return false
 	}
@@ -129,7 +132,7 @@ func (ml *methodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
 	return truncated
 }
 
-func (ml *methodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) {
+func (ml *TruncatingMethodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) {
 	if ml.messageMaxLen == maxUInt {
 		return false
 	}
diff --git a/vendor/google.golang.org/grpc/internal/envconfig/observability.go b/vendor/google.golang.org/grpc/internal/envconfig/observability.go
new file mode 100644
index 000000000..821dd0a7c
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/envconfig/observability.go
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2022 gRPC 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 envconfig
+
+import "os"
+
+const (
+	envObservabilityConfig     = "GRPC_GCP_OBSERVABILITY_CONFIG"
+	envObservabilityConfigFile = "GRPC_GCP_OBSERVABILITY_CONFIG_FILE"
+)
+
+var (
+	// ObservabilityConfig is the json configuration for the gcp/observability
+	// package specified directly in the envObservabilityConfig env var.
+	ObservabilityConfig = os.Getenv(envObservabilityConfig)
+	// ObservabilityConfigFile is the json configuration for the
+	// gcp/observability specified in a file with the location specified in
+	// envObservabilityConfigFile env var.
+	ObservabilityConfigFile = os.Getenv(envObservabilityConfigFile)
+)
diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go
index 87cacb5b8..fd0ee3dca 100644
--- a/vendor/google.golang.org/grpc/internal/internal.go
+++ b/vendor/google.golang.org/grpc/internal/internal.go
@@ -63,24 +63,31 @@ var (
 	// xDS-enabled server invokes this method on a grpc.Server when a particular
 	// listener moves to "not-serving" mode.
 	DrainServerTransports interface{} // func(*grpc.Server, string)
-	// AddExtraServerOptions adds an array of ServerOption that will be
+	// AddGlobalServerOptions adds an array of ServerOption that will be
 	// effective globally for newly created servers. The priority will be: 1.
 	// user-provided; 2. this method; 3. default values.
-	AddExtraServerOptions interface{} // func(opt ...ServerOption)
-	// ClearExtraServerOptions clears the array of extra ServerOption. This
+	AddGlobalServerOptions interface{} // func(opt ...ServerOption)
+	// ClearGlobalServerOptions clears the array of extra ServerOption. This
 	// method is useful in testing and benchmarking.
-	ClearExtraServerOptions func()
-	// AddExtraDialOptions adds an array of DialOption that will be effective
+	ClearGlobalServerOptions func()
+	// AddGlobalDialOptions adds an array of DialOption that will be effective
 	// globally for newly created client channels. The priority will be: 1.
 	// user-provided; 2. this method; 3. default values.
-	AddExtraDialOptions interface{} // func(opt ...DialOption)
-	// ClearExtraDialOptions clears the array of extra DialOption. This
+	AddGlobalDialOptions interface{} // func(opt ...DialOption)
+	// ClearGlobalDialOptions clears the array of extra DialOption. This
 	// method is useful in testing and benchmarking.
-	ClearExtraDialOptions func()
+	ClearGlobalDialOptions func()
 	// JoinServerOptions combines the server options passed as arguments into a
 	// single server option.
 	JoinServerOptions interface{} // func(...grpc.ServerOption) grpc.ServerOption
 
+	// WithBinaryLogger returns a DialOption that specifies the binary logger
+	// for a ClientConn.
+	WithBinaryLogger interface{} // func(binarylog.Logger) grpc.DialOption
+	// BinaryLogger returns a ServerOption that can set the binary logger for a
+	// server.
+	BinaryLogger interface{} // func(binarylog.Logger) grpc.ServerOption
+
 	// NewXDSResolverWithConfigForTesting creates a new xds resolver builder using
 	// the provided xds bootstrap config instead of the global configuration from
 	// the supported environment variables.  The resolver.Builder is meant to be
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 6ef3df67d..f4dde72b4 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -73,12 +73,13 @@ func init() {
 	internal.DrainServerTransports = func(srv *Server, addr string) {
 		srv.drainServerTransports(addr)
 	}
-	internal.AddExtraServerOptions = func(opt ...ServerOption) {
-		extraServerOptions = opt
+	internal.AddGlobalServerOptions = func(opt ...ServerOption) {
+		extraServerOptions = append(extraServerOptions, opt...)
 	}
-	internal.ClearExtraServerOptions = func() {
+	internal.ClearGlobalServerOptions = func() {
 		extraServerOptions = nil
 	}
+	internal.BinaryLogger = binaryLogger
 	internal.JoinServerOptions = newJoinServerOption
 }
 
@@ -156,6 +157,7 @@ type serverOptions struct {
 	streamInt             StreamServerInterceptor
 	chainUnaryInts        []UnaryServerInterceptor
 	chainStreamInts       []StreamServerInterceptor
+	binaryLogger          binarylog.Logger
 	inTapHandle           tap.ServerInHandle
 	statsHandlers         []stats.Handler
 	maxConcurrentStreams  uint32
@@ -469,6 +471,14 @@ func StatsHandler(h stats.Handler) ServerOption {
 	})
 }
 
+// binaryLogger returns a ServerOption that can set the binary logger for the
+// server.
+func binaryLogger(bl binarylog.Logger) ServerOption {
+	return newFuncServerOption(func(o *serverOptions) {
+		o.binaryLogger = bl
+	})
+}
+
 // UnknownServiceHandler returns a ServerOption that allows for adding a custom
 // unknown service handler. The provided method is a bidi-streaming RPC service
 // handler that will be invoked instead of returning the "unimplemented" gRPC
@@ -1216,9 +1226,16 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 			}
 		}()
 	}
-
-	binlog := binarylog.GetMethodLogger(stream.Method())
-	if binlog != nil {
+	var binlogs []binarylog.MethodLogger
+	if ml := binarylog.GetMethodLogger(stream.Method()); ml != nil {
+		binlogs = append(binlogs, ml)
+	}
+	if s.opts.binaryLogger != nil {
+		if ml := s.opts.binaryLogger.GetMethodLogger(stream.Method()); ml != nil {
+			binlogs = append(binlogs, ml)
+		}
+	}
+	if len(binlogs) != 0 {
 		ctx := stream.Context()
 		md, _ := metadata.FromIncomingContext(ctx)
 		logEntry := &binarylog.ClientHeader{
@@ -1238,7 +1255,9 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 		if peer, ok := peer.FromContext(ctx); ok {
 			logEntry.PeerAddr = peer.Addr
 		}
-		binlog.Log(logEntry)
+		for _, binlog := range binlogs {
+			binlog.Log(logEntry)
+		}
 	}
 
 	// comp and cp are used for compression.  decomp and dc are used for
@@ -1278,7 +1297,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 	}
 
 	var payInfo *payloadInfo
-	if len(shs) != 0 || binlog != nil {
+	if len(shs) != 0 || len(binlogs) != 0 {
 		payInfo = &payloadInfo{}
 	}
 	d, err := recvAndDecompress(&parser{r: stream}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp)
@@ -1304,10 +1323,13 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 				Length:     len(d),
 			})
 		}
-		if binlog != nil {
-			binlog.Log(&binarylog.ClientMessage{
+		if len(binlogs) != 0 {
+			cm := &binarylog.ClientMessage{
 				Message: d,
-			})
+			}
+			for _, binlog := range binlogs {
+				binlog.Log(cm)
+			}
 		}
 		if trInfo != nil {
 			trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
@@ -1331,18 +1353,24 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 		if e := t.WriteStatus(stream, appStatus); e != nil {
 			channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
 		}
-		if binlog != nil {
+		if len(binlogs) != 0 {
 			if h, _ := stream.Header(); h.Len() > 0 {
 				// Only log serverHeader if there was header. Otherwise it can
 				// be trailer only.
-				binlog.Log(&binarylog.ServerHeader{
+				sh := &binarylog.ServerHeader{
 					Header: h,
-				})
+				}
+				for _, binlog := range binlogs {
+					binlog.Log(sh)
+				}
 			}
-			binlog.Log(&binarylog.ServerTrailer{
+			st := &binarylog.ServerTrailer{
 				Trailer: stream.Trailer(),
 				Err:     appErr,
-			})
+			}
+			for _, binlog := range binlogs {
+				binlog.Log(st)
+			}
 		}
 		return appErr
 	}
@@ -1368,26 +1396,34 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 				panic(fmt.Sprintf("grpc: Unexpected error (%T) from sendResponse: %v", st, st))
 			}
 		}
-		if binlog != nil {
+		if len(binlogs) != 0 {
 			h, _ := stream.Header()
-			binlog.Log(&binarylog.ServerHeader{
+			sh := &binarylog.ServerHeader{
 				Header: h,
-			})
-			binlog.Log(&binarylog.ServerTrailer{
+			}
+			st := &binarylog.ServerTrailer{
 				Trailer: stream.Trailer(),
 				Err:     appErr,
-			})
+			}
+			for _, binlog := range binlogs {
+				binlog.Log(sh)
+				binlog.Log(st)
+			}
 		}
 		return err
 	}
-	if binlog != nil {
+	if len(binlogs) != 0 {
 		h, _ := stream.Header()
-		binlog.Log(&binarylog.ServerHeader{
+		sh := &binarylog.ServerHeader{
 			Header: h,
-		})
-		binlog.Log(&binarylog.ServerMessage{
+		}
+		sm := &binarylog.ServerMessage{
 			Message: reply,
-		})
+		}
+		for _, binlog := range binlogs {
+			binlog.Log(sh)
+			binlog.Log(sm)
+		}
 	}
 	if channelz.IsOn() {
 		t.IncrMsgSent()
@@ -1399,11 +1435,14 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 	// Should the logging be in WriteStatus?  Should we ignore the WriteStatus
 	// error or allow the stats handler to see it?
 	err = t.WriteStatus(stream, statusOK)
-	if binlog != nil {
-		binlog.Log(&binarylog.ServerTrailer{
+	if len(binlogs) != 0 {
+		st := &binarylog.ServerTrailer{
 			Trailer: stream.Trailer(),
 			Err:     appErr,
-		})
+		}
+		for _, binlog := range binlogs {
+			binlog.Log(st)
+		}
 	}
 	return err
 }
@@ -1516,8 +1555,15 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
 		}()
 	}
 
-	ss.binlog = binarylog.GetMethodLogger(stream.Method())
-	if ss.binlog != nil {
+	if ml := binarylog.GetMethodLogger(stream.Method()); ml != nil {
+		ss.binlogs = append(ss.binlogs, ml)
+	}
+	if s.opts.binaryLogger != nil {
+		if ml := s.opts.binaryLogger.GetMethodLogger(stream.Method()); ml != nil {
+			ss.binlogs = append(ss.binlogs, ml)
+		}
+	}
+	if len(ss.binlogs) != 0 {
 		md, _ := metadata.FromIncomingContext(ctx)
 		logEntry := &binarylog.ClientHeader{
 			Header:     md,
@@ -1536,7 +1582,9 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
 		if peer, ok := peer.FromContext(ss.Context()); ok {
 			logEntry.PeerAddr = peer.Addr
 		}
-		ss.binlog.Log(logEntry)
+		for _, binlog := range ss.binlogs {
+			binlog.Log(logEntry)
+		}
 	}
 
 	// If dc is set and matches the stream's compression, use it.  Otherwise, try
@@ -1602,11 +1650,14 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
 			ss.mu.Unlock()
 		}
 		t.WriteStatus(ss.s, appStatus)
-		if ss.binlog != nil {
-			ss.binlog.Log(&binarylog.ServerTrailer{
+		if len(ss.binlogs) != 0 {
+			st := &binarylog.ServerTrailer{
 				Trailer: ss.s.Trailer(),
 				Err:     appErr,
-			})
+			}
+			for _, binlog := range ss.binlogs {
+				binlog.Log(st)
+			}
 		}
 		// TODO: Should we log an error from WriteStatus here and below?
 		return appErr
@@ -1617,11 +1668,14 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
 		ss.mu.Unlock()
 	}
 	err = t.WriteStatus(ss.s, statusOK)
-	if ss.binlog != nil {
-		ss.binlog.Log(&binarylog.ServerTrailer{
+	if len(ss.binlogs) != 0 {
+		st := &binarylog.ServerTrailer{
 			Trailer: ss.s.Trailer(),
 			Err:     appErr,
-		})
+		}
+		for _, binlog := range ss.binlogs {
+			binlog.Log(st)
+		}
 	}
 	return err
 }
diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go
index 446a91e32..0c16cfb2e 100644
--- a/vendor/google.golang.org/grpc/stream.go
+++ b/vendor/google.golang.org/grpc/stream.go
@@ -301,7 +301,14 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
 	if !cc.dopts.disableRetry {
 		cs.retryThrottler = cc.retryThrottler.Load().(*retryThrottler)
 	}
-	cs.binlog = binarylog.GetMethodLogger(method)
+	if ml := binarylog.GetMethodLogger(method); ml != nil {
+		cs.binlogs = append(cs.binlogs, ml)
+	}
+	if cc.dopts.binaryLogger != nil {
+		if ml := cc.dopts.binaryLogger.GetMethodLogger(method); ml != nil {
+			cs.binlogs = append(cs.binlogs, ml)
+		}
+	}
 
 	// Pick the transport to use and create a new stream on the transport.
 	// Assign cs.attempt upon success.
@@ -322,7 +329,7 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
 		return nil, err
 	}
 
-	if cs.binlog != nil {
+	if len(cs.binlogs) != 0 {
 		md, _ := metadata.FromOutgoingContext(ctx)
 		logEntry := &binarylog.ClientHeader{
 			OnClientSide: true,
@@ -336,7 +343,9 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
 				logEntry.Timeout = 0
 			}
 		}
-		cs.binlog.Log(logEntry)
+		for _, binlog := range cs.binlogs {
+			binlog.Log(logEntry)
+		}
 	}
 
 	if desc != unaryStreamDesc {
@@ -480,7 +489,7 @@ type clientStream struct {
 
 	retryThrottler *retryThrottler // The throttler active when the RPC began.
 
-	binlog binarylog.MethodLogger // Binary logger, can be nil.
+	binlogs []binarylog.MethodLogger
 	// serverHeaderBinlogged is a boolean for whether server header has been
 	// logged. Server header will be logged when the first time one of those
 	// happens: stream.Header(), stream.Recv().
@@ -744,7 +753,7 @@ func (cs *clientStream) Header() (metadata.MD, error) {
 		cs.finish(err)
 		return nil, err
 	}
-	if cs.binlog != nil && !cs.serverHeaderBinlogged {
+	if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged {
 		// Only log if binary log is on and header has not been logged.
 		logEntry := &binarylog.ServerHeader{
 			OnClientSide: true,
@@ -754,8 +763,10 @@ func (cs *clientStream) Header() (metadata.MD, error) {
 		if peer, ok := peer.FromContext(cs.Context()); ok {
 			logEntry.PeerAddr = peer.Addr
 		}
-		cs.binlog.Log(logEntry)
 		cs.serverHeaderBinlogged = true
+		for _, binlog := range cs.binlogs {
+			binlog.Log(logEntry)
+		}
 	}
 	return m, nil
 }
@@ -829,38 +840,44 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
 		return a.sendMsg(m, hdr, payload, data)
 	}
 	err = cs.withRetry(op, func() { cs.bufferForRetryLocked(len(hdr)+len(payload), op) })
-	if cs.binlog != nil && err == nil {
-		cs.binlog.Log(&binarylog.ClientMessage{
+	if len(cs.binlogs) != 0 && err == nil {
+		cm := &binarylog.ClientMessage{
 			OnClientSide: true,
 			Message:      data,
-		})
+		}
+		for _, binlog := range cs.binlogs {
+			binlog.Log(cm)
+		}
 	}
 	return err
 }
 
 func (cs *clientStream) RecvMsg(m interface{}) error {
-	if cs.binlog != nil && !cs.serverHeaderBinlogged {
+	if len(cs.binlogs) != 0 && !cs.serverHeaderBinlogged {
 		// Call Header() to binary log header if it's not already logged.
 		cs.Header()
 	}
 	var recvInfo *payloadInfo
-	if cs.binlog != nil {
+	if len(cs.binlogs) != 0 {
 		recvInfo = &payloadInfo{}
 	}
 	err := cs.withRetry(func(a *csAttempt) error {
 		return a.recvMsg(m, recvInfo)
 	}, cs.commitAttemptLocked)
-	if cs.binlog != nil && err == nil {
-		cs.binlog.Log(&binarylog.ServerMessage{
+	if len(cs.binlogs) != 0 && err == nil {
+		sm := &binarylog.ServerMessage{
 			OnClientSide: true,
 			Message:      recvInfo.uncompressedBytes,
-		})
+		}
+		for _, binlog := range cs.binlogs {
+			binlog.Log(sm)
+		}
 	}
 	if err != nil || !cs.desc.ServerStreams {
 		// err != nil or non-server-streaming indicates end of stream.
 		cs.finish(err)
 
-		if cs.binlog != nil {
+		if len(cs.binlogs) != 0 {
 			// finish will not log Trailer. Log Trailer here.
 			logEntry := &binarylog.ServerTrailer{
 				OnClientSide: true,
@@ -873,7 +890,9 @@ func (cs *clientStream) RecvMsg(m interface{}) error {
 			if peer, ok := peer.FromContext(cs.Context()); ok {
 				logEntry.PeerAddr = peer.Addr
 			}
-			cs.binlog.Log(logEntry)
+			for _, binlog := range cs.binlogs {
+				binlog.Log(logEntry)
+			}
 		}
 	}
 	return err
@@ -894,10 +913,13 @@ func (cs *clientStream) CloseSend() error {
 		return nil
 	}
 	cs.withRetry(op, func() { cs.bufferForRetryLocked(0, op) })
-	if cs.binlog != nil {
-		cs.binlog.Log(&binarylog.ClientHalfClose{
+	if len(cs.binlogs) != 0 {
+		chc := &binarylog.ClientHalfClose{
 			OnClientSide: true,
-		})
+		}
+		for _, binlog := range cs.binlogs {
+			binlog.Log(chc)
+		}
 	}
 	// We never returned an error here for reasons.
 	return nil
@@ -930,10 +952,13 @@ func (cs *clientStream) finish(err error) {
 	//
 	// Only one of cancel or trailer needs to be logged. In the cases where
 	// users don't call RecvMsg, users must have already canceled the RPC.
-	if cs.binlog != nil && status.Code(err) == codes.Canceled {
-		cs.binlog.Log(&binarylog.Cancel{
+	if len(cs.binlogs) != 0 && status.Code(err) == codes.Canceled {
+		c := &binarylog.Cancel{
 			OnClientSide: true,
-		})
+		}
+		for _, binlog := range cs.binlogs {
+			binlog.Log(c)
+		}
 	}
 	if err == nil {
 		cs.retryThrottler.successfulRPC()
@@ -1005,6 +1030,7 @@ func (a *csAttempt) recvMsg(m interface{}, payInfo *payloadInfo) (err error) {
 			}
 			return io.EOF // indicates successful end of stream.
 		}
+
 		return toRPCErr(err)
 	}
 	if a.trInfo != nil {
@@ -1453,7 +1479,7 @@ type serverStream struct {
 
 	statsHandler []stats.Handler
 
-	binlog binarylog.MethodLogger
+	binlogs []binarylog.MethodLogger
 	// serverHeaderBinlogged indicates whether server header has been logged. It
 	// will happen when one of the following two happens: stream.SendHeader(),
 	// stream.Send().
@@ -1487,12 +1513,15 @@ func (ss *serverStream) SendHeader(md metadata.MD) error {
 	}
 
 	err = ss.t.WriteHeader(ss.s, md)
-	if ss.binlog != nil && !ss.serverHeaderBinlogged {
+	if len(ss.binlogs) != 0 && !ss.serverHeaderBinlogged {
 		h, _ := ss.s.Header()
-		ss.binlog.Log(&binarylog.ServerHeader{
+		sh := &binarylog.ServerHeader{
 			Header: h,
-		})
+		}
 		ss.serverHeaderBinlogged = true
+		for _, binlog := range ss.binlogs {
+			binlog.Log(sh)
+		}
 	}
 	return err
 }
@@ -1549,17 +1578,23 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
 	if err := ss.t.Write(ss.s, hdr, payload, &transport.Options{Last: false}); err != nil {
 		return toRPCErr(err)
 	}
-	if ss.binlog != nil {
+	if len(ss.binlogs) != 0 {
 		if !ss.serverHeaderBinlogged {
 			h, _ := ss.s.Header()
-			ss.binlog.Log(&binarylog.ServerHeader{
+			sh := &binarylog.ServerHeader{
 				Header: h,
-			})
+			}
 			ss.serverHeaderBinlogged = true
+			for _, binlog := range ss.binlogs {
+				binlog.Log(sh)
+			}
 		}
-		ss.binlog.Log(&binarylog.ServerMessage{
+		sm := &binarylog.ServerMessage{
 			Message: data,
-		})
+		}
+		for _, binlog := range ss.binlogs {
+			binlog.Log(sm)
+		}
 	}
 	if len(ss.statsHandler) != 0 {
 		for _, sh := range ss.statsHandler {
@@ -1598,13 +1633,16 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
 		}
 	}()
 	var payInfo *payloadInfo
-	if len(ss.statsHandler) != 0 || ss.binlog != nil {
+	if len(ss.statsHandler) != 0 || len(ss.binlogs) != 0 {
 		payInfo = &payloadInfo{}
 	}
 	if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxReceiveMessageSize, payInfo, ss.decomp); err != nil {
 		if err == io.EOF {
-			if ss.binlog != nil {
-				ss.binlog.Log(&binarylog.ClientHalfClose{})
+			if len(ss.binlogs) != 0 {
+				chc := &binarylog.ClientHalfClose{}
+				for _, binlog := range ss.binlogs {
+					binlog.Log(chc)
+				}
 			}
 			return err
 		}
@@ -1625,10 +1663,13 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
 			})
 		}
 	}
-	if ss.binlog != nil {
-		ss.binlog.Log(&binarylog.ClientMessage{
+	if len(ss.binlogs) != 0 {
+		cm := &binarylog.ClientMessage{
 			Message: payInfo.uncompressedBytes,
-		})
+		}
+		for _, binlog := range ss.binlogs {
+			binlog.Log(cm)
+		}
 	}
 	return nil
 }
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index bfe4fafac..d472ca643 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
 package grpc
 
 // Version is the current grpc version.
-const Version = "1.50.0"
+const Version = "1.50.1"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index a07dbedce..bdb981d8c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -701,7 +701,7 @@ google.golang.org/appengine/urlfetch
 # google.golang.org/genproto v0.0.0-20220805133916-01dd62135a58
 ## explicit; go 1.18
 google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.50.0
+# google.golang.org/grpc v1.50.1
 ## explicit; go 1.17
 google.golang.org/grpc
 google.golang.org/grpc/attributes
@@ -936,7 +936,7 @@ k8s.io/apimachinery/third_party/forked/golang/reflect
 k8s.io/cli-runtime/pkg/genericclioptions
 k8s.io/cli-runtime/pkg/printers
 k8s.io/cli-runtime/pkg/resource
-# k8s.io/client-go v0.25.2
+# k8s.io/client-go v0.25.3
 ## explicit; go 1.19
 k8s.io/client-go/applyconfigurations/admissionregistration/v1
 k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1


Test patch:
diff --git a/test/tc_webhook_config_test.go b/test/tc_webhook_config_test.go
index 88054c7dc..b3a5a2739 100644
--- a/test/tc_webhook_config_test.go
+++ b/test/tc_webhook_config_test.go
@@ -21,67 +21,79 @@ import (
 	"time"
 )
 
-const whNamespaceSelector = `{"matchExpressions":[{"key":"prod","operator":"In","values":["true"]}]}`
+const (
+	whNamespaceSelector = `{"matchExpressions":[{"key":"prod","operator":"In","values":["true"]}]}`
+	whObjectSelector    = `{"matchExpressions":[{"key":"record-pod","operator":"In","values":["true"]}]}`
+)
+
+type whConfigOutput struct {
+	name              string
+	namespaceSelector string
+	objectSelector    string
+	failurePolicy     string
+}
+
+const (
+	bindingIdx = iota
+	recordingIdx
+	numWebhooks
+)
 
 func (e *e2e) testCaseWebhookOptionsChange([]string) {
 	if !e.testWebhookConfig {
 		e.T().Skip("Skipping webhook config related tests")
 	}
 	e.logf("Change webhook options")
-	origOutput0 := e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"binding.spo.io\")].namespaceSelector}")
-	origOutput1 := e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"recording.spo.io\")].namespaceSelector}")
 
-	whPatch := fmt.Sprintf(`{"spec":{"webhookOptions":[{"name":"binding.spo.io","failurePolicy":"Ignore","namespaceSelector":%s}]}}`, whNamespaceSelector) //nolint:lll // very long patch line
+	whDefault := e.getAllWebhookAttributes()
+
+	whPatch := fmt.Sprintf(`{"spec":{"webhookOptions":[{"name":"binding.spo.io","failurePolicy":"Ignore","namespaceSelector":%s, "objectSelector":%s}]}}`, whNamespaceSelector, whObjectSelector) //nolint:lll // very long patch line
 	e.logf(whPatch)
 	e.kubectlOperatorNS("patch", "spod", "spod", "-p", whPatch, "--type=merge")
 	time.Sleep(defaultWaitTime)
 
 	// check the configured hook
-	output := e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"binding.spo.io\")].failurePolicy}")
-	e.Equal("Ignore", output)
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"binding.spo.io\")].namespaceSelector}")
-	e.Equal(whNamespaceSelector, output)
-
+	whPatchedConfig := e.getAllWebhookAttributes()
+	e.Equal("Ignore", whPatchedConfig[bindingIdx].failurePolicy)
+	e.Equal(whNamespaceSelector, whPatchedConfig[bindingIdx].namespaceSelector)
+	e.Equal(whObjectSelector, whPatchedConfig[bindingIdx].objectSelector)
 	// check the other hook did not change
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"recording.spo.io\")].failurePolicy}")
-	e.Equal("Fail", output)
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"recording.spo.io\")].namespaceSelector}")
-	e.Equal(origOutput1, output)
+	e.Equal("Fail", whPatchedConfig[recordingIdx].failurePolicy)
+	e.Equal(whDefault[recordingIdx].namespaceSelector, whPatchedConfig[recordingIdx].namespaceSelector)
 
 	// go back to defaults
 	e.kubectlOperatorNS("patch", "spod", "spod", "-p", `{"spec":{"webhookOptions":[]}}`, "--type=merge")
 	time.Sleep(defaultWaitTime)
 
 	// check we are back to defaults
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"binding.spo.io\")].failurePolicy}")
-	e.Equal("Fail", output)
+	whRevertedConfig := e.getAllWebhookAttributes()
+	e.Equal("Fail", whRevertedConfig[bindingIdx].failurePolicy)
+	e.Equal(whDefault[bindingIdx].namespaceSelector, whRevertedConfig[bindingIdx].namespaceSelector)
+	// check the other hook did not change
+	e.Equal("Fail", whRevertedConfig[recordingIdx].failurePolicy)
+	e.Equal(whDefault[recordingIdx].namespaceSelector, whRevertedConfig[recordingIdx].namespaceSelector)
+}
 
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"binding.spo.io\")].namespaceSelector}")
-	e.Equal(origOutput0, output)
+func getWhConfigs() []*whConfigOutput {
+	whConfigs := make([]*whConfigOutput, numWebhooks)
+	whConfigs[0] = &whConfigOutput{name: "binding.spo.io"}
+	whConfigs[1] = &whConfigOutput{name: "recording.spo.io"}
+	return whConfigs
+}
 
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
-		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"recording.spo.io\")].failurePolicy}")
-	e.Equal("Fail", output)
+func (e *e2e) getAllWebhookAttributes() []*whConfigOutput {
+	out := getWhConfigs()
+	for i := range out {
+		out[i].objectSelector = e.getWebhookAttribute(out[i].name, "objectSelector")
+		out[i].namespaceSelector = e.getWebhookAttribute(out[i].name, "namespaceSelector")
+		out[i].failurePolicy = e.getWebhookAttribute(out[i].name, "failurePolicy")
+	}
+	return out
+}
 
-	output = e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
+func (e *e2e) getWebhookAttribute(hook, attr string) string {
+	jsonPath := fmt.Sprintf("{.webhooks[?(@.name==\"%s\")].%s}", hook, attr)
+	return e.kubectlOperatorNS("get", "MutatingWebhookConfiguration",
 		"spo-mutating-webhook-configuration",
-		"--output", "jsonpath={.webhooks[?(@.name==\"recording.spo.io\")].namespaceSelector}")
-	e.Equal(origOutput1, output)
+		"--output", "jsonpath="+jsonPath)
 }