sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/scope/session_test.go (about) 1 /* 2 Copyright 2020 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 scope 18 19 import ( 20 "context" 21 "testing" 22 23 . "github.com/onsi/gomega" 24 "github.com/pkg/errors" 25 corev1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/klog/v2/klogr" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 "sigs.k8s.io/controller-runtime/pkg/client/fake" 32 33 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 34 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/identity" 35 "sigs.k8s.io/cluster-api-provider-aws/util/system" 36 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 37 ) 38 39 func TestIsClusterPermittedToUsePrincipal(t *testing.T) { 40 testCases := []struct { 41 name string 42 clusterNamespace string 43 allowedNs *infrav1.AllowedNamespaces 44 setup func(*testing.T, client.Client) 45 expectedResult bool 46 expectErr bool 47 }{ 48 { 49 name: "All clusters are permitted to use identity if allowedNamespaces is empty", 50 clusterNamespace: "default", 51 allowedNs: &infrav1.AllowedNamespaces{}, 52 expectedResult: true, 53 expectErr: false, 54 }, 55 { 56 name: "No clusters are permitted to use identity if allowedNamespaces is nil", 57 clusterNamespace: "default", 58 allowedNs: nil, 59 expectedResult: false, 60 expectErr: false, 61 }, 62 { 63 name: "A namespace is permitted if allowedNamespaces list has it", 64 clusterNamespace: "match", 65 allowedNs: &infrav1.AllowedNamespaces{ 66 NamespaceList: []string{"match"}, 67 Selector: metav1.LabelSelector{}, 68 }, 69 setup: func(t *testing.T, c client.Client) { 70 t.Helper() 71 72 ns := &corev1.Namespace{ 73 ObjectMeta: metav1.ObjectMeta{ 74 Name: "match", 75 }, 76 } 77 ns.SetGroupVersionKind(infrav1.GroupVersion.WithKind("Namespace")) 78 err := c.Create(context.Background(), ns) 79 if err != nil { 80 t.Fatal(err) 81 } 82 }, 83 expectedResult: true, 84 expectErr: false, 85 }, 86 { 87 name: "A namespace is not permitted if allowedNamespaces list does not have it", 88 clusterNamespace: "default", 89 allowedNs: &infrav1.AllowedNamespaces{ 90 NamespaceList: []string{"nomatch"}, 91 Selector: metav1.LabelSelector{}, 92 }, 93 setup: func(t *testing.T, c client.Client) { 94 t.Helper() 95 96 ns := &corev1.Namespace{ 97 ObjectMeta: metav1.ObjectMeta{ 98 Name: "default", 99 }, 100 } 101 ns.SetGroupVersionKind(infrav1.GroupVersion.WithKind("Namespace")) 102 err := c.Create(context.Background(), ns) 103 if err != nil { 104 t.Fatal(err) 105 } 106 }, 107 expectedResult: false, 108 expectErr: false, 109 }, 110 { 111 name: "A namespace is not permitted if allowedNamespaces list and selector do not have it", 112 clusterNamespace: "default", 113 allowedNs: &infrav1.AllowedNamespaces{ 114 NamespaceList: []string{"nomatch"}, 115 Selector: metav1.LabelSelector{ 116 MatchLabels: map[string]string{"ns": "nomatchlabel"}, 117 }, 118 }, 119 setup: func(t *testing.T, c client.Client) { 120 t.Helper() 121 122 ns := &corev1.Namespace{ 123 ObjectMeta: metav1.ObjectMeta{ 124 Name: "match", 125 }, 126 } 127 ns.SetGroupVersionKind(infrav1.GroupVersion.WithKind("Namespace")) 128 err := c.Create(context.Background(), ns) 129 if err != nil { 130 t.Fatal(err) 131 } 132 }, 133 expectedResult: false, 134 expectErr: false, 135 }, 136 { 137 name: "A namespace is not permitted if allowedNamespaces list and selector do not have it", 138 clusterNamespace: "default", 139 allowedNs: &infrav1.AllowedNamespaces{ 140 NamespaceList: nil, 141 Selector: metav1.LabelSelector{ 142 MatchLabels: map[string]string{"ns": "nomatchlabel"}, 143 }, 144 }, 145 setup: func(t *testing.T, c client.Client) { 146 t.Helper() 147 148 ns := &corev1.Namespace{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: "default", 151 }, 152 } 153 ns.SetGroupVersionKind(infrav1.GroupVersion.WithKind("Namespace")) 154 err := c.Create(context.Background(), ns) 155 if err != nil { 156 t.Fatal(err) 157 } 158 }, 159 expectedResult: false, 160 expectErr: false, 161 }, 162 { 163 name: "A namespace is permitted if allowedNamespaces list does not have it but selector matches its label", 164 clusterNamespace: "default", 165 allowedNs: &infrav1.AllowedNamespaces{ 166 NamespaceList: []string{"noMatch"}, 167 Selector: metav1.LabelSelector{ 168 MatchLabels: map[string]string{"ns": "matchlabel"}, 169 }, 170 }, 171 setup: func(t *testing.T, c client.Client) { 172 t.Helper() 173 174 ns := &corev1.Namespace{ 175 ObjectMeta: metav1.ObjectMeta{ 176 Name: "default", 177 Labels: map[string]string{"ns": "matchlabel"}, 178 }, 179 } 180 ns.SetGroupVersionKind(infrav1.GroupVersion.WithKind("Namespace")) 181 err := c.Create(context.Background(), ns) 182 if err != nil { 183 t.Fatal(err) 184 } 185 }, 186 expectedResult: true, 187 expectErr: false, 188 }, 189 } 190 191 for _, tc := range testCases { 192 t.Run(tc.name, func(t *testing.T) { 193 g := NewWithT(t) 194 195 scheme, err := setupScheme() 196 if err != nil { 197 t.Fatal(err) 198 } 199 k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() 200 if tc.setup != nil { 201 tc.setup(t, k8sClient) 202 } 203 result, err := isClusterPermittedToUsePrincipal(k8sClient, tc.allowedNs, tc.clusterNamespace) 204 if tc.expectErr { 205 g.Expect(err).ToNot(BeNil()) 206 } else { 207 g.Expect(err).To(BeNil()) 208 } 209 210 if tc.expectedResult != result { 211 t.Fatal("Did not get expected result") 212 } 213 }) 214 } 215 } 216 217 func TestPrincipalParsing(t *testing.T) { 218 // Create the scope. 219 scheme := runtime.NewScheme() 220 _ = infrav1.AddToScheme(scheme) 221 cl := fake.NewClientBuilder().WithScheme(scheme).Build() 222 clusterScope, _ := NewClusterScope(ClusterScopeParams{ 223 Client: cl, 224 Cluster: &clusterv1.Cluster{ 225 ObjectMeta: metav1.ObjectMeta{ 226 Name: "test", 227 Namespace: "default", 228 }, 229 }, 230 AWSCluster: &infrav1.AWSCluster{}, 231 }, 232 ) 233 234 testCases := []struct { 235 name string 236 awsCluster infrav1.AWSCluster 237 identityRef *corev1.ObjectReference 238 identity runtime.Object 239 setup func(*testing.T, client.Client) 240 expect func([]identity.AWSPrincipalTypeProvider) 241 expectError bool 242 }{ 243 { 244 name: "Default case - no Principal specified", 245 awsCluster: infrav1.AWSCluster{ 246 ObjectMeta: metav1.ObjectMeta{ 247 Name: "cluster1", 248 Namespace: "default", 249 }, 250 TypeMeta: metav1.TypeMeta{ 251 APIVersion: infrav1.GroupVersion.String(), 252 Kind: "AWSCluster", 253 }, 254 Spec: infrav1.AWSClusterSpec{}, 255 }, 256 setup: func(t *testing.T, c client.Client) { 257 t.Helper() 258 }, 259 expect: func(providers []identity.AWSPrincipalTypeProvider) { 260 if len(providers) != 0 { 261 t.Fatalf("Expected 0 providers, got %v", len(providers)) 262 } 263 }, 264 }, 265 { 266 name: "Can get a session for a static Principal", 267 awsCluster: infrav1.AWSCluster{ 268 ObjectMeta: metav1.ObjectMeta{ 269 Name: "cluster2", 270 Namespace: "default", 271 }, 272 TypeMeta: metav1.TypeMeta{ 273 APIVersion: infrav1.GroupVersion.String(), 274 Kind: "AWSCluster", 275 }, 276 Spec: infrav1.AWSClusterSpec{ 277 IdentityRef: &infrav1.AWSIdentityReference{ 278 Name: "static-identity", 279 Kind: infrav1.ClusterStaticIdentityKind, 280 }, 281 }, 282 }, 283 setup: func(t *testing.T, c client.Client) { 284 t.Helper() 285 286 identity := &infrav1.AWSClusterStaticIdentity{ 287 ObjectMeta: metav1.ObjectMeta{ 288 Name: "static-identity", 289 }, 290 Spec: infrav1.AWSClusterStaticIdentitySpec{ 291 SecretRef: "static-credentials-secret", 292 AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{ 293 AllowedNamespaces: &infrav1.AllowedNamespaces{}, 294 }, 295 }, 296 } 297 identity.SetGroupVersionKind(infrav1.GroupVersion.WithKind("AWSClusterStaticIdentity")) 298 err := c.Create(context.Background(), identity) 299 if err != nil { 300 t.Fatal(err) 301 } 302 303 credentialsSecret := &corev1.Secret{ 304 ObjectMeta: metav1.ObjectMeta{ 305 Name: "static-credentials-secret", 306 Namespace: system.GetManagerNamespace(), 307 }, 308 Data: map[string][]byte{ 309 "AccessKeyID": []byte("1234567890"), 310 "SecretAccessKey": []byte("abcdefghijklmnop"), 311 "SessionToken": []byte("asdfasdfasdf"), 312 }, 313 } 314 credentialsSecret.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Kind: "Secret", Version: "v1"}) 315 err = c.Create(context.Background(), credentialsSecret) 316 if err != nil { 317 t.Fatal(err) 318 } 319 }, 320 expect: func(providers []identity.AWSPrincipalTypeProvider) { 321 if len(providers) != 1 { 322 t.Fatalf("Expected 1 provider, got %v", len(providers)) 323 } 324 provider := providers[0] 325 p, ok := provider.(*identity.AWSStaticPrincipalTypeProvider) 326 if !ok { 327 t.Fatal("Expected providers to be of type AWSStaticPrincipalTypeProvider") 328 } 329 if p.AccessKeyID != "1234567890" { 330 t.Fatalf("Expected AccessKeyID to be '%s', got '%s'", "1234567890", p.AccessKeyID) 331 } 332 if p.SecretAccessKey != "abcdefghijklmnop" { 333 t.Fatalf("Expected SecretAccessKey to be '%s', got '%s'", "abcdefghijklmnop", p.SecretAccessKey) 334 } 335 if p.SessionToken != "asdfasdfasdf" { 336 t.Fatalf("Expected SessionToken to be '%s', got '%s'", "asdfasdfasdf", p.SessionToken) 337 } 338 }, 339 }, 340 { 341 name: "Can build a chain identity", 342 awsCluster: infrav1.AWSCluster{ 343 ObjectMeta: metav1.ObjectMeta{ 344 Name: "cluster3", 345 Namespace: "default", 346 }, 347 TypeMeta: metav1.TypeMeta{ 348 APIVersion: infrav1.GroupVersion.String(), 349 Kind: "AWSCluster", 350 }, 351 Spec: infrav1.AWSClusterSpec{ 352 IdentityRef: &infrav1.AWSIdentityReference{ 353 Name: "role-identity", 354 Kind: infrav1.ClusterRoleIdentityKind, 355 }, 356 }, 357 }, 358 setup: func(t *testing.T, c client.Client) { 359 t.Helper() 360 361 staticPrincipal := &infrav1.AWSClusterStaticIdentity{ 362 ObjectMeta: metav1.ObjectMeta{ 363 Name: "static-identity", 364 }, 365 Spec: infrav1.AWSClusterStaticIdentitySpec{ 366 SecretRef: "static-credentials-secret", 367 AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{ 368 AllowedNamespaces: &infrav1.AllowedNamespaces{}, 369 }, 370 }, 371 } 372 staticPrincipal.SetGroupVersionKind(infrav1.GroupVersion.WithKind("AWSClusterStaticIdentity")) 373 err := c.Create(context.Background(), staticPrincipal) 374 if err != nil { 375 t.Fatal(err) 376 } 377 378 credentialsSecret := &corev1.Secret{ 379 ObjectMeta: metav1.ObjectMeta{ 380 Name: "static-credentials-secret", 381 Namespace: system.GetManagerNamespace(), 382 }, 383 Data: map[string][]byte{ 384 "AccessKeyID": []byte("1234567890"), 385 "SecretAccessKey": []byte("abcdefghijklmnop"), 386 "SessionToken": []byte("asdfasdfasdf"), 387 }, 388 } 389 credentialsSecret.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Kind: "Secret", Version: "v1"}) 390 err = c.Create(context.Background(), credentialsSecret) 391 if err != nil { 392 t.Fatal(err) 393 } 394 395 roleIdentity := &infrav1.AWSClusterRoleIdentity{ 396 ObjectMeta: metav1.ObjectMeta{ 397 Name: "role-identity", 398 }, 399 Spec: infrav1.AWSClusterRoleIdentitySpec{ 400 AWSRoleSpec: infrav1.AWSRoleSpec{ 401 RoleArn: "role-arn", 402 SessionName: "test-session", 403 }, 404 SourceIdentityRef: &infrav1.AWSIdentityReference{ 405 Name: "static-identity", 406 Kind: infrav1.ClusterStaticIdentityKind, 407 }, 408 AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{ 409 AllowedNamespaces: &infrav1.AllowedNamespaces{}, 410 }, 411 }, 412 } 413 roleIdentity.SetGroupVersionKind(infrav1.GroupVersion.WithKind("AWSClusterRoleIdentity")) 414 err = c.Create(context.Background(), roleIdentity) 415 if err != nil { 416 t.Fatal(err) 417 } 418 }, 419 expect: func(providers []identity.AWSPrincipalTypeProvider) { 420 if len(providers) != 1 { 421 t.Fatalf("Expected 1 providers, got %v", len(providers)) 422 } 423 }, 424 }, 425 { 426 name: "Can get a session for a role Principal", 427 awsCluster: infrav1.AWSCluster{ 428 ObjectMeta: metav1.ObjectMeta{ 429 Name: "cluster3", 430 Namespace: "default", 431 }, 432 TypeMeta: metav1.TypeMeta{ 433 APIVersion: infrav1.GroupVersion.String(), 434 Kind: "AWSCluster", 435 }, 436 Spec: infrav1.AWSClusterSpec{ 437 IdentityRef: &infrav1.AWSIdentityReference{ 438 Name: "role-identity", 439 Kind: infrav1.ClusterRoleIdentityKind, 440 }, 441 }, 442 }, 443 setup: func(t *testing.T, c client.Client) { 444 t.Helper() 445 446 identity := &infrav1.AWSClusterRoleIdentity{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: "role-identity", 449 }, 450 Spec: infrav1.AWSClusterRoleIdentitySpec{ 451 AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{ 452 AllowedNamespaces: &infrav1.AllowedNamespaces{}, 453 }, 454 AWSRoleSpec: infrav1.AWSRoleSpec{ 455 RoleArn: "role-arn", 456 }, 457 }, 458 } 459 identity.SetGroupVersionKind(infrav1.GroupVersion.WithKind("AWSClusterRoleIdentity")) 460 err := c.Create(context.Background(), identity) 461 if err != nil { 462 t.Fatal(err) 463 } 464 }, 465 expect: func(providers []identity.AWSPrincipalTypeProvider) { 466 if len(providers) != 1 { 467 t.Fatalf("Expected 1 providers, got %v", len(providers)) 468 } 469 provider := providers[0] 470 p, ok := provider.(*identity.AWSRolePrincipalTypeProvider) 471 if !ok { 472 t.Fatal("Expected providers to be of type AWSRolePrincipalTypeProvider") 473 } 474 if p.Principal.Spec.RoleArn != "role-arn" { 475 t.Fatal(errors.Errorf("Expected Role Provider ARN to be 'role-arn', got '%s'", p.Principal.Spec.RoleArn)) 476 } 477 }, 478 }, 479 } 480 481 for _, tc := range testCases { 482 tc := tc 483 t.Run(tc.name, func(t *testing.T) { 484 scheme, err := setupScheme() 485 if err != nil { 486 t.Fatal(err) 487 } 488 k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() 489 tc.setup(t, k8sClient) 490 clusterScope.AWSCluster = &tc.awsCluster 491 providers, err := getProvidersForCluster(context.Background(), k8sClient, clusterScope, klogr.New()) 492 if tc.expectError { 493 if err == nil { 494 t.Fatal("Expected an error but didn't get one") 495 } 496 } else { 497 if err != nil { 498 t.Fatal(err) 499 } 500 tc.expect(providers) 501 } 502 }) 503 } 504 }