/* * * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // Package google defines credentials for google cloud services. package google import ( "context" "fmt" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/alts" "google.golang.org/grpc/credentials/oauth" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal" ) const defaultCloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform" var logger = grpclog.Component("credentials") // DefaultCredentialsOptions constructs options to build DefaultCredentials. type DefaultCredentialsOptions struct { // PerRPCCreds is a per RPC credentials that is passed to a bundle. PerRPCCreds credentials.PerRPCCredentials // ALTSPerRPCCreds is a per RPC credentials that, if specified, will // supercede PerRPCCreds above for and only for ALTS connections. ALTSPerRPCCreds credentials.PerRPCCredentials } // NewDefaultCredentialsWithOptions returns a credentials bundle that is // configured to work with google services. // // This API is experimental. func NewDefaultCredentialsWithOptions(opts DefaultCredentialsOptions) credentials.Bundle { if opts.PerRPCCreds == nil { var err error // If the ADC ends up being Compute Engine Credentials, this context // won't be used. Otherwise, the context dictates all the subsequent // token requests via HTTP. So we cannot have any deadline or timeout. opts.PerRPCCreds, err = newADC(context.TODO()) if err != nil { logger.Warningf("NewDefaultCredentialsWithOptions: failed to create application oauth: %v", err) } } if opts.ALTSPerRPCCreds != nil { opts.PerRPCCreds = &dualPerRPCCreds{ perRPCCreds: opts.PerRPCCreds, altsPerRPCCreds: opts.ALTSPerRPCCreds, } } c := &creds{opts: opts} bundle, err := c.NewWithMode(internal.CredsBundleModeFallback) if err != nil { logger.Warningf("NewDefaultCredentialsWithOptions: failed to create new creds: %v", err) } return bundle } // NewDefaultCredentials returns a credentials bundle that is configured to work // with google services. // // This API is experimental. func NewDefaultCredentials() credentials.Bundle { return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{}) } // NewComputeEngineCredentials returns a credentials bundle that is configured to work // with google services. This API must only be used when running on GCE. Authentication configured // by this API represents the GCE VM's default service account. // // This API is experimental. func NewComputeEngineCredentials() credentials.Bundle { return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{ PerRPCCreds: oauth.NewComputeEngine(), }) } // creds implements credentials.Bundle. type creds struct { opts DefaultCredentialsOptions // Supported modes are defined in internal/internal.go. mode string // The active transport credentials associated with this bundle. transportCreds credentials.TransportCredentials // The active per RPC credentials associated with this bundle. perRPCCreds credentials.PerRPCCredentials } func (c *creds) TransportCredentials() credentials.TransportCredentials { return c.transportCreds } func (c *creds) PerRPCCredentials() credentials.PerRPCCredentials { if c == nil { return nil } return c.perRPCCreds } var ( newTLS = func() credentials.TransportCredentials { return credentials.NewTLS(nil) } newALTS = func() credentials.TransportCredentials { return alts.NewClientCreds(alts.DefaultClientOptions()) } newADC = func(ctx context.Context) (credentials.PerRPCCredentials, error) { return oauth.NewApplicationDefault(ctx, defaultCloudPlatformScope) } ) // NewWithMode should make a copy of Bundle, and switch mode. Modifying the // existing Bundle may cause races. func (c *creds) NewWithMode(mode string) (credentials.Bundle, error) { newCreds := &creds{ opts: c.opts, mode: mode, } // Create transport credentials. switch mode { case internal.CredsBundleModeFallback: newCreds.transportCreds = newClusterTransportCreds(newTLS(), newALTS()) case internal.CredsBundleModeBackendFromBalancer, internal.CredsBundleModeBalancer: // Only the clients can use google default credentials, so we only need // to create new ALTS client creds here. newCreds.transportCreds = newALTS() default: return nil, fmt.Errorf("unsupported mode: %v", mode) } if mode == internal.CredsBundleModeFallback || mode == internal.CredsBundleModeBackendFromBalancer { newCreds.perRPCCreds = newCreds.opts.PerRPCCreds } return newCreds, nil } // dualPerRPCCreds implements credentials.PerRPCCredentials by embedding the // fallback PerRPCCredentials and the ALTS one. It pickes one of them based on // the channel type. type dualPerRPCCreds struct { perRPCCreds credentials.PerRPCCredentials altsPerRPCCreds credentials.PerRPCCredentials } func (d *dualPerRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { ri, ok := credentials.RequestInfoFromContext(ctx) if !ok { return nil, fmt.Errorf("request info not found from context") } if authType := ri.AuthInfo.AuthType(); authType == "alts" { return d.altsPerRPCCreds.GetRequestMetadata(ctx, uri...) } // This ensures backward compatibility even if authType is not "tls". return d.perRPCCreds.GetRequestMetadata(ctx, uri...) } func (d *dualPerRPCCreds) RequireTransportSecurity() bool { return d.altsPerRPCCreds.RequireTransportSecurity() || d.perRPCCreds.RequireTransportSecurity() }