diff --git a/credentials/insecure/insecure.go b/credentials/insecure/insecure.go index 4fbed12565f..82bee1443bf 100644 --- a/credentials/insecure/insecure.go +++ b/credentials/insecure/insecure.go @@ -70,3 +70,29 @@ type info struct { func (info) AuthType() string { return "insecure" } + +// insecureBundle implements an insecure bundle. +// An insecure bundle provides a thin wrapper around insecureTC to support +// the credentials.Bundle interface. +type insecureBundle struct{} + +// NewBundle returns a bundle with disabled transport security and no per rpc credential. +func NewBundle() credentials.Bundle { + return insecureBundle{} +} + +// NewWithMode returns a new insecure Bundle. The mode is ignored. +func (insecureBundle) NewWithMode(string) (credentials.Bundle, error) { + return insecureBundle{}, nil +} + +// PerRPCCredentials returns an nil implementation as insecure +// bundle does not support a per rpc credential. +func (insecureBundle) PerRPCCredentials() credentials.PerRPCCredentials { + return nil +} + +// TransportCredentials returns the underlying insecure transport credential. +func (insecureBundle) TransportCredentials() credentials.TransportCredentials { + return NewCredentials() +} diff --git a/xds/bootstrap/bootstrap.go b/xds/bootstrap/bootstrap.go new file mode 100644 index 00000000000..db6c7d6754a --- /dev/null +++ b/xds/bootstrap/bootstrap.go @@ -0,0 +1,64 @@ +/* + * + * Copyright 2022 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 bootstrap provides the functionality to register possible options +// for aspects of the xDS client through the bootstrap file. +// +// Experimental +// +// Notice: This package is EXPERIMENTAL and may be changed or removed +// in a later release. +package bootstrap + +import ( + "encoding/json" + + "google.golang.org/grpc/credentials" +) + +// registry is a map from credential type name to Credential builder. +var registry = make(map[string]Credentials) + +// Credentials interface encapsulates a credentials.Bundle builder +// that can be used for communicating with the xDS Management server. +type Credentials interface { + // Build returns a credential bundle associated with this credential. + Build(config json.RawMessage) (credentials.Bundle, error) + // Name returns the credential name associated with this credential. + Name() string +} + +// RegisterCredentials registers Credentials used for connecting to the xds +// management server. +// +// NOTE: this function must only be called during initialization time (i.e. in +// an init() function), and is not thread-safe. If multiple credentials are +// registered with the same name, the one registered last will take effect. +func RegisterCredentials(c Credentials) { + registry[c.Name()] = c +} + +// GetCredentials returns the credentials associated with a given name. +// If no credentials are registered with the name, nil will be returned. +func GetCredentials(name string) Credentials { + if c, ok := registry[name]; ok { + return c + } + + return nil +} diff --git a/xds/bootstrap/bootstrap_test.go b/xds/bootstrap/bootstrap_test.go new file mode 100644 index 00000000000..80ae31ccd2e --- /dev/null +++ b/xds/bootstrap/bootstrap_test.go @@ -0,0 +1,63 @@ +/* + * + * Copyright 2022 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 bootstrap + +import ( + "encoding/json" + "testing" + + "google.golang.org/grpc/credentials" +) + +const testCredsBuilderName = "test_creds" + +var builder = &testCredsBuilder{} + +func init() { + RegisterCredentials(builder) +} + +type testCredsBuilder struct { + config json.RawMessage +} + +func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, error) { + t.config = config + return nil, nil +} + +func (t *testCredsBuilder) Name() string { + return testCredsBuilderName +} + +func TestRegisterNew(t *testing.T) { + c := GetCredentials(testCredsBuilderName) + if c == nil { + t.Fatalf("GetCredentials(%q) credential = nil", testCredsBuilderName) + } + + const sampleConfig = "sample_config" + rawMessage := json.RawMessage(sampleConfig) + if _, err := c.Build(rawMessage); err != nil { + t.Errorf("Build(%v) error = %v, want nil", rawMessage, err) + } + + if got, want := string(builder.config), sampleConfig; got != want { + t.Errorf("Build config = %v, want %v", got, want) + } +} diff --git a/xds/internal/xdsclient/bootstrap/bootstrap.go b/xds/internal/xdsclient/bootstrap/bootstrap.go index 15aed44eb73..0115dcf927a 100644 --- a/xds/internal/xdsclient/bootstrap/bootstrap.go +++ b/xds/internal/xdsclient/bootstrap/bootstrap.go @@ -31,12 +31,14 @@ import ( "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/google" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/tls/certprovider" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/pretty" + "google.golang.org/grpc/xds/bootstrap" "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version" v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" @@ -49,18 +51,43 @@ const ( // server supports the v3 version of the xDS transport protocol. serverFeaturesV3 = "xds_v3" - // Type name for Google default credentials. - credsGoogleDefault = "google_default" - credsInsecure = "insecure" gRPCUserAgentName = "gRPC Go" clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning" ) +func init() { + bootstrap.RegisterCredentials(&insecureCredsBuilder{}) + bootstrap.RegisterCredentials(&googleDefaultCredsBuilder{}) +} + var gRPCVersion = fmt.Sprintf("%s %s", gRPCUserAgentName, grpc.Version) // For overriding in unit tests. var bootstrapFileReadFunc = ioutil.ReadFile +// insecureCredsBuilder encapsulates a insecure credential that is built using a +// JSON config. +type insecureCredsBuilder struct{} + +func (i *insecureCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) { + return insecure.NewBundle(), nil +} +func (i *insecureCredsBuilder) Name() string { + return "insecure" +} + +// googleDefaultCredsBuilder encapsulates a Google Default credential that is built using a +// JSON config. +type googleDefaultCredsBuilder struct{} + +func (d *googleDefaultCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) { + return google.NewDefaultCredentials(), nil +} + +func (d *googleDefaultCredsBuilder) Name() string { + return "google_default" +} + // ServerConfig contains the configuration to connect to a server, including // URI, creds, and transport API version (e.g. v2 or v3). type ServerConfig struct { @@ -129,13 +156,16 @@ func (sc *ServerConfig) UnmarshalJSON(data []byte) error { for _, cc := range server.ChannelCreds { // We stop at the first credential type that we support. sc.CredsType = cc.Type - if cc.Type == credsGoogleDefault { - sc.Creds = grpc.WithCredentialsBundle(google.NewDefaultCredentials()) - break - } else if cc.Type == credsInsecure { - sc.Creds = grpc.WithTransportCredentials(insecure.NewCredentials()) - break + c := bootstrap.GetCredentials(cc.Type) + if c == nil { + continue + } + bundle, err := c.Build(cc.Config) + if err != nil { + return fmt.Errorf("failed to build credentials bundle from bootstrap for %q: %v", cc.Type, err) } + sc.Creds = grpc.WithCredentialsBundle(bundle) + break } for _, f := range server.ServerFeatures { if f == serverFeaturesV3 { diff --git a/xds/internal/xdsclient/bootstrap/bootstrap_test.go b/xds/internal/xdsclient/bootstrap/bootstrap_test.go index 573a3fca173..36b4302c8fa 100644 --- a/xds/internal/xdsclient/bootstrap/bootstrap_test.go +++ b/xds/internal/xdsclient/bootstrap/bootstrap_test.go @@ -35,6 +35,7 @@ import ( "google.golang.org/grpc/credentials/tls/certprovider" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/envconfig" + "google.golang.org/grpc/xds/bootstrap" "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version" v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" @@ -1015,3 +1016,32 @@ func TestServerConfigMarshalAndUnmarshal(t *testing.T) { t.Fatalf("diff (-got +want): %v", diff) } } + +func TestDefaultBundles(t *testing.T) { + if c := bootstrap.GetCredentials("google_default"); c == nil { + t.Errorf(`bootstrap.GetCredentials("google_default") credential is nil, want non-nil`) + } + + if c := bootstrap.GetCredentials("insecure"); c == nil { + t.Errorf(`bootstrap.GetCredentials("insecure") credential is nil, want non-nil`) + } +} + +func TestCredsBuilders(t *testing.T) { + b := &googleDefaultCredsBuilder{} + if _, err := b.Build(nil); err != nil { + t.Errorf("googleDefaultCredsBuilder.Build failed: %v", err) + } + if got, want := b.Name(), "google_default"; got != want { + t.Errorf("googleDefaultCredsBuilder.Name = %v, want %v", got, want) + } + + i := &insecureCredsBuilder{} + if _, err := i.Build(nil); err != nil { + t.Errorf("insecureCredsBuilder.Build failed: %v", err) + } + + if got, want := i.Name(), "insecure"; got != want { + t.Errorf("insecureCredsBuilder.Name = %v, want %v", got, want) + } +}