istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/xds/google.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package xds 16 17 import ( 18 "context" 19 "crypto/tls" 20 "crypto/x509" 21 "errors" 22 "fmt" 23 "strings" 24 25 "google.golang.org/grpc" 26 "google.golang.org/grpc/credentials" 27 "google.golang.org/grpc/credentials/oauth" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 31 "istio.io/istio/pkg/kube" 32 ) 33 34 type meshAuthCredentials struct { 35 k8sCreds credentials.PerRPCCredentials 36 gcpCreds credentials.PerRPCCredentials 37 project string 38 } 39 40 func (c *meshAuthCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 41 ret := map[string]string{ 42 "x-goog-user-project": c.project, 43 } 44 if err := updateAuthHdrs(ctx, uri, "k8s", c.k8sCreds, ret, "x-mesh-authorization"); err != nil { 45 return nil, err 46 } 47 if err := updateAuthHdrs(ctx, uri, "gcp", c.gcpCreds, ret, "authorization"); err != nil { 48 return nil, err 49 } 50 return ret, nil 51 } 52 53 func (*meshAuthCredentials) RequireTransportSecurity() bool { 54 return true 55 } 56 57 func updateAuthHdrs(ctx context.Context, uri []string, kind string, creds credentials.PerRPCCredentials, dst map[string]string, dstHdr string) error { 58 ret, err := creds.GetRequestMetadata(ctx, uri...) 59 if err != nil { 60 return err 61 } 62 for k, v := range ret { 63 if !strings.EqualFold(k, "authorization") { 64 if _, ok := dst[k]; ok { 65 return fmt.Errorf("underlying %s credentials contain a %s header which is already present in the combined credentials", kind, k) 66 } 67 dst[k] = v 68 } else { 69 dst[dstHdr] = v 70 } 71 } 72 return nil 73 } 74 75 type hubMembership struct { 76 WorkloadIdentityPool string 77 } 78 79 func getHubMembership(ctx context.Context, exClient kube.CLIClient) (*hubMembership, error) { 80 client := exClient.Dynamic() 81 gvr := schema.GroupVersionResource{ 82 Group: "hub.gke.io", 83 Version: "v1", 84 Resource: "memberships", 85 } 86 u, err := client.Resource(gvr).Get(ctx, "membership", metav1.GetOptions{}) 87 if err != nil { 88 return nil, err 89 } 90 spec, ok := u.Object["spec"].(map[string]any) 91 if !ok { 92 return nil, errors.New(`field "spec" is not a map`) 93 } 94 var mem hubMembership 95 mem.WorkloadIdentityPool, ok = spec["workload_identity_pool"].(string) 96 if !ok { 97 return nil, errors.New(`field "spec.workload_identity_pool" is not a string`) 98 } 99 return &mem, nil 100 } 101 102 func mcpDialOptions(ctx context.Context, gcpProject string, k8sCreds credentials.PerRPCCredentials) ([]grpc.DialOption, error) { 103 systemRoots, err := x509.SystemCertPool() 104 if err != nil { 105 return nil, fmt.Errorf("failed to get system cert pool: %w", err) 106 } 107 gcpCreds, err := oauth.NewApplicationDefault(ctx) 108 if err != nil { 109 return nil, fmt.Errorf("failed to get application default credentials: %w", err) 110 } 111 112 return []grpc.DialOption{ 113 grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ 114 RootCAs: systemRoots, 115 MinVersion: tls.VersionTLS12, 116 })), 117 grpc.WithPerRPCCredentials(&meshAuthCredentials{ 118 k8sCreds: k8sCreds, 119 gcpCreds: gcpCreds, 120 project: gcpProject, 121 }), 122 }, nil 123 }