Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/xds/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (

const (
serverFeaturesIgnoreResourceDeletion = "ignore_resource_deletion"
serverFeaturesTrustedXDSServer = "trusted_xds_server"
gRPCUserAgentName = "gRPC Go"
clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning"
clientFeatureResourceWrapper = "xds.config.resource-in-sotw"
Expand Down Expand Up @@ -256,6 +257,18 @@ func (sc *ServerConfig) ServerFeaturesIgnoreResourceDeletion() bool {
return false
}

// ServerFeaturesTrustedXDSServer returns true if this server is trusted,
// and gRPC should accept security-config-affecting fields from the server
// as described in gRFC A81.
func (sc *ServerConfig) ServerFeaturesTrustedXDSServer() bool {
for _, sf := range sc.serverFeatures {
if sf == serverFeaturesTrustedXDSServer {
return true
}
}
return false
}

// SelectedChannelCreds returns the selected credentials configuration for
// communicating with this server.
func (sc *ServerConfig) SelectedChannelCreds() ChannelCreds {
Expand Down
27 changes: 27 additions & 0 deletions internal/xds/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,22 @@ var (
"server_features" : ["xds_v3"]
}]
}`,
"serverSupportsTrustedXDSServer": `
{
"node": {
"id": "ENVOY_NODE_ID",
"metadata": {
"TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector"
}
},
"xds_servers" : [{
"server_uri": "trafficdirector.googleapis.com:443",
"channel_creds": [
{ "type": "google_default" }
],
"server_features" : ["trusted_xds_server", "xds_v3"]
}]
}`,
}
metadata = &structpb.Struct{
Fields: map[string]*structpb.Value{
Expand Down Expand Up @@ -338,6 +354,16 @@ var (
node: v3Node,
clientDefaultListenerResourceNameTemplate: "%s",
}
configWithGoogleDefaultCredsAndTrustedXDSServer = &Config{
xDSServers: []*ServerConfig{{
serverURI: "trafficdirector.googleapis.com:443",
channelCreds: []ChannelCreds{{Type: "google_default"}},
serverFeatures: []string{"trusted_xds_server", "xds_v3"},
selectedChannelCreds: ChannelCreds{Type: "google_default"},
}},
node: v3Node,
clientDefaultListenerResourceNameTemplate: "%s",
}
configWithGoogleDefaultCredsAndNoServerFeatures = &Config{
xDSServers: []*ServerConfig{{
serverURI: "trafficdirector.googleapis.com:443",
Expand Down Expand Up @@ -539,6 +565,7 @@ func (s) TestGetConfiguration_Success(t *testing.T) {
{"goodBootstrap", configWithGoogleDefaultCredsAndV3},
{"multipleXDSServers", configWithMultipleServers},
{"serverSupportsIgnoreResourceDeletion", configWithGoogleDefaultCredsAndIgnoreResourceDeletion},
{"serverSupportsTrustedXDSServer", configWithGoogleDefaultCredsAndTrustedXDSServer},
{"istioStyleInsecureWithoutCallCreds", configWithIstioStyleNoCallCreds},
}

Expand Down
2 changes: 1 addition & 1 deletion internal/xds/clients/xdsclient/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ func (a *authority) handleADSResourceUpdate(serverConfig *ServerConfig, rType Re
// "resource-not-found" error.
continue
}
if serverConfig.IgnoreResourceDeletion {
if serverConfig.SupportsServerFeature(ServerFeatureIgnoreResourceDeletion) {
// Per A53, resource deletions are ignored if the
// `ignore_resource_deletion` server feature is enabled through the
// xDS client configuration. If the resource deletion is to be
Expand Down
33 changes: 22 additions & 11 deletions internal/xds/clients/xdsclient/xdsconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ import (
"google.golang.org/grpc/internal/xds/clients"
)

// ServerFeature indicates the features that will be supported by an xDS server.
type ServerFeature uint64

const (
// ServerFeatureIgnoreResourceDeletion indicates that the server supports a
// feature where the xDS client can ignore resource deletions from this server,
// as described in gRFC A53.
ServerFeatureIgnoreResourceDeletion ServerFeature = 1 << iota
// ServerFeatureTrustedXDSServer returns true if this server is trusted,
// and gRPC should accept security-config-affecting fields from the server
// as described in gRFC A81.
ServerFeatureTrustedXDSServer
)

// Config is used to configure an xDS client. After one has been passed to the
// xDS client's New function, no part of it may be modified. A Config may be
// reused; the xdsclient package will also not modify it.
Expand Down Expand Up @@ -75,16 +89,7 @@ type Config struct {
type ServerConfig struct {
ServerIdentifier clients.ServerIdentifier

// IgnoreResourceDeletion is a server feature which if set to true,
// indicates that resource deletion errors from xDS management servers can
// be ignored and cached resource data can be used.
//
// This will be removed in the future once we implement gRFC A88
// and two new fields FailOnDataErrors and
// ResourceTimerIsTransientError will be introduced.
IgnoreResourceDeletion bool

// TODO: Link to gRFC A88
ServerFeature ServerFeature
}

// Authority contains configuration for an xDS control plane authority.
Expand All @@ -98,5 +103,11 @@ type Authority struct {
}

func isServerConfigEqual(a, b *ServerConfig) bool {
return a.ServerIdentifier == b.ServerIdentifier && a.IgnoreResourceDeletion == b.IgnoreResourceDeletion
return a.ServerIdentifier == b.ServerIdentifier && a.ServerFeature == b.ServerFeature
}

// SupportsServerFeature returns true if the server configuration indicates that
// the server supports the given feature.
func (s *ServerConfig) SupportsServerFeature(feature ServerFeature) bool {
return s.ServerFeature&feature != 0
}
51 changes: 30 additions & 21 deletions internal/xds/xdsclient/clientimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,29 @@ func (c *clientImpl) decrRef() int32 {
return atomic.AddInt32(&c.refCount, -1)
}

func buildServerConfigs(bootstrapSC []*bootstrap.ServerConfig, grpcTransportConfigs map[string]grpctransport.Config, gServerCfgMap map[xdsclient.ServerConfig]*bootstrap.ServerConfig) ([]xdsclient.ServerConfig, error) {
var gServerCfg []xdsclient.ServerConfig
for _, sc := range bootstrapSC {
if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
return nil, err
}
var serverFeatures xdsclient.ServerFeature
if sc.ServerFeaturesIgnoreResourceDeletion() {
serverFeatures = serverFeatures | xdsclient.ServerFeatureIgnoreResourceDeletion
}
if sc.ServerFeaturesTrustedXDSServer() {
serverFeatures = serverFeatures | xdsclient.ServerFeatureTrustedXDSServer
}
gsc := xdsclient.ServerConfig{
ServerIdentifier: clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type}},
ServerFeature: serverFeatures,
}
gServerCfg = append(gServerCfg, gsc)
gServerCfgMap[gsc] = sc
}
return gServerCfg, nil
}

// buildXDSClientConfig builds the xdsclient.Config from the bootstrap.Config.
func buildXDSClientConfig(config *bootstrap.Config, metricsRecorder estats.MetricsRecorder, target string, watchExpiryTimeout time.Duration) (xdsclient.Config, error) {
grpcTransportConfigs := make(map[string]grpctransport.Config)
Expand All @@ -175,30 +198,16 @@ func buildXDSClientConfig(config *bootstrap.Config, metricsRecorder estats.Metri
if len(cfg.XDSServers) >= 1 {
serverCfg = cfg.XDSServers
}
var gServerCfg []xdsclient.ServerConfig
for _, sc := range serverCfg {
if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
return xdsclient.Config{}, err
}
gsc := xdsclient.ServerConfig{
ServerIdentifier: clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type}},
IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()}
gServerCfg = append(gServerCfg, gsc)
gServerCfgMap[gsc] = sc
gsc, err := buildServerConfigs(serverCfg, grpcTransportConfigs, gServerCfgMap)
if err != nil {
return xdsclient.Config{}, err
}
gAuthorities[name] = xdsclient.Authority{XDSServers: gServerCfg}
gAuthorities[name] = xdsclient.Authority{XDSServers: gsc}
}

gServerCfgs := make([]xdsclient.ServerConfig, 0, len(config.XDSServers()))
for _, sc := range config.XDSServers() {
if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
return xdsclient.Config{}, err
}
gsc := xdsclient.ServerConfig{
ServerIdentifier: clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type}},
IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()}
gServerCfgs = append(gServerCfgs, gsc)
gServerCfgMap[gsc] = sc
gServerCfgs, err := buildServerConfigs(config.XDSServers(), grpcTransportConfigs, gServerCfgMap)
if err != nil {
return xdsclient.Config{}, err
}

node := config.Node()
Expand Down
73 changes: 72 additions & 1 deletion internal/xds/xdsclient/clientimpl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,78 @@ func (s) TestBuildXDSClientConfig_Success(t *testing.T) {
}`, testXDSServerURL, testNodeID)),
wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
node, serverCfg := c.Node(), c.XDSServers()[0]
expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}}, IgnoreResourceDeletion: true}
serverFeature := xdsclient.ServerFeatureIgnoreResourceDeletion
expectedServer := xdsclient.ServerConfig{
ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
ServerFeature: serverFeature}
gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
return xdsclient.Config{
Servers: []xdsclient.ServerConfig{expectedServer},
Node: clients.Node{ID: node.GetId(), Cluster: node.GetCluster(), Metadata: node.Metadata, UserAgentName: node.UserAgentName, UserAgentVersion: node.GetUserAgentVersion()},
Authorities: map[string]xdsclient.Authority{},
ResourceTypes: map[string]xdsclient.ResourceType{
version.V3ListenerURL: {TypeURL: version.V3ListenerURL, TypeName: xdsresource.ListenerResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewListenerResourceTypeDecoder(c)},
version.V3RouteConfigURL: {TypeURL: version.V3RouteConfigURL, TypeName: xdsresource.RouteConfigTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewRouteConfigResourceTypeDecoder(c)},
version.V3ClusterURL: {TypeURL: version.V3ClusterURL, TypeName: xdsresource.ClusterResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewClusterResourceTypeDecoder(c, gServerCfgMap)},
version.V3EndpointsURL: {TypeURL: version.V3EndpointsURL, TypeName: xdsresource.EndpointsResourceTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewEndpointsResourceTypeDecoder(c)},
},
MetricsReporter: &metricsReporter{recorder: stats.NewTestMetricsRecorder(), target: testTargetName},
TransportBuilder: grpctransport.NewBuilder(map[string]grpctransport.Config{
"insecure": {
Credentials: insecure.NewBundle(),
GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
opts = append(opts, serverCfg.DialOptions()...)
return grpc.NewClient(target, opts...)
}},
}),
}
},
},
{
name: "server features with trusted_xds_server",
bootstrapContents: []byte(fmt.Sprintf(`{
"xds_servers": [{"server_uri": "%s", "channel_creds": [{"type": "insecure"}], "server_features": ["trusted_xds_server"]}],
"node": {"id": "%s"}
}`, testXDSServerURL, testNodeID)),
wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
node, serverCfg := c.Node(), c.XDSServers()[0]
serverFeature := xdsclient.ServerFeatureTrustedXDSServer
expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
ServerFeature: serverFeature}
gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
return xdsclient.Config{
Servers: []xdsclient.ServerConfig{expectedServer},
Node: clients.Node{ID: node.GetId(), Cluster: node.GetCluster(), Metadata: node.Metadata, UserAgentName: node.UserAgentName, UserAgentVersion: node.GetUserAgentVersion()},
Authorities: map[string]xdsclient.Authority{},
ResourceTypes: map[string]xdsclient.ResourceType{
version.V3ListenerURL: {TypeURL: version.V3ListenerURL, TypeName: xdsresource.ListenerResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewListenerResourceTypeDecoder(c)},
version.V3RouteConfigURL: {TypeURL: version.V3RouteConfigURL, TypeName: xdsresource.RouteConfigTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewRouteConfigResourceTypeDecoder(c)},
version.V3ClusterURL: {TypeURL: version.V3ClusterURL, TypeName: xdsresource.ClusterResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewClusterResourceTypeDecoder(c, gServerCfgMap)},
version.V3EndpointsURL: {TypeURL: version.V3EndpointsURL, TypeName: xdsresource.EndpointsResourceTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewEndpointsResourceTypeDecoder(c)},
},
MetricsReporter: &metricsReporter{recorder: stats.NewTestMetricsRecorder(), target: testTargetName},
TransportBuilder: grpctransport.NewBuilder(map[string]grpctransport.Config{
"insecure": {
Credentials: insecure.NewBundle(),
GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
opts = append(opts, serverCfg.DialOptions()...)
return grpc.NewClient(target, opts...)
}},
}),
}
},
},
{
name: "server features with ignore_resource_deletion and trusted_xds_server",
bootstrapContents: []byte(fmt.Sprintf(`{
"xds_servers": [{"server_uri": "%s", "channel_creds": [{"type": "insecure"}], "server_features": ["ignore_resource_deletion", "trusted_xds_server"]}],
"node": {"id": "%s"}
}`, testXDSServerURL, testNodeID)),
wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
node, serverCfg := c.Node(), c.XDSServers()[0]
serverFeature := xdsclient.ServerFeatureTrustedXDSServer | xdsclient.ServerFeatureIgnoreResourceDeletion
expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
ServerFeature: serverFeature}
gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
return xdsclient.Config{
Servers: []xdsclient.ServerConfig{expectedServer},
Expand Down