k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/gce/gci/audit_policy_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes 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 gci 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "testing" 24 25 "k8s.io/apiserver/pkg/apis/audit" 26 auditinstall "k8s.io/apiserver/pkg/apis/audit/install" 27 auditpkg "k8s.io/apiserver/pkg/audit" 28 auditpolicy "k8s.io/apiserver/pkg/audit/policy" 29 "k8s.io/apiserver/pkg/authentication/serviceaccount" 30 "k8s.io/apiserver/pkg/authentication/user" 31 "k8s.io/apiserver/pkg/authorization/authorizer" 32 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 func init() { 38 // Register audit scheme to parse audit config. 39 auditinstall.Install(auditpkg.Scheme) 40 } 41 42 func TestCreateMasterAuditPolicy(t *testing.T) { 43 baseDir, err := os.MkdirTemp("", "configure-helper-test") // cleaned up by c.tearDown() 44 require.NoError(t, err, "Failed to create temp directory") 45 46 policyFile := filepath.Join(baseDir, "audit_policy.yaml") 47 c := ManifestTestCase{ 48 t: t, 49 kubeHome: baseDir, 50 manifestFuncName: fmt.Sprintf("create-master-audit-policy %s", policyFile), 51 } 52 defer c.tearDown() 53 54 // Initialize required environment variables. 55 c.mustInvokeFunc( 56 kubeAPIServerEnv{KubeHome: c.kubeHome}, 57 []string{"configure-helper.sh"}, 58 "base.template", 59 "testdata/kube-apiserver/base.template", 60 ) 61 62 policy, err := auditpolicy.LoadPolicyFromFile(policyFile) 63 require.NoError(t, err, "Failed to load generated policy.") 64 65 // Users for test cases 66 var ( 67 anonymous = newUserInfo(user.Anonymous, user.AllUnauthenticated) 68 kubeproxy = newUserInfo(user.KubeProxy, user.AllAuthenticated) 69 ingress = newUserInfo("system:unsecured", user.AllAuthenticated, user.SystemPrivilegedGroup) 70 kubelet = newUserInfo("kubelet", user.AllAuthenticated, user.NodesGroup) 71 node = newUserInfo("system:node:node-123", user.AllAuthenticated, user.NodesGroup) 72 controller = newUserInfo(user.KubeControllerManager, user.AllAuthenticated) 73 scheduler = newUserInfo(user.KubeScheduler, user.AllAuthenticated) 74 apiserver = newUserInfo(user.APIServerUser, user.SystemPrivilegedGroup) 75 autoscaler = newUserInfo("cluster-autoscaler", user.AllAuthenticated) 76 npd = newUserInfo("system:node-problem-detector", user.AllAuthenticated) 77 npdSA = serviceaccount.UserInfo("kube-system", "node-problem-detector", "") 78 namespaceController = serviceaccount.UserInfo("kube-system", "namespace-controller", "") 79 endpointController = serviceaccount.UserInfo("kube-system", "endpoint-controller", "") 80 defaultSA = serviceaccount.UserInfo("default", "default", "") 81 82 allUsers = []user.Info{anonymous, kubeproxy, ingress, kubelet, node, controller, scheduler, apiserver, autoscaler, npd, npdSA, namespaceController, endpointController, defaultSA} 83 ) 84 85 // Resources for test cases 86 var ( 87 nodes = resource("nodes") 88 nodeStatus = resource("nodes", "", "", "status") 89 endpoints = resource("endpoints", "default") 90 sysEndpoints = resource("endpoints", "kube-system") 91 services = resource("services", "default") 92 serviceStatus = resource("services", "default", "", "status") 93 configmaps = resource("configmaps", "default") 94 sysConfigmaps = resource("configmaps", "kube-system") 95 namespaces = resource("namespaces") 96 namespaceStatus = resource("namespaces", "", "", "status") 97 namespaceFinal = resource("namespaces", "", "", "finalize") 98 podMetrics = resource("podmetrics", "default", "metrics.k8s.io") 99 nodeMetrics = resource("nodemetrics", "", "metrics.k8s.io") 100 pods = resource("pods", "default") 101 podStatus = resource("pods", "default", "", "status") 102 secrets = resource("secrets", "default") 103 tokenReviews = resource("tokenreviews", "", "authentication.k8s.io") 104 deployments = resource("deployments", "default", "apps") 105 clusterRoles = resource("clusterroles", "", "rbac.authorization.k8s.io") 106 events = resource("events", "default") 107 foobars = resource("foos", "default", "example.com") 108 foobarbaz = resource("foos", "default", "example.com", "baz") 109 ) 110 111 // Aliases 112 const ( 113 none = audit.LevelNone 114 metadata = audit.LevelMetadata 115 request = audit.LevelRequest 116 response = audit.LevelRequestResponse 117 ) 118 119 at := auditTester{ 120 T: t, 121 evaluator: auditpolicy.NewPolicyRuleEvaluator(policy), 122 } 123 124 at.testResources(none, kubeproxy, "watch", endpoints, sysEndpoints, services, serviceStatus) 125 at.testResources(request, kubeproxy, "watch", nodes, pods) 126 127 at.testResources(none, ingress, "get", sysConfigmaps) 128 at.testResources(metadata, ingress, "get", configmaps) 129 130 at.testResources(none, kubelet, node, "get", nodes, nodeStatus) 131 at.testResources(metadata, kubelet, node, "get", sysConfigmaps, secrets) 132 at.testResources(response, kubelet, node, "create", deployments, pods) 133 134 at.testResources(none, controller, scheduler, endpointController, "get", "update", sysEndpoints) 135 at.testResources(request, controller, scheduler, endpointController, "get", endpoints) 136 at.testResources(response, controller, scheduler, endpointController, "update", endpoints) 137 138 at.testResources(none, apiserver, "get", namespaces, namespaceStatus, namespaceFinal) 139 at.testResources(metadata, apiserver, "get", "create", "update", sysConfigmaps, secrets) 140 141 at.testResources(none, autoscaler, "get", "update", sysConfigmaps, sysEndpoints) 142 at.testResources(metadata, autoscaler, "get", "update", configmaps) 143 at.testResources(response, autoscaler, "update", endpoints) 144 145 at.testResources(none, controller, "get", "list", podMetrics, nodeMetrics) 146 147 at.testNonResources(none, allUsers, "/healthz", "/healthz/etcd", "/swagger-2.0.0.json", "/swagger-2.0.0.pb-v1.gz", "/version") 148 at.testNonResources(metadata, allUsers, "/logs", "/openapi/v2", "/apis/policy", "/metrics", "/api") 149 150 at.testResources(none, node, apiserver, defaultSA, anonymous, "get", "list", "create", "patch", "update", "delete", events) 151 152 at.testResources(request, kubelet, node, npd, npdSA, "update", "patch", nodeStatus, podStatus) 153 154 at.testResources(request, namespaceController, "deletecollection", pods, namespaces) 155 156 at.testResources(metadata, defaultSA, anonymous, npd, namespaceController, "get", "create", "update", secrets, configmaps, sysConfigmaps, tokenReviews) 157 at.testResources(request, defaultSA, anonymous, npd, namespaceController, "get", "list", "watch", sysEndpoints, podMetrics, pods, clusterRoles, deployments) 158 at.testResources(response, defaultSA, anonymous, npd, namespaceController, "create", "update", "patch", "delete", sysEndpoints, podMetrics, pods, clusterRoles, deployments) 159 160 at.testResources(metadata, defaultSA, anonymous, npd, namespaceController, "get", "list", "watch", "create", "update", "patch", "delete", foobars, foobarbaz) 161 } 162 163 type auditTester struct { 164 *testing.T 165 evaluator auditpkg.PolicyRuleEvaluator 166 } 167 168 func (t *auditTester) testResources(level audit.Level, usrVerbRes ...interface{}) { 169 verbs := []string{} 170 users := []user.Info{} 171 resources := []Resource{} 172 for _, arg := range usrVerbRes { 173 switch v := arg.(type) { 174 case string: 175 verbs = append(verbs, v) 176 case user.Info: 177 users = append(users, v) 178 case Resource: 179 resources = append(resources, v) 180 default: 181 t.Fatalf("Invalid test argument: %+v", arg) 182 } 183 } 184 require.NotEmpty(t, verbs, "testcases must have a verb") 185 require.NotEmpty(t, users, "testcases must have a user") 186 require.NotEmpty(t, resources, "resource testcases must have a resource") 187 188 for _, usr := range users { 189 for _, verb := range verbs { 190 for _, res := range resources { 191 attrs := &authorizer.AttributesRecord{ 192 User: usr, 193 Verb: verb, 194 Namespace: res.Namespace, 195 APIGroup: res.Group, 196 APIVersion: "v1", 197 Resource: res.Resource, 198 Subresource: res.Subresource, 199 ResourceRequest: true, 200 } 201 t.expectLevel(level, attrs) 202 } 203 } 204 } 205 } 206 207 func (t *auditTester) testNonResources(level audit.Level, users []user.Info, paths ...string) { 208 for _, usr := range users { 209 for _, verb := range []string{"get", "post"} { 210 for _, path := range paths { 211 attrs := &authorizer.AttributesRecord{ 212 User: usr, 213 Verb: verb, 214 ResourceRequest: false, 215 Path: path, 216 } 217 t.expectLevel(level, attrs) 218 } 219 } 220 } 221 } 222 223 func (t *auditTester) expectLevel(expected audit.Level, attrs authorizer.Attributes) { 224 obj := attrs.GetPath() 225 if attrs.IsResourceRequest() { 226 obj = attrs.GetResource() 227 if attrs.GetNamespace() != "" { 228 obj = obj + ":" + attrs.GetNamespace() 229 } 230 } 231 name := fmt.Sprintf("%s.%s.%s", attrs.GetUser().GetName(), attrs.GetVerb(), obj) 232 evaluator := t.evaluator 233 t.Run(name, func(t *testing.T) { 234 auditConfig := evaluator.EvaluatePolicyRule(attrs) 235 assert.Equal(t, expected, auditConfig.Level) 236 if auditConfig.Level != audit.LevelNone { 237 assert.ElementsMatch(t, auditConfig.OmitStages, []audit.Stage{audit.StageRequestReceived}) 238 } 239 }) 240 } 241 242 func newUserInfo(name string, groups ...string) user.Info { 243 return &user.DefaultInfo{ 244 Name: name, 245 Groups: groups, 246 } 247 } 248 249 type Resource struct { 250 Group, Resource, Subresource, Namespace string 251 } 252 253 func resource(kind string, nsGroupSub ...string) Resource { 254 res := Resource{Resource: kind} 255 if len(nsGroupSub) > 0 { 256 res.Namespace = nsGroupSub[0] 257 } 258 if len(nsGroupSub) > 1 { 259 res.Group = nsGroupSub[1] 260 } 261 if len(nsGroupSub) > 2 { 262 res.Subresource = nsGroupSub[2] 263 } 264 return res 265 }