Skip to content

Commit a9115c3

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

File tree

14 files changed

+636
-1
lines changed

14 files changed

+636
-1
lines changed

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,77 @@ spec:
4949
spec:
5050
description: RootShardSpec defines the desired state of RootShard.
5151
properties:
52+
audit:
53+
properties:
54+
webhook:
55+
properties:
56+
batchBufferSize:
57+
description: The size of the buffer to store events before
58+
batching and writing. Only used in batch mode.
59+
type: integer
60+
batchMaxSize:
61+
description: The maximum size of a batch. Only used in batch
62+
mode.
63+
type: integer
64+
batchMaxWait:
65+
description: |-
66+
The amount of time to wait before force writing the batch that hadn't reached the max size.
67+
Only used in batch mode.
68+
type: string
69+
batchThrottleBurst:
70+
description: |-
71+
Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before.
72+
Only used in batch mode.
73+
type: integer
74+
batchThrottleEnable:
75+
description: Whether batching throttling is enabled. Only
76+
used in batch mode.
77+
type: boolean
78+
batchThrottleQPS:
79+
description: |-
80+
Maximum average number of batches per second. Only used in batch mode.
81+
This value is a floating point number, stored as a string (e.g. "3.1").
82+
type: string
83+
configSecretName:
84+
description: |-
85+
Name of a Kubernetes Secret that contains a kubeconfig formatted file that defines the
86+
audit webhook configuration.
87+
type: string
88+
initialBackoff:
89+
description: The amount of time to wait before retrying the
90+
first failed request.
91+
type: string
92+
mode:
93+
description: |-
94+
Strategy for sending audit events. Blocking indicates sending events should block server
95+
responses. Batch causes the backend to buffer and write events asynchronously.
96+
enum:
97+
- ""
98+
- batch
99+
- blocking
100+
- blocking-strict
101+
type: string
102+
truncateEnabled:
103+
description: Whether event and batch truncating is enabled.
104+
type: boolean
105+
truncateMaxBatchSize:
106+
description: |-
107+
Maximum size of the batch sent to the underlying backend. Actual serialized size can be
108+
several hundreds of bytes greater. If a batch exceeds this limit, it is split into several
109+
batches of smaller size.
110+
type: integer
111+
truncateMaxEventSize:
112+
description: |-
113+
Maximum size of the audit event sent to the underlying backend. If the size of an event
114+
is greater than this number, first request and response are removed, and if this doesn't
115+
reduce the size enough, event is discarded.
116+
type: integer
117+
version:
118+
description: API group and version used for serializing audit
119+
events written to webhook.
120+
type: string
121+
type: object
122+
type: object
52123
cache:
53124
description: Cache configures the cache server (with a Kubernetes-like
54125
API) used by a sharded kcp instance.

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,77 @@ spec:
4949
spec:
5050
description: ShardSpec defines the desired state of Shard
5151
properties:
52+
audit:
53+
properties:
54+
webhook:
55+
properties:
56+
batchBufferSize:
57+
description: The size of the buffer to store events before
58+
batching and writing. Only used in batch mode.
59+
type: integer
60+
batchMaxSize:
61+
description: The maximum size of a batch. Only used in batch
62+
mode.
63+
type: integer
64+
batchMaxWait:
65+
description: |-
66+
The amount of time to wait before force writing the batch that hadn't reached the max size.
67+
Only used in batch mode.
68+
type: string
69+
batchThrottleBurst:
70+
description: |-
71+
Maximum number of requests sent at the same moment if ThrottleQPS was not utilized before.
72+
Only used in batch mode.
73+
type: integer
74+
batchThrottleEnable:
75+
description: Whether batching throttling is enabled. Only
76+
used in batch mode.
77+
type: boolean
78+
batchThrottleQPS:
79+
description: |-
80+
Maximum average number of batches per second. Only used in batch mode.
81+
This value is a floating point number, stored as a string (e.g. "3.1").
82+
type: string
83+
configSecretName:
84+
description: |-
85+
Name of a Kubernetes Secret that contains a kubeconfig formatted file that defines the
86+
audit webhook configuration.
87+
type: string
88+
initialBackoff:
89+
description: The amount of time to wait before retrying the
90+
first failed request.
91+
type: string
92+
mode:
93+
description: |-
94+
Strategy for sending audit events. Blocking indicates sending events should block server
95+
responses. Batch causes the backend to buffer and write events asynchronously.
96+
enum:
97+
- ""
98+
- batch
99+
- blocking
100+
- blocking-strict
101+
type: string
102+
truncateEnabled:
103+
description: Whether event and batch truncating is enabled.
104+
type: boolean
105+
truncateMaxBatchSize:
106+
description: |-
107+
Maximum size of the batch sent to the underlying backend. Actual serialized size can be
108+
several hundreds of bytes greater. If a batch exceeds this limit, it is split into several
109+
batches of smaller size.
110+
type: integer
111+
truncateMaxEventSize:
112+
description: |-
113+
Maximum size of the audit event sent to the underlying backend. If the size of an event
114+
is greater than this number, first request and response are removed, and if this doesn't
115+
reduce the size enough, event is discarded.
116+
type: integer
117+
version:
118+
description: API group and version used for serializing audit
119+
events written to webhook.
120+
type: string
121+
type: object
122+
type: object
52123
clusterDomain:
53124
type: string
54125
etcd:

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.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.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: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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 == nil || config.Webhook == nil {
35+
return deployment, nil
36+
}
37+
38+
return applyAuditWebhookConfiguration(deployment, *config.Webhook), nil
39+
}
40+
41+
func applyAuditWebhookConfiguration(deployment *appsv1.Deployment, config operatorv1alpha1.AuditWebhookSpec) *appsv1.Deployment {
42+
podSpec := deployment.Spec.Template.Spec
43+
44+
var extraArgs []string
45+
46+
if val := config.BatchBufferSize; val != 0 {
47+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-buffer-size=%d", val))
48+
}
49+
50+
if val := config.BatchMaxSize; val != 0 {
51+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-max-size=%d", val))
52+
}
53+
54+
if val := config.BatchMaxWait; val != nil {
55+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-max-wait=%v", val.String()))
56+
}
57+
58+
if val := config.BatchThrottleBurst; val != 0 {
59+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-throttle-burst=%d", val))
60+
}
61+
62+
if val := config.BatchThrottleEnable; val {
63+
extraArgs = append(extraArgs, "--audit-webhook-batch-throttle-enable")
64+
}
65+
66+
if val := config.BatchThrottleQPS; val != "" {
67+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-batch-throttle-qps=%s", val))
68+
}
69+
70+
if val := config.InitialBackoff; val != nil {
71+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-initial-backoff=%v", val.String()))
72+
}
73+
74+
if val := config.Mode; val != "" {
75+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-mode=%s", val))
76+
}
77+
78+
if val := config.TruncateEnabled; val {
79+
extraArgs = append(extraArgs, "--audit-webhook-truncate-enabled")
80+
}
81+
82+
if val := config.TruncateMaxBatchSize; val != 0 {
83+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-truncate-max-batch-size=%d", val))
84+
}
85+
86+
if val := config.TruncateMaxEventSize; val != 0 {
87+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-truncate-max-event-sizes=%d", val))
88+
}
89+
90+
if val := config.Version; val != "" {
91+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-version=%s", val))
92+
}
93+
94+
if val := config.ConfigSecretName; val != "" {
95+
volumeName := "audit-webhook-config"
96+
mountPath := "/etc/kcp/audit/webhook"
97+
98+
extraArgs = append(extraArgs, fmt.Sprintf("--audit-webhook-config-file=%s/kubeconfig", mountPath))
99+
podSpec.Containers[0].VolumeMounts = append(podSpec.Containers[0].VolumeMounts, corev1.VolumeMount{
100+
Name: volumeName,
101+
ReadOnly: true,
102+
MountPath: mountPath,
103+
})
104+
105+
deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, corev1.Volume{
106+
Name: volumeName,
107+
VolumeSource: corev1.VolumeSource{
108+
Secret: &corev1.SecretVolumeSource{
109+
SecretName: val,
110+
},
111+
},
112+
})
113+
}
114+
115+
podSpec.Containers[0].Args = append(podSpec.Containers[0].Args, extraArgs...)
116+
deployment.Spec.Template.Spec = podSpec
117+
118+
return deployment
119+
}

sdk/apis/operator/v1alpha1/shard_types.go

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

4297
// ShardStatus defines the observed state of Shard

0 commit comments

Comments
 (0)