Skip to content

Commit 056a35b

Browse files
committed
add Audit webhook configuration options to RootShard/Shard objects
On-behalf-of: @SAP [email protected]
1 parent efe76b9 commit 056a35b

File tree

13 files changed

+607
-5
lines changed

13 files changed

+607
-5
lines changed

config/crd/bases/operator.kcp.io_rootshards.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,80 @@ spec:
185185
Defaults to the latest kcp release that the operator supports.
186186
type: string
187187
type: object
188+
kcpConfiguration:
189+
properties:
190+
audit:
191+
properties:
192+
webhook:
193+
properties:
194+
batchBufferSize:
195+
description: The size of the buffer to store events before
196+
batching and writing. Only used in batch mode.
197+
type: integer
198+
batchMaxSize:
199+
description: The maximum size of a batch. Only used in
200+
batch mode.
201+
type: integer
202+
batchMaxWait:
203+
description: |-
204+
The amount of time to wait before force writing the batch that hadn't reached the max size.
205+
Only used in batch mode.
206+
type: string
207+
batchThrottleBurst:
208+
description: |-
209+
Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before.
210+
Only used in batch mode.
211+
type: integer
212+
batchThrottleEnable:
213+
description: Whether batching throttling is enabled. Only
214+
used in batch mode.
215+
type: boolean
216+
batchThrottleQPS:
217+
description: |-
218+
Maximum average number of batches per second. Only used in batch mode.
219+
This value is a floating point number, stored as a string (e.g. "3.1").
220+
type: string
221+
configSecretName:
222+
description: |-
223+
Name of a Kubernetes Secret that contains a kubeconfig formatted file that defines the
224+
audit webhook configuration.
225+
type: string
226+
initialBackoff:
227+
description: The amount of time to wait before retrying
228+
the first failed request.
229+
type: string
230+
mode:
231+
description: |-
232+
Strategy for sending audit events. Blocking indicates sending events should block server
233+
responses. Batch causes the backend to buffer and write events asynchronously.
234+
enum:
235+
- ""
236+
- batch
237+
- blocking
238+
- blocking-strict
239+
type: string
240+
truncateEnabled:
241+
description: Whether event and batch truncating is enabled.
242+
type: boolean
243+
truncateMaxBatchSize:
244+
description: |-
245+
Maximum size of the batch sent to the underlying backend. Actual serialized size can be
246+
several hundreds of bytes greater. If a batch exceeds this limit, it is split into several
247+
batches of smaller size.
248+
type: integer
249+
truncateMaxEventSize:
250+
description: |-
251+
Maximum size of the audit event sent to the underlying backend. If the size of an event
252+
is greater than this number, first request and response are removed, and if this doesn't
253+
reduce the size enough, event is discarded.
254+
type: integer
255+
version:
256+
description: API group and version used for serializing
257+
audit events written to webhook.
258+
type: string
259+
type: object
260+
type: object
261+
type: object
188262
replicas:
189263
description: Replicas configures how many instances of this shard
190264
run in parallel. Defaults to 2 if not set.

config/crd/bases/operator.kcp.io_shards.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,80 @@ spec:
110110
Defaults to the latest kcp release that the operator supports.
111111
type: string
112112
type: object
113+
kcpConfiguration:
114+
properties:
115+
audit:
116+
properties:
117+
webhook:
118+
properties:
119+
batchBufferSize:
120+
description: The size of the buffer to store events before
121+
batching and writing. Only used in batch mode.
122+
type: integer
123+
batchMaxSize:
124+
description: The maximum size of a batch. Only used in
125+
batch mode.
126+
type: integer
127+
batchMaxWait:
128+
description: |-
129+
The amount of time to wait before force writing the batch that hadn't reached the max size.
130+
Only used in batch mode.
131+
type: string
132+
batchThrottleBurst:
133+
description: |-
134+
Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before.
135+
Only used in batch mode.
136+
type: integer
137+
batchThrottleEnable:
138+
description: Whether batching throttling is enabled. Only
139+
used in batch mode.
140+
type: boolean
141+
batchThrottleQPS:
142+
description: |-
143+
Maximum average number of batches per second. Only used in batch mode.
144+
This value is a floating point number, stored as a string (e.g. "3.1").
145+
type: string
146+
configSecretName:
147+
description: |-
148+
Name of a Kubernetes Secret that contains a kubeconfig formatted file that defines the
149+
audit webhook configuration.
150+
type: string
151+
initialBackoff:
152+
description: The amount of time to wait before retrying
153+
the first failed request.
154+
type: string
155+
mode:
156+
description: |-
157+
Strategy for sending audit events. Blocking indicates sending events should block server
158+
responses. Batch causes the backend to buffer and write events asynchronously.
159+
enum:
160+
- ""
161+
- batch
162+
- blocking
163+
- blocking-strict
164+
type: string
165+
truncateEnabled:
166+
description: Whether event and batch truncating is enabled.
167+
type: boolean
168+
truncateMaxBatchSize:
169+
description: |-
170+
Maximum size of the batch sent to the underlying backend. Actual serialized size can be
171+
several hundreds of bytes greater. If a batch exceeds this limit, it is split into several
172+
batches of smaller size.
173+
type: integer
174+
truncateMaxEventSize:
175+
description: |-
176+
Maximum size of the audit event sent to the underlying backend. If the size of an event
177+
is greater than this number, first request and response are removed, and if this doesn't
178+
reduce the size enough, event is discarded.
179+
type: integer
180+
version:
181+
description: API group and version used for serializing
182+
audit events written to webhook.
183+
type: string
184+
type: object
185+
type: object
186+
type: object
113187
replicas:
114188
description: Replicas configures how many instances of this shard
115189
run in parallel. Defaults to 2 if not set.

internal/resources/rootshard/deployment.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.Nam
168168
dep.Spec.Replicas = ptr.To[int32](2)
169169
}
170170

171+
dep, err := utils.ApplyAuditConfiguration(dep, *rootShard.Spec.KcpConfiguration.Audit)
172+
if err != nil {
173+
return nil, fmt.Errorf("failed to apply audit configuration: %w", err)
174+
}
175+
171176
return dep, nil
172177
}
173178
}

internal/resources/shard/deployment.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ func getKubeconfigMountPath(certName operatorv1alpha1.Certificate) string {
6363
}
6464

6565
func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedDeploymentReconcilerFactory {
66-
6766
return func() (string, reconciling.DeploymentReconciler) {
6867
return resources.GetShardDeploymentName(shard), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) {
6968
labels := resources.GetShardResourceLabels(shard)
@@ -166,6 +165,11 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al
166165
dep.Spec.Replicas = ptr.To[int32](2)
167166
}
168167

168+
dep, err := utils.ApplyAuditConfiguration(dep, *shard.Spec.KcpConfiguration.Audit)
169+
if err != nil {
170+
return nil, fmt.Errorf("failed to apply audit configuration: %w", err)
171+
}
172+
169173
return dep, nil
170174
}
171175
}

internal/resources/utils/audit.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright 2025 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package utils
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
23+
appsv1 "k8s.io/api/apps/v1"
24+
corev1 "k8s.io/api/core/v1"
25+
26+
operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1"
27+
)
28+
29+
func ApplyAuditConfiguration(deployment *appsv1.Deployment, config operatorv1alpha1.AuditSpec) (*appsv1.Deployment, error) {
30+
if len(deployment.Spec.Template.Spec.Containers) == 0 {
31+
return deployment, errors.New("Deployment does not contain any containers")
32+
}
33+
34+
if config.Webhook == nil {
35+
return deployment, nil
36+
}
37+
38+
deployment, err := applyAuditWebhookConfiguration(deployment, *config.Webhook)
39+
if err != nil {
40+
return deployment, err
41+
}
42+
43+
return deployment, nil
44+
}
45+
46+
func applyAuditWebhookConfiguration(deployment *appsv1.Deployment, config operatorv1alpha1.AuditWebhookSpec) (*appsv1.Deployment, error) {
47+
podSpec := deployment.Spec.Template.Spec
48+
49+
var extraArgs []string
50+
51+
if val := config.BatchBufferSize; val != 0 {
52+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-buffer-size=%d", val))
53+
}
54+
55+
if val := config.BatchMaxSize; val != 0 {
56+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-max-size=%d", val))
57+
}
58+
59+
if val := config.BatchMaxWait; val != nil {
60+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-max-wait=%v", val.String()))
61+
}
62+
63+
if val := config.BatchThrottleBurst; val != 0 {
64+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-throttle-burst=%d", val))
65+
}
66+
67+
if val := config.BatchThrottleEnable; val {
68+
extraArgs = append(extraArgs, "--audit-webhook-batch-throttle-enable")
69+
}
70+
71+
if val := config.BatchThrottleQPS; val != "" {
72+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-throttle-qps=%s", val))
73+
}
74+
75+
if val := config.InitialBackoff; val != nil {
76+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-initial-backoff=%v", val.String()))
77+
}
78+
79+
if val := config.Mode; val != "" {
80+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-mode=%s", val))
81+
}
82+
83+
if val := config.TruncateEnabled; val {
84+
extraArgs = append(extraArgs, "--audit-webhook-truncate-enabled")
85+
}
86+
87+
if val := config.TruncateMaxBatchSize; val != 0 {
88+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-truncate-max-batch-size=%d", val))
89+
}
90+
91+
if val := config.TruncateMaxEventSize; val != 0 {
92+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-truncate-max-event-sizes=%d", val))
93+
}
94+
95+
if val := config.Version; val != "" {
96+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-version=%s", val))
97+
}
98+
99+
if val := config.ConfigSecretName; val != "" {
100+
volumeName := "audit-webhook-config"
101+
mountPath := "/etc/kcp/audit/webhook"
102+
103+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-config-file=%s/kubeconfig", mountPath))
104+
podSpec.Containers[0].VolumeMounts = append(podSpec.Containers[0].VolumeMounts, corev1.VolumeMount{
105+
Name: volumeName,
106+
ReadOnly: true,
107+
MountPath: mountPath,
108+
})
109+
110+
deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, corev1.Volume{
111+
Name: volumeName,
112+
VolumeSource: corev1.VolumeSource{
113+
Secret: &corev1.SecretVolumeSource{
114+
SecretName: val,
115+
},
116+
},
117+
})
118+
}
119+
120+
podSpec.Containers[0].Args = append(podSpec.Containers[0].Args, extraArgs...)
121+
deployment.Spec.Template.Spec = podSpec
122+
123+
return deployment, nil
124+
}

sdk/apis/operator/v1alpha1/shard_types.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,65 @@ type CommonShardSpec struct {
3737

3838
// Replicas configures how many instances of this shard run in parallel. Defaults to 2 if not set.
3939
Replicas *int32 `json:"replicas,omitempty"`
40+
41+
KcpConfiguration KcpConfigurationSpec `json:"kcpConfiguration,omitempty"`
42+
}
43+
44+
type KcpConfigurationSpec struct {
45+
Audit *AuditSpec `json:"audit,omitempty"`
46+
}
47+
48+
type AuditSpec struct {
49+
Webhook *AuditWebhookSpec `json:"webhook,omitempty"`
50+
}
51+
52+
// +kubebuilder:validation:Enum="";batch;blocking;blocking-strict
53+
54+
type AuditWebhookMode string
55+
56+
const (
57+
AuditWebhookBatchMode AuditWebhookMode = "batch"
58+
AuditWebhookBlockingMode AuditWebhookMode = "blocking"
59+
AuditWebhookBlockingStrictMode AuditWebhookMode = "blocking-strict"
60+
)
61+
62+
type AuditWebhookSpec struct {
63+
// The size of the buffer to store events before batching and writing. Only used in batch mode.
64+
BatchBufferSize int `json:"batchBufferSize,omitempty"`
65+
// The maximum size of a batch. Only used in batch mode.
66+
BatchMaxSize int `json:"batchMaxSize,omitempty"`
67+
// The amount of time to wait before force writing the batch that hadn't reached the max size.
68+
// Only used in batch mode.
69+
BatchMaxWait *metav1.Duration `json:"batchMaxWait,omitempty"`
70+
// Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before.
71+
// Only used in batch mode.
72+
BatchThrottleBurst int `json:"batchThrottleBurst,omitempty"`
73+
// Whether batching throttling is enabled. Only used in batch mode.
74+
BatchThrottleEnable bool `json:"batchThrottleEnable,omitempty"`
75+
// Maximum average number of batches per second. Only used in batch mode.
76+
// This value is a floating point number, stored as a string (e.g. "3.1").
77+
BatchThrottleQPS string `json:"batchThrottleQPS,omitempty"`
78+
79+
// Name of a Kubernetes Secret that contains a kubeconfig formatted file that defines the
80+
// audit webhook configuration.
81+
ConfigSecretName string `json:"configSecretName,omitempty"`
82+
// The amount of time to wait before retrying the first failed request.
83+
InitialBackoff *metav1.Duration `json:"initialBackoff,omitempty"`
84+
// Strategy for sending audit events. Blocking indicates sending events should block server
85+
// responses. Batch causes the backend to buffer and write events asynchronously.
86+
Mode AuditWebhookMode `json:"mode,omitempty"`
87+
// Whether event and batch truncating is enabled.
88+
TruncateEnabled bool `json:"truncateEnabled,omitempty"`
89+
// Maximum size of the batch sent to the underlying backend. Actual serialized size can be
90+
// several hundreds of bytes greater. If a batch exceeds this limit, it is split into several
91+
// batches of smaller size.
92+
TruncateMaxBatchSize int `json:"truncateMaxBatchSize,omitempty"`
93+
// Maximum size of the audit event sent to the underlying backend. If the size of an event
94+
// is greater than this number, first request and response are removed, and if this doesn't
95+
// reduce the size enough, event is discarded.
96+
TruncateMaxEventSize int `json:"truncateMaxEventSize,omitempty"`
97+
// API group and version used for serializing audit events written to webhook.
98+
Version string `json:"version,omitempty"`
4099
}
41100

42101
// ShardStatus defines the observed state of Shard

0 commit comments

Comments
 (0)