Skip to content

Commit 3210390

Browse files
authored
fix: Avoid rollout due to updated auditpolicy handler (#1147)
Using the previously established pattern for retaining previous handler functionality for existing clusters. Requires #1144.
1 parent ca2deb9 commit 3210390

File tree

5 files changed

+357
-10
lines changed

5 files changed

+357
-10
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Taken from https://github.com/kubernetes/kubernetes/blob/v1.28.1/cluster/gce/gci/configure-helper.sh#L1101
2+
# Recommended in Kubernetes docs
3+
apiVersion: audit.k8s.io/v1
4+
kind: Policy
5+
rules:
6+
# The following requests were manually identified as high-volume and low-risk,
7+
# so drop them.
8+
- level: None
9+
users: ["system:kube-proxy"]
10+
verbs: ["watch"]
11+
resources:
12+
- group: "" # core
13+
resources: ["endpoints", "services", "services/status"]
14+
- level: None
15+
# Ingress controller reads 'configmaps/ingress-uid' through the unsecured port.
16+
# TODO(#46983): Change this to the ingress controller service account.
17+
users: ["system:unsecured"]
18+
namespaces: ["kube-system"]
19+
verbs: ["get"]
20+
resources:
21+
- group: "" # core
22+
resources: ["configmaps"]
23+
- level: None
24+
users: ["kubelet"] # legacy kubelet identity
25+
verbs: ["get"]
26+
resources:
27+
- group: "" # core
28+
resources: ["nodes", "nodes/status"]
29+
- level: None
30+
userGroups: ["system:nodes"]
31+
verbs: ["get"]
32+
resources:
33+
- group: "" # core
34+
resources: ["nodes", "nodes/status"]
35+
- level: None
36+
users:
37+
- system:kube-controller-manager
38+
- system:cloud-controller-manager
39+
- system:kube-scheduler
40+
- system:serviceaccount:kube-system:endpoint-controller
41+
verbs: ["get", "update"]
42+
namespaces: ["kube-system"]
43+
resources:
44+
- group: "" # core
45+
resources: ["endpoints"]
46+
- level: None
47+
users: ["system:apiserver"]
48+
verbs: ["get"]
49+
resources:
50+
- group: "" # core
51+
resources: ["namespaces", "namespaces/status", "namespaces/finalize"]
52+
- level: None
53+
users: ["cluster-autoscaler"]
54+
verbs: ["get", "update"]
55+
namespaces: ["kube-system"]
56+
resources:
57+
- group: "" # core
58+
resources: ["configmaps", "endpoints"]
59+
# Don't log HPA fetching metrics.
60+
- level: None
61+
users:
62+
- system:kube-controller-manager
63+
- system:cloud-controller-manager
64+
verbs: ["get", "list"]
65+
resources:
66+
- group: "metrics.k8s.io"
67+
68+
# Don't log these read-only URLs.
69+
- level: None
70+
nonResourceURLs:
71+
- /healthz*
72+
- /version
73+
- /swagger*
74+
75+
# Don't log events requests because of performance impact.
76+
- level: None
77+
resources:
78+
- group: "" # core
79+
resources: ["events"]
80+
81+
# node and pod status calls from nodes are high-volume and can be large, don't log responses for expected updates from nodes
82+
- level: Request
83+
users: ["kubelet", "system:node-problem-detector", "system:serviceaccount:kube-system:node-problem-detector"]
84+
verbs: ["update","patch"]
85+
resources:
86+
- group: "" # core
87+
resources: ["nodes/status", "pods/status"]
88+
omitStages:
89+
- "RequestReceived"
90+
- level: Request
91+
userGroups: ["system:nodes"]
92+
verbs: ["update","patch"]
93+
resources:
94+
- group: "" # core
95+
resources: ["nodes/status", "pods/status"]
96+
omitStages:
97+
- "RequestReceived"
98+
99+
# deletecollection calls can be large, don't log responses for expected namespace deletions
100+
- level: Request
101+
users: ["system:serviceaccount:kube-system:namespace-controller"]
102+
verbs: ["deletecollection"]
103+
omitStages:
104+
- "RequestReceived"
105+
106+
# Secrets, ConfigMaps, TokenRequest and TokenReviews can contain sensitive & binary data,
107+
# so only log at the Metadata level.
108+
- level: Metadata
109+
resources:
110+
- group: "" # core
111+
resources: ["secrets", "configmaps", "serviceaccounts/token"]
112+
- group: authentication.k8s.io
113+
resources: ["tokenreviews"]
114+
omitStages:
115+
- "RequestReceived"
116+
# Get responses can be large; skip them.
117+
- level: Request
118+
verbs: ["get", "list", "watch"]
119+
resources:
120+
- group: "" # core
121+
- group: "admissionregistration.k8s.io"
122+
- group: "apiextensions.k8s.io"
123+
- group: "apiregistration.k8s.io"
124+
- group: "apps"
125+
- group: "authentication.k8s.io"
126+
- group: "authorization.k8s.io"
127+
- group: "autoscaling"
128+
- group: "batch"
129+
- group: "certificates.k8s.io"
130+
- group: "extensions"
131+
- group: "metrics.k8s.io"
132+
- group: "networking.k8s.io"
133+
- group: "node.k8s.io"
134+
- group: "policy"
135+
- group: "rbac.authorization.k8s.io"
136+
- group: "scheduling.k8s.io"
137+
- group: "storage.k8s.io"
138+
omitStages:
139+
- "RequestReceived"
140+
# Default level for known APIs
141+
- level: RequestResponse
142+
resources:
143+
- group: "" # core
144+
- group: "admissionregistration.k8s.io"
145+
- group: "apiextensions.k8s.io"
146+
- group: "apiregistration.k8s.io"
147+
- group: "apps"
148+
- group: "authentication.k8s.io"
149+
- group: "authorization.k8s.io"
150+
- group: "autoscaling"
151+
- group: "batch"
152+
- group: "certificates.k8s.io"
153+
- group: "extensions"
154+
- group: "metrics.k8s.io"
155+
- group: "networking.k8s.io"
156+
- group: "node.k8s.io"
157+
- group: "policy"
158+
- group: "rbac.authorization.k8s.io"
159+
- group: "scheduling.k8s.io"
160+
- group: "storage.k8s.io"
161+
omitStages:
162+
- "RequestReceived"
163+
# Default level for all other requests.
164+
- level: Metadata
165+
omitStages:
166+
- "RequestReceived"
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2025 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package auditpolicy
5+
6+
import (
7+
"context"
8+
_ "embed"
9+
10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
13+
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
14+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
15+
ctrl "sigs.k8s.io/controller-runtime"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
20+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
21+
)
22+
23+
type auditPolicyPatchHandler struct{}
24+
25+
//go:embed embedded/apiserver-audit-policy.yaml
26+
var auditPolicy string
27+
28+
const auditPolicyPath = "/etc/kubernetes/audit-policy/apiserver-audit-policy.yaml"
29+
30+
func NewPatch() *auditPolicyPatchHandler {
31+
return &auditPolicyPatchHandler{}
32+
}
33+
34+
func (h *auditPolicyPatchHandler) Mutate(
35+
ctx context.Context,
36+
obj *unstructured.Unstructured,
37+
vars map[string]apiextensionsv1.JSON,
38+
holderRef runtimehooksv1.HolderReference,
39+
_ client.ObjectKey,
40+
_ mutation.ClusterGetter,
41+
) error {
42+
log := ctrl.LoggerFrom(ctx).WithValues(
43+
"holderRef", holderRef,
44+
)
45+
46+
return patches.MutateIfApplicable(
47+
obj, vars, &holderRef, selectors.ControlPlane(), log,
48+
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
49+
log.WithValues(
50+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
51+
"patchedObjectName", client.ObjectKeyFromObject(obj),
52+
).Info("adding files and updating API server extra args in kubeadm config spec")
53+
54+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files = append(
55+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files,
56+
bootstrapv1.File{
57+
Path: auditPolicyPath,
58+
Permissions: "0600",
59+
Content: auditPolicy,
60+
},
61+
)
62+
63+
if obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
64+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
65+
}
66+
apiServer := &obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer
67+
if apiServer.ExtraArgs == nil {
68+
apiServer.ExtraArgs = make(map[string]string, 5)
69+
}
70+
71+
apiServer.ExtraArgs["audit-log-path"] = "/var/log/audit/kube-apiserver-audit.log"
72+
apiServer.ExtraArgs["audit-log-maxage"] = "30"
73+
apiServer.ExtraArgs["audit-log-maxbackup"] = "10"
74+
apiServer.ExtraArgs["audit-log-maxsize"] = "100"
75+
apiServer.ExtraArgs["audit-policy-file"] = auditPolicyPath
76+
77+
if apiServer.ExtraVolumes == nil {
78+
apiServer.ExtraVolumes = make([]bootstrapv1.HostPathMount, 0, 2)
79+
}
80+
81+
apiServer.ExtraVolumes = append(
82+
apiServer.ExtraVolumes,
83+
bootstrapv1.HostPathMount{
84+
Name: "audit-policy",
85+
HostPath: "/etc/kubernetes/audit-policy/",
86+
MountPath: "/etc/kubernetes/audit-policy/",
87+
ReadOnly: true,
88+
},
89+
bootstrapv1.HostPathMount{
90+
Name: "audit-logs",
91+
HostPath: "/var/log/kubernetes/audit",
92+
MountPath: "/var/log/audit/",
93+
},
94+
)
95+
96+
return nil
97+
},
98+
)
99+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2025 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package auditpolicy
5+
6+
import (
7+
"testing"
8+
9+
. "github.com/onsi/ginkgo/v2"
10+
"github.com/onsi/gomega"
11+
12+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/test/helpers"
16+
)
17+
18+
func TestAuditPolicyPatch(t *testing.T) {
19+
gomega.RegisterFailHandler(Fail)
20+
RunSpecs(t, "Audit Policy mutator suite")
21+
}
22+
23+
var _ = Describe("Generate Audit Policy patches", func() {
24+
patchGenerator := func() mutation.GeneratePatches {
25+
return mutation.NewMetaGeneratePatchesHandler("", helpers.TestEnv.Client, NewPatch()).(mutation.GeneratePatches)
26+
}
27+
28+
testDefs := []capitest.PatchTestDef{
29+
{
30+
Name: "unset variable",
31+
},
32+
{
33+
Name: "auditpolicy set for KubeadmControlPlaneTemplate",
34+
RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""),
35+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
36+
Operation: "add",
37+
Path: "/spec/template/spec/kubeadmConfigSpec/files",
38+
ValueMatcher: gomega.ContainElements(
39+
gomega.HaveKeyWithValue(
40+
"path", "/etc/kubernetes/audit-policy/apiserver-audit-policy.yaml",
41+
),
42+
),
43+
}, {
44+
Operation: "add",
45+
Path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration",
46+
ValueMatcher: gomega.HaveKeyWithValue(
47+
"apiServer",
48+
gomega.SatisfyAll(
49+
gomega.HaveKeyWithValue(
50+
"extraArgs",
51+
map[string]interface{}{
52+
"audit-log-maxbackup": "10",
53+
"audit-log-maxsize": "100",
54+
"audit-log-path": "/var/log/audit/kube-apiserver-audit.log",
55+
"audit-policy-file": "/etc/kubernetes/audit-policy/apiserver-audit-policy.yaml",
56+
"audit-log-maxage": "30",
57+
},
58+
),
59+
gomega.HaveKeyWithValue(
60+
"extraVolumes",
61+
[]interface{}{
62+
map[string]interface{}{
63+
"hostPath": "/etc/kubernetes/audit-policy/",
64+
"mountPath": "/etc/kubernetes/audit-policy/",
65+
"name": "audit-policy",
66+
"readOnly": true,
67+
},
68+
map[string]interface{}{
69+
"name": "audit-logs",
70+
"hostPath": "/var/log/kubernetes/audit",
71+
"mountPath": "/var/log/audit/",
72+
},
73+
},
74+
),
75+
),
76+
),
77+
}},
78+
},
79+
}
80+
81+
// create test node for each case
82+
for testIdx := range testDefs {
83+
tt := testDefs[testIdx]
84+
It(tt.Name, func() {
85+
capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt)
86+
})
87+
}
88+
})

pkg/handlers/v3/generic/mutation/handlers.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 Nutanix. All rights reserved.
1+
// Copyright 2025 Nutanix. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
package mutation
@@ -8,7 +8,6 @@ import (
88

99
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
1010
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/cni/calico"
11-
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/auditpolicy"
1211
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/autorenewcerts"
1312
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/containerdapplypatchesandrestart"
1413
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/containerdmetrics"
@@ -23,6 +22,7 @@ import (
2322
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors"
2423
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/taints"
2524
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users"
25+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/v3/generic/mutation/auditpolicy"
2626
)
2727

2828
// MetaMutators returns all generic patch handlers.
@@ -59,17 +59,11 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator {
5959
func ControlPlaneMetaMutators() []mutation.MetaMutator {
6060
return []mutation.MetaMutator{
6161
taints.NewControlPlanePatch(),
62-
// Intentionally not include this patch as it was not available in previous version the hook,
63-
// and it uses an API is on by default, which causes a rollout of all Machines in all managed clusters.
64-
// noderegistration.NewControlPlanePatch(),
6562
}
6663
}
6764

6865
func WorkerMetaMutators() []mutation.MetaMutator {
6966
return []mutation.MetaMutator{
7067
taints.NewWorkerPatch(),
71-
// Intentionally not include this patch as it was not available in previous version the hook,
72-
// and it uses an API is on by default, which causes a rollout of all Machines in all managed clusters.
73-
// noderegistration.NewControlPlanePatch(),
7468
}
7569
}

0 commit comments

Comments
 (0)