istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/xds/client.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  // xds uses ADSC to call XDS
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"strings"
    24  
    25  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/credentials"
    28  
    29  	"istio.io/istio/istioctl/pkg/clioptions"
    30  	"istio.io/istio/pilot/pkg/model"
    31  	"istio.io/istio/pkg/adsc"
    32  	"istio.io/istio/pkg/kube"
    33  )
    34  
    35  const (
    36  	// defaultExpirationSeconds is how long-lived a token to request (an hour)
    37  	defaultExpirationSeconds = 60 * 60
    38  )
    39  
    40  // Audience to create tokens for
    41  var tokenAudiences = []string{"istio-ca"}
    42  
    43  // GetXdsResponse opens a gRPC connection to opts.xds and waits for a single response
    44  func GetXdsResponse(dr *discovery.DiscoveryRequest, ns string, serviceAccount string, opts clioptions.CentralControlPlaneOptions,
    45  	grpcOpts []grpc.DialOption,
    46  ) (*discovery.DiscoveryResponse, error) {
    47  	adscConn, err := adsc.NewWithBackoffPolicy(opts.Xds, &adsc.ADSConfig{
    48  		Config: adsc.Config{
    49  			Meta: model.NodeMetadata{
    50  				Generator:      "event",
    51  				ServiceAccount: serviceAccount,
    52  				Namespace:      ns,
    53  				CloudrunAddr:   opts.IstiodAddr,
    54  			}.ToStruct(),
    55  			CertDir:            opts.CertDir,
    56  			InsecureSkipVerify: opts.InsecureSkipVerify,
    57  			XDSSAN:             opts.XDSSAN,
    58  			GrpcOpts:           grpcOpts,
    59  		},
    60  	}, nil)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("could not dial: %w", err)
    63  	}
    64  	err = adscConn.Run()
    65  	if err != nil {
    66  		return nil, fmt.Errorf("ADSC: failed running %v", err)
    67  	}
    68  
    69  	err = adscConn.Send(dr)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	response, err := adscConn.WaitVersion(opts.Timeout, dr.TypeUrl, "")
    74  	return response, err
    75  }
    76  
    77  // DialOptions constructs gRPC dial options from command line configuration
    78  func DialOptions(opts clioptions.CentralControlPlaneOptions,
    79  	ns, serviceAccount string, kubeClient kube.CLIClient,
    80  ) ([]grpc.DialOption, error) {
    81  	ctx := context.TODO()
    82  	// If we are using the insecure 15010 don't bother getting a token
    83  	if opts.Plaintext || opts.CertDir != "" {
    84  		return make([]grpc.DialOption, 0), nil
    85  	}
    86  	// Use bearer token
    87  	aud := tokenAudiences
    88  	isMCP := strings.HasSuffix(opts.Xds, ".googleapis.com") || strings.HasSuffix(opts.Xds, ".googleapis.com:443")
    89  	if isMCP {
    90  		// Special credentials handling when using ASM Managed Control Plane.
    91  		mem, err := getHubMembership(ctx, kubeClient)
    92  		if err != nil {
    93  			return nil, fmt.Errorf("failed to query Hub membership: %w", err)
    94  		}
    95  		aud = []string{mem.WorkloadIdentityPool}
    96  	}
    97  	k8sCreds, err := kubeClient.CreatePerRPCCredentials(ctx, ns, serviceAccount, aud, defaultExpirationSeconds)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("failed to create RPC credentials for \"%s.%s\": %w", serviceAccount, ns, err)
   100  	}
   101  	if isMCP {
   102  		return mcpDialOptions(ctx, opts.GCPProject, k8sCreds)
   103  	}
   104  	return []grpc.DialOption{
   105  		// nolint: gosec
   106  		// Only runs over istioctl experimental
   107  		// TODO: https://github.com/istio/istio/issues/41937
   108  		grpc.WithTransportCredentials(credentials.NewTLS(
   109  			&tls.Config{
   110  				// Always skip verifying, because without it we always get "certificate signed by unknown authority".
   111  				// We don't set the XDSSAN for the same reason.
   112  				InsecureSkipVerify: true,
   113  			})),
   114  		grpc.WithPerRPCCredentials(k8sCreds),
   115  	}, nil
   116  }