k8s.io/apiserver@v0.31.1/pkg/server/egressselector/config_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 egressselector 18 19 import ( 20 "fmt" 21 "os" 22 "reflect" 23 "testing" 24 25 utiltesting "k8s.io/client-go/util/testing" 26 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apiserver/pkg/apis/apiserver" 29 ) 30 31 func strptr(s string) *string { 32 return &s 33 } 34 35 func TestReadEgressSelectorConfiguration(t *testing.T) { 36 testcases := []struct { 37 name string 38 contents string 39 createFile bool 40 expectedResult *apiserver.EgressSelectorConfiguration 41 expectedError *string 42 }{ 43 { 44 name: "empty", 45 createFile: true, 46 contents: ``, 47 expectedResult: nil, 48 expectedError: strptr("invalid service configuration object \"\""), 49 }, 50 { 51 name: "absent", 52 createFile: false, 53 contents: ``, 54 expectedResult: nil, 55 expectedError: strptr("unable to read egress selector configuration from \"test-egress-selector-config-absent\" [open test-egress-selector-config-absent: no such file or directory]"), 56 }, 57 { 58 name: "v1beta1", 59 createFile: true, 60 contents: ` 61 apiVersion: apiserver.k8s.io/v1beta1 62 kind: EgressSelectorConfiguration 63 egressSelections: 64 - name: "cluster" 65 connection: 66 proxyProtocol: "HTTPConnect" 67 transport: 68 tcp: 69 url: "https://127.0.0.1:8131" 70 tlsConfig: 71 caBundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt" 72 clientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key" 73 clientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt" 74 - name: "controlplane" 75 connection: 76 proxyProtocol: "HTTPConnect" 77 transport: 78 tcp: 79 url: "https://127.0.0.1:8132" 80 tlsConfig: 81 caBundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt" 82 clientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key" 83 clientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt" 84 - name: "etcd" 85 connection: 86 proxyProtocol: "Direct" 87 `, 88 expectedResult: &apiserver.EgressSelectorConfiguration{ 89 TypeMeta: metav1.TypeMeta{ 90 Kind: "", 91 APIVersion: "", 92 }, 93 EgressSelections: []apiserver.EgressSelection{ 94 { 95 Name: "cluster", 96 Connection: apiserver.Connection{ 97 ProxyProtocol: "HTTPConnect", 98 Transport: &apiserver.Transport{ 99 TCP: &apiserver.TCPTransport{ 100 URL: "https://127.0.0.1:8131", 101 102 TLSConfig: &apiserver.TLSConfig{ 103 CABundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt", 104 ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key", 105 ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt", 106 }, 107 }, 108 }, 109 }, 110 }, 111 { 112 Name: "controlplane", 113 Connection: apiserver.Connection{ 114 ProxyProtocol: "HTTPConnect", 115 Transport: &apiserver.Transport{ 116 TCP: &apiserver.TCPTransport{ 117 URL: "https://127.0.0.1:8132", 118 TLSConfig: &apiserver.TLSConfig{ 119 CABundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt", 120 ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key", 121 ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt", 122 }, 123 }, 124 }, 125 }, 126 }, 127 { 128 Name: "etcd", 129 Connection: apiserver.Connection{ 130 ProxyProtocol: "Direct", 131 }, 132 }, 133 }, 134 }, 135 expectedError: nil, 136 }, 137 { 138 name: "v1beta1 using deprecated 'master' type", 139 createFile: true, 140 contents: ` 141 apiVersion: apiserver.k8s.io/v1beta1 142 kind: EgressSelectorConfiguration 143 egressSelections: 144 - name: "cluster" 145 connection: 146 proxyProtocol: "HTTPConnect" 147 transport: 148 tcp: 149 url: "https://127.0.0.1:8131" 150 tlsConfig: 151 caBundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt" 152 clientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key" 153 clientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt" 154 - name: "master" 155 connection: 156 proxyProtocol: "HTTPConnect" 157 transport: 158 tcp: 159 url: "https://127.0.0.1:8132" 160 tlsConfig: 161 caBundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt" 162 clientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key" 163 clientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt" 164 - name: "etcd" 165 connection: 166 proxyProtocol: "Direct" 167 `, 168 expectedResult: &apiserver.EgressSelectorConfiguration{ 169 TypeMeta: metav1.TypeMeta{ 170 Kind: "", 171 APIVersion: "", 172 }, 173 EgressSelections: []apiserver.EgressSelection{ 174 { 175 Name: "cluster", 176 Connection: apiserver.Connection{ 177 ProxyProtocol: "HTTPConnect", 178 Transport: &apiserver.Transport{ 179 TCP: &apiserver.TCPTransport{ 180 URL: "https://127.0.0.1:8131", 181 182 TLSConfig: &apiserver.TLSConfig{ 183 CABundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt", 184 ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key", 185 ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt", 186 }, 187 }, 188 }, 189 }, 190 }, 191 { 192 Name: "controlplane", 193 Connection: apiserver.Connection{ 194 ProxyProtocol: "HTTPConnect", 195 Transport: &apiserver.Transport{ 196 TCP: &apiserver.TCPTransport{ 197 URL: "https://127.0.0.1:8132", 198 TLSConfig: &apiserver.TLSConfig{ 199 CABundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt", 200 ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key", 201 ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt", 202 }, 203 }, 204 }, 205 }, 206 }, 207 { 208 Name: "etcd", 209 Connection: apiserver.Connection{ 210 ProxyProtocol: "Direct", 211 }, 212 }, 213 }, 214 }, 215 expectedError: nil, 216 }, 217 { 218 name: "wrong_type", 219 createFile: true, 220 contents: ` 221 apiVersion: apps/v1 222 kind: DaemonSet 223 metadata: 224 labels: 225 addonmanager.kubernetes.io/mode: Reconcile 226 k8s-app: konnectivity-agent 227 namespace: kube-system 228 name: proxy-agent 229 spec: 230 selector: 231 matchLabels: 232 k8s-app: konnectivity-agent 233 updateStrategy: 234 type: RollingUpdate 235 template: 236 metadata: 237 labels: 238 k8s-app: proxy-agent 239 spec: 240 priorityClassName: system-cluster-critical 241 # Necessary to reboot node 242 hostPID: true 243 volumes: 244 - name: pki 245 hostPath: 246 path: /etc/srv/kubernetes/pki/konnectivity-agent 247 containers: 248 - image: registry.k8s.io/proxy-agent:v0.0.3 249 name: proxy-agent 250 command: ["/proxy-agent"] 251 args: ["--caCert=/etc/srv/kubernetes/pki/proxy-agent/ca.crt", "--agentCert=/etc/srv/kubernetes/pki/proxy-agent/client.crt", "--agentKey=/etc/srv/kubernetes/pki/proxy-agent/client.key", "--proxyServerHost=127.0.0.1", "--proxyServerPort=8132"] 252 securityContext: 253 capabilities: 254 add: ["SYS_BOOT"] 255 env: 256 - name: wrong-type 257 valueFrom: 258 fieldRef: 259 fieldPath: metadata.name 260 - name: kube-system 261 valueFrom: 262 fieldRef: 263 fieldPath: metadata.namespace 264 resources: 265 limits: 266 cpu: 50m 267 memory: 30Mi 268 volumeMounts: 269 - name: pki 270 mountPath: /etc/srv/kubernetes/pki/konnectivity-agent 271 `, 272 expectedResult: nil, 273 expectedError: strptr("invalid service configuration object \"DaemonSet\""), 274 }, 275 } 276 277 for _, tc := range testcases { 278 t.Run(tc.name, func(t *testing.T) { 279 proxyConfig := fmt.Sprintf("test-egress-selector-config-%s", tc.name) 280 if tc.createFile { 281 f, err := os.CreateTemp("", proxyConfig) 282 if err != nil { 283 t.Fatal(err) 284 } 285 defer utiltesting.CloseAndRemove(t, f) 286 if err := os.WriteFile(f.Name(), []byte(tc.contents), os.FileMode(0755)); err != nil { 287 t.Fatal(err) 288 } 289 proxyConfig = f.Name() 290 } 291 config, err := ReadEgressSelectorConfiguration(proxyConfig) 292 if err == nil && tc.expectedError != nil { 293 t.Errorf("calling ReadEgressSelectorConfiguration expected error: %s, did not get it", *tc.expectedError) 294 } 295 if err != nil && tc.expectedError == nil { 296 t.Errorf("unexpected error calling ReadEgressSelectorConfiguration got: %#v", err) 297 } 298 if err != nil && tc.expectedError != nil && err.Error() != *tc.expectedError { 299 t.Errorf("calling ReadEgressSelectorConfiguration expected error: %s, got %#v", *tc.expectedError, err) 300 } 301 if !reflect.DeepEqual(config, tc.expectedResult) { 302 t.Errorf("problem with configuration returned from ReadEgressSelectorConfiguration expected: %#v, got: %#v", tc.expectedResult, config) 303 } 304 }) 305 } 306 } 307 308 func TestValidateEgressSelectorConfiguration(t *testing.T) { 309 testcases := []struct { 310 name string 311 expectError bool 312 contents *apiserver.EgressSelectorConfiguration 313 }{ 314 { 315 name: "direct-valid", 316 expectError: false, 317 contents: &apiserver.EgressSelectorConfiguration{ 318 TypeMeta: metav1.TypeMeta{ 319 Kind: "", 320 APIVersion: "", 321 }, 322 EgressSelections: []apiserver.EgressSelection{ 323 { 324 Name: "controlplane", 325 Connection: apiserver.Connection{ 326 ProxyProtocol: apiserver.ProtocolDirect, 327 }, 328 }, 329 }, 330 }, 331 }, 332 { 333 name: "direct-invalid-transport", 334 expectError: true, 335 contents: &apiserver.EgressSelectorConfiguration{ 336 TypeMeta: metav1.TypeMeta{ 337 Kind: "", 338 APIVersion: "", 339 }, 340 EgressSelections: []apiserver.EgressSelection{ 341 { 342 Name: "controlplane", 343 Connection: apiserver.Connection{ 344 ProxyProtocol: apiserver.ProtocolDirect, 345 Transport: &apiserver.Transport{}, 346 }, 347 }, 348 }, 349 }, 350 }, 351 { 352 name: "httpconnect-no-https", 353 expectError: false, 354 contents: &apiserver.EgressSelectorConfiguration{ 355 TypeMeta: metav1.TypeMeta{ 356 Kind: "", 357 APIVersion: "", 358 }, 359 EgressSelections: []apiserver.EgressSelection{ 360 { 361 Name: "cluster", 362 Connection: apiserver.Connection{ 363 ProxyProtocol: apiserver.ProtocolHTTPConnect, 364 Transport: &apiserver.Transport{ 365 TCP: &apiserver.TCPTransport{ 366 URL: "http://127.0.0.1:8131", 367 }, 368 }, 369 }, 370 }, 371 }, 372 }, 373 }, 374 { 375 name: "httpconnect-https-no-cert-error", 376 expectError: true, 377 contents: &apiserver.EgressSelectorConfiguration{ 378 TypeMeta: metav1.TypeMeta{ 379 Kind: "", 380 APIVersion: "", 381 }, 382 EgressSelections: []apiserver.EgressSelection{ 383 { 384 Name: "cluster", 385 Connection: apiserver.Connection{ 386 ProxyProtocol: apiserver.ProtocolHTTPConnect, 387 Transport: &apiserver.Transport{ 388 TCP: &apiserver.TCPTransport{ 389 URL: "https://127.0.0.1:8131", 390 }, 391 }, 392 }, 393 }, 394 }, 395 }, 396 }, 397 { 398 name: "httpconnect-tcp-uds-both-set", 399 expectError: true, 400 contents: &apiserver.EgressSelectorConfiguration{ 401 TypeMeta: metav1.TypeMeta{ 402 Kind: "", 403 APIVersion: "", 404 }, 405 EgressSelections: []apiserver.EgressSelection{ 406 { 407 Name: "cluster", 408 Connection: apiserver.Connection{ 409 ProxyProtocol: apiserver.ProtocolHTTPConnect, 410 Transport: &apiserver.Transport{ 411 TCP: &apiserver.TCPTransport{ 412 URL: "http://127.0.0.1:8131", 413 }, 414 UDS: &apiserver.UDSTransport{ 415 UDSName: "/etc/srv/kubernetes/konnectivity/konnectivity-server.socket", 416 }, 417 }, 418 }, 419 }, 420 }, 421 }, 422 }, 423 { 424 name: "httpconnect-uds", 425 expectError: false, 426 contents: &apiserver.EgressSelectorConfiguration{ 427 TypeMeta: metav1.TypeMeta{ 428 Kind: "", 429 APIVersion: "", 430 }, 431 EgressSelections: []apiserver.EgressSelection{ 432 { 433 Name: "cluster", 434 Connection: apiserver.Connection{ 435 ProxyProtocol: apiserver.ProtocolHTTPConnect, 436 Transport: &apiserver.Transport{ 437 UDS: &apiserver.UDSTransport{ 438 UDSName: "/etc/srv/kubernetes/konnectivity/konnectivity-server.socket", 439 }, 440 }, 441 }, 442 }, 443 }, 444 }, 445 }, 446 { 447 name: "grpc-https-invalid", 448 expectError: true, 449 contents: &apiserver.EgressSelectorConfiguration{ 450 TypeMeta: metav1.TypeMeta{ 451 Kind: "", 452 APIVersion: "", 453 }, 454 EgressSelections: []apiserver.EgressSelection{ 455 { 456 Name: "cluster", 457 Connection: apiserver.Connection{ 458 ProxyProtocol: apiserver.ProtocolGRPC, 459 Transport: &apiserver.Transport{ 460 TCP: &apiserver.TCPTransport{ 461 URL: "http://127.0.0.1:8131", 462 TLSConfig: &apiserver.TLSConfig{ 463 CABundle: "", 464 ClientKey: "", 465 ClientCert: "", 466 }, 467 }, 468 }, 469 }, 470 }, 471 }, 472 }, 473 }, 474 { 475 name: "grpc-uds", 476 expectError: false, 477 contents: &apiserver.EgressSelectorConfiguration{ 478 TypeMeta: metav1.TypeMeta{ 479 Kind: "", 480 APIVersion: "", 481 }, 482 EgressSelections: []apiserver.EgressSelection{ 483 { 484 Name: "cluster", 485 Connection: apiserver.Connection{ 486 ProxyProtocol: apiserver.ProtocolGRPC, 487 Transport: &apiserver.Transport{ 488 UDS: &apiserver.UDSTransport{ 489 UDSName: "/etc/srv/kubernetes/konnectivity/konnectivity-server.socket", 490 }, 491 }, 492 }, 493 }, 494 }, 495 }, 496 }, 497 { 498 name: "invalid egress selection name", 499 expectError: true, 500 contents: &apiserver.EgressSelectorConfiguration{ 501 TypeMeta: metav1.TypeMeta{ 502 Kind: "", 503 APIVersion: "", 504 }, 505 EgressSelections: []apiserver.EgressSelection{ 506 { 507 Name: "invalid", 508 Connection: apiserver.Connection{ 509 ProxyProtocol: apiserver.ProtocolDirect, 510 }, 511 }, 512 }, 513 }, 514 }, 515 { 516 name: "duplicate egress selections configured", 517 expectError: true, 518 contents: &apiserver.EgressSelectorConfiguration{ 519 TypeMeta: metav1.TypeMeta{ 520 Kind: "", 521 APIVersion: "", 522 }, 523 EgressSelections: []apiserver.EgressSelection{ 524 { 525 Name: "controlplane", 526 Connection: apiserver.Connection{ 527 ProxyProtocol: apiserver.ProtocolDirect, 528 }, 529 }, 530 { 531 Name: "controlplane", 532 Connection: apiserver.Connection{ 533 ProxyProtocol: apiserver.ProtocolDirect, 534 }, 535 }, 536 }, 537 }, 538 }, 539 } 540 541 for _, tc := range testcases { 542 t.Run(tc.name, func(t *testing.T) { 543 errs := ValidateEgressSelectorConfiguration(tc.contents) 544 if !tc.expectError && len(errs) != 0 { 545 t.Errorf("Calling ValidateEgressSelectorConfiguration expected no error, got %v", errs) 546 } else if tc.expectError && len(errs) == 0 { 547 t.Errorf("Calling ValidateEgressSelectorConfiguration expected error, got no error") 548 } 549 }) 550 } 551 }