mirror of https://github.com/grpc/grpc-go.git
remove internal/ and xds/ changes
This commit is contained in:
parent
67131908f3
commit
3f563eb09c
|
@ -86,7 +86,7 @@ func (c *jwtTokenFileCallCreds) GetRequestMetadata(ctx context.Context, _ ...str
|
||||||
if c.isTokenValidLocked() {
|
if c.isTokenValidLocked() {
|
||||||
if c.needsPreemptiveRefreshLocked() {
|
if c.needsPreemptiveRefreshLocked() {
|
||||||
// Start refresh if not pending (handling the prior RPC may have
|
// Start refresh if not pending (handling the prior RPC may have
|
||||||
// just spwaned a goroutine).
|
// just spawned a goroutine).
|
||||||
if !c.pendingRefresh {
|
if !c.pendingRefresh {
|
||||||
c.pendingRefresh = true
|
c.pendingRefresh = true
|
||||||
go c.refreshToken()
|
go c.refreshToken()
|
||||||
|
@ -105,7 +105,7 @@ func (c *jwtTokenFileCallCreds) GetRequestMetadata(ctx context.Context, _ ...str
|
||||||
// At this point, the token is either invalid or expired and we are no
|
// At this point, the token is either invalid or expired and we are no
|
||||||
// longer backing off. So refresh it.
|
// longer backing off. So refresh it.
|
||||||
token, expiry, err := c.fileReader.ReadToken()
|
token, expiry, err := c.fileReader.ReadToken()
|
||||||
c.setCacheLocked(token, expiry, err)
|
c.updateCacheLocked(token, expiry, err)
|
||||||
|
|
||||||
if c.cachedError != nil {
|
if c.cachedError != nil {
|
||||||
return nil, c.cachedError
|
return nil, c.cachedError
|
||||||
|
@ -148,17 +148,17 @@ func (c *jwtTokenFileCallCreds) refreshToken() {
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.setCacheLocked(token, expiry, err)
|
c.updateCacheLocked(token, expiry, err)
|
||||||
|
|
||||||
// Reset pending refresh and broadcast to waiting goroutines
|
// Reset pending refresh and broadcast to waiting goroutines
|
||||||
c.pendingRefresh = false
|
c.pendingRefresh = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// setCacheLocked updates the cached token, expiry, and error state.
|
// updateCacheLocked updates the cached token, expiry, and error state.
|
||||||
// If an error is provided, it determines whether to set it as an UNAVAILABLE
|
// If an error is provided, it determines whether to set it as an UNAVAILABLE
|
||||||
// or UNAUTHENTICATED error based on the error type.
|
// or UNAUTHENTICATED error based on the error type.
|
||||||
// Caller must hold c.mu lock.
|
// Caller must hold c.mu lock.
|
||||||
func (c *jwtTokenFileCallCreds) setCacheLocked(token string, expiry time.Time, err error) {
|
func (c *jwtTokenFileCallCreds) updateCacheLocked(token string, expiry time.Time, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Convert to gRPC status codes
|
// Convert to gRPC status codes
|
||||||
if strings.Contains(err.Error(), "failed to read token file") || strings.Contains(err.Error(), "token file") && strings.Contains(err.Error(), "is empty") {
|
if strings.Contains(err.Error(), "failed to read token file") || strings.Contains(err.Error(), "token file") && strings.Contains(err.Error(), "is empty") {
|
||||||
|
|
|
@ -68,9 +68,4 @@ var (
|
||||||
// trust. For more details, see:
|
// trust. For more details, see:
|
||||||
// https://github.com/grpc/proposal/blob/master/A87-mtls-spiffe-support.md
|
// https://github.com/grpc/proposal/blob/master/A87-mtls-spiffe-support.md
|
||||||
XDSSPIFFEEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE", false)
|
XDSSPIFFEEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE", false)
|
||||||
|
|
||||||
// XDSBootstrapCallCredsEnabled controls if JWT call credentials can be used
|
|
||||||
// in xDS bootstrap configuration. For more details, see:
|
|
||||||
// https://github.com/grpc/proposal/blob/master/A97-xds-jwt-call-creds.md
|
|
||||||
XDSBootstrapCallCredsEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS", false)
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
"google.golang.org/grpc/internal/envconfig"
|
||||||
|
@ -65,26 +64,11 @@ type ChannelCreds struct {
|
||||||
Config json.RawMessage `json:"config,omitempty"`
|
Config json.RawMessage `json:"config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCreds contains the call credentials configuration for individual RPCs.
|
|
||||||
// This type implements RFC A97 call credentials structure.
|
|
||||||
type CallCreds struct {
|
|
||||||
// Type contains a unique name identifying the call credentials type.
|
|
||||||
// Currently only "jwt_token_file" is supported.
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
// Config contains the JSON configuration associated with the call credentials.
|
|
||||||
Config json.RawMessage `json:"config,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal reports whether cc and other are considered equal.
|
// Equal reports whether cc and other are considered equal.
|
||||||
func (cc ChannelCreds) Equal(other ChannelCreds) bool {
|
func (cc ChannelCreds) Equal(other ChannelCreds) bool {
|
||||||
return cc.Type == other.Type && bytes.Equal(cc.Config, other.Config)
|
return cc.Type == other.Type && bytes.Equal(cc.Config, other.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal reports whether cc and other are considered equal.
|
|
||||||
func (cc CallCreds) Equal(other CallCreds) bool {
|
|
||||||
return cc.Type == other.Type && bytes.Equal(cc.Config, other.Config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the credentials. It contains the
|
// String returns a string representation of the credentials. It contains the
|
||||||
// type and the config (if non-nil) separated by a "-".
|
// type and the config (if non-nil) separated by a "-".
|
||||||
func (cc ChannelCreds) String() string {
|
func (cc ChannelCreds) String() string {
|
||||||
|
@ -188,15 +172,13 @@ type ServerConfig struct {
|
||||||
serverURI string
|
serverURI string
|
||||||
channelCreds []ChannelCreds
|
channelCreds []ChannelCreds
|
||||||
serverFeatures []string
|
serverFeatures []string
|
||||||
callCreds []CallCreds
|
|
||||||
|
|
||||||
// As part of unmarshalling the JSON config into this struct, we ensure that
|
// As part of unmarshalling the JSON config into this struct, we ensure that
|
||||||
// the credentials config is valid by building an instance of the specified
|
// the credentials config is valid by building an instance of the specified
|
||||||
// credentials and store it here for easy access.
|
// credentials and store it here for easy access.
|
||||||
selectedCreds ChannelCreds
|
selectedCreds ChannelCreds
|
||||||
credsDialOption grpc.DialOption
|
credsDialOption grpc.DialOption
|
||||||
extraDialOptions []grpc.DialOption
|
extraDialOptions []grpc.DialOption
|
||||||
selectedCallCreds []credentials.PerRPCCredentials // Built call credentials
|
|
||||||
|
|
||||||
cleanups []func()
|
cleanups []func()
|
||||||
}
|
}
|
||||||
|
@ -218,17 +200,6 @@ func (sc *ServerConfig) ServerFeatures() []string {
|
||||||
return sc.serverFeatures
|
return sc.serverFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCreds returns the call credentials configuration for this server.
|
|
||||||
func (sc *ServerConfig) CallCreds() []CallCreds {
|
|
||||||
return sc.callCreds
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectedCallCreds returns the built call credentials that are ready to use.
|
|
||||||
// These are the credentials that were successfully built from the call_creds configuration.
|
|
||||||
func (sc *ServerConfig) SelectedCallCreds() []credentials.PerRPCCredentials {
|
|
||||||
return sc.selectedCallCreds
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerFeaturesIgnoreResourceDeletion returns true if this server supports a
|
// ServerFeaturesIgnoreResourceDeletion returns true if this server supports a
|
||||||
// feature where the xDS client can ignore resource deletions from this server,
|
// feature where the xDS client can ignore resource deletions from this server,
|
||||||
// as described in gRFC A53.
|
// as described in gRFC A53.
|
||||||
|
@ -262,28 +233,6 @@ func (sc *ServerConfig) DialOptions() []grpc.DialOption {
|
||||||
return dopts
|
return dopts
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOptionsWithCallCredsForTransport returns dial options including call credentials
|
|
||||||
// only if they are compatible with the specified transport credentials type.
|
|
||||||
// Call credentials that require transport security will be skipped for insecure transports.
|
|
||||||
func (sc *ServerConfig) DialOptionsWithCallCredsForTransport(transportCredsType string, transportCreds credentials.TransportCredentials) []grpc.DialOption {
|
|
||||||
dopts := sc.DialOptions()
|
|
||||||
|
|
||||||
// Check if transport is insecure
|
|
||||||
isInsecureTransport := transportCredsType == "insecure" ||
|
|
||||||
(transportCreds != nil && transportCreds.Info().SecurityProtocol == "insecure")
|
|
||||||
|
|
||||||
// Add call credentials only if compatible with transport security
|
|
||||||
for _, callCred := range sc.selectedCallCreds {
|
|
||||||
// Skip call credentials that require transport security on insecure transports
|
|
||||||
if isInsecureTransport && callCred.RequireTransportSecurity() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dopts = append(dopts, grpc.WithPerRPCCredentials(callCred))
|
|
||||||
}
|
|
||||||
|
|
||||||
return dopts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanups returns a collection of functions to be called when the xDS client
|
// Cleanups returns a collection of functions to be called when the xDS client
|
||||||
// for this server is closed. Allows cleaning up resources created specifically
|
// for this server is closed. Allows cleaning up resources created specifically
|
||||||
// for this server.
|
// for this server.
|
||||||
|
@ -302,8 +251,6 @@ func (sc *ServerConfig) Equal(other *ServerConfig) bool {
|
||||||
return false
|
return false
|
||||||
case !slices.EqualFunc(sc.channelCreds, other.channelCreds, func(a, b ChannelCreds) bool { return a.Equal(b) }):
|
case !slices.EqualFunc(sc.channelCreds, other.channelCreds, func(a, b ChannelCreds) bool { return a.Equal(b) }):
|
||||||
return false
|
return false
|
||||||
case !slices.EqualFunc(sc.callCreds, other.callCreds, func(a, b CallCreds) bool { return a.Equal(b) }):
|
|
||||||
return false
|
|
||||||
case !slices.Equal(sc.serverFeatures, other.serverFeatures):
|
case !slices.Equal(sc.serverFeatures, other.serverFeatures):
|
||||||
return false
|
return false
|
||||||
case !sc.selectedCreds.Equal(other.selectedCreds):
|
case !sc.selectedCreds.Equal(other.selectedCreds):
|
||||||
|
@ -326,7 +273,6 @@ type serverConfigJSON struct {
|
||||||
ServerURI string `json:"server_uri,omitempty"`
|
ServerURI string `json:"server_uri,omitempty"`
|
||||||
ChannelCreds []ChannelCreds `json:"channel_creds,omitempty"`
|
ChannelCreds []ChannelCreds `json:"channel_creds,omitempty"`
|
||||||
ServerFeatures []string `json:"server_features,omitempty"`
|
ServerFeatures []string `json:"server_features,omitempty"`
|
||||||
CallCreds []CallCreds `json:"call_creds,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON returns marshaled JSON bytes corresponding to this server config.
|
// MarshalJSON returns marshaled JSON bytes corresponding to this server config.
|
||||||
|
@ -335,7 +281,6 @@ func (sc *ServerConfig) MarshalJSON() ([]byte, error) {
|
||||||
ServerURI: sc.serverURI,
|
ServerURI: sc.serverURI,
|
||||||
ChannelCreds: sc.channelCreds,
|
ChannelCreds: sc.channelCreds,
|
||||||
ServerFeatures: sc.serverFeatures,
|
ServerFeatures: sc.serverFeatures,
|
||||||
CallCreds: sc.callCreds,
|
|
||||||
}
|
}
|
||||||
return json.Marshal(server)
|
return json.Marshal(server)
|
||||||
}
|
}
|
||||||
|
@ -356,7 +301,6 @@ func (sc *ServerConfig) UnmarshalJSON(data []byte) error {
|
||||||
sc.serverURI = server.ServerURI
|
sc.serverURI = server.ServerURI
|
||||||
sc.channelCreds = server.ChannelCreds
|
sc.channelCreds = server.ChannelCreds
|
||||||
sc.serverFeatures = server.ServerFeatures
|
sc.serverFeatures = server.ServerFeatures
|
||||||
sc.callCreds = server.CallCreds
|
|
||||||
|
|
||||||
for _, cc := range server.ChannelCreds {
|
for _, cc := range server.ChannelCreds {
|
||||||
// We stop at the first credential type that we support.
|
// We stop at the first credential type that we support.
|
||||||
|
@ -376,27 +320,6 @@ func (sc *ServerConfig) UnmarshalJSON(data []byte) error {
|
||||||
sc.cleanups = append(sc.cleanups, cancel)
|
sc.cleanups = append(sc.cleanups, cancel)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process call credentials - unlike channel creds, we use ALL supported types
|
|
||||||
// Call credentials are optional per RFC A97
|
|
||||||
for _, callCred := range server.CallCreds {
|
|
||||||
c := bootstrap.GetCredentials(callCred.Type)
|
|
||||||
if c == nil {
|
|
||||||
// Skip unsupported call credential types (don't fail bootstrap)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bundle, cancel, err := c.Build(callCred.Config)
|
|
||||||
if err != nil {
|
|
||||||
// Call credential validation failed - this should fail bootstrap
|
|
||||||
return fmt.Errorf("failed to build call credentials from bootstrap for %q: %v", callCred.Type, err)
|
|
||||||
}
|
|
||||||
// Extract the PerRPCCredentials from the bundle. Sanity check for nil just in case
|
|
||||||
if callCredentials := bundle.PerRPCCredentials(); callCredentials != nil {
|
|
||||||
sc.selectedCallCreds = append(sc.selectedCallCreds, callCredentials)
|
|
||||||
}
|
|
||||||
sc.cleanups = append(sc.cleanups, cancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sc.serverURI == "" {
|
if sc.serverURI == "" {
|
||||||
return fmt.Errorf("xds: `server_uri` field in server config cannot be empty: %s", string(data))
|
return fmt.Errorf("xds: `server_uri` field in server config cannot be empty: %s", string(data))
|
||||||
}
|
}
|
||||||
|
@ -418,9 +341,6 @@ type ServerConfigTestingOptions struct {
|
||||||
ChannelCreds []ChannelCreds
|
ChannelCreds []ChannelCreds
|
||||||
// ServerFeatures represents the list of features supported by this server.
|
// ServerFeatures represents the list of features supported by this server.
|
||||||
ServerFeatures []string
|
ServerFeatures []string
|
||||||
// CallCreds contains a list of call credentials to use for individual RPCs
|
|
||||||
// to this server. Optional.
|
|
||||||
CallCreds []CallCreds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfigForTesting creates a new ServerConfig from the passed in options,
|
// ServerConfigForTesting creates a new ServerConfig from the passed in options,
|
||||||
|
@ -436,7 +356,6 @@ func ServerConfigForTesting(opts ServerConfigTestingOptions) (*ServerConfig, err
|
||||||
ServerURI: opts.URI,
|
ServerURI: opts.URI,
|
||||||
ChannelCreds: cc,
|
ChannelCreds: cc,
|
||||||
ServerFeatures: opts.ServerFeatures,
|
ServerFeatures: opts.ServerFeatures,
|
||||||
CallCreds: opts.CallCreds,
|
|
||||||
}
|
}
|
||||||
scJSON, err := json.Marshal(scInternal)
|
scJSON, err := json.Marshal(scInternal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,21 +19,15 @@
|
||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
|
||||||
"google.golang.org/grpc/credentials/jwt"
|
|
||||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
"google.golang.org/grpc/internal/envconfig"
|
||||||
|
@ -202,74 +196,6 @@ var (
|
||||||
"server_features" : ["ignore_resource_deletion", "xds_v3"]
|
"server_features" : ["ignore_resource_deletion", "xds_v3"]
|
||||||
}]
|
}]
|
||||||
}`,
|
}`,
|
||||||
// example data seeded from
|
|
||||||
// https://github.com/istio/istio/blob/master/pkg/istio-agent/testdata/grpc-bootstrap.json
|
|
||||||
"istioStyleWithJWTCallCreds": `
|
|
||||||
{
|
|
||||||
"node": {
|
|
||||||
"id": "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
"metadata": {
|
|
||||||
"GENERATOR": "grpc",
|
|
||||||
"INSTANCE_IPS": "127.0.0.1",
|
|
||||||
"ISTIO_VERSION": "1.26.2",
|
|
||||||
"WORKLOAD_IDENTITY_SOCKET_FILE": "socket"
|
|
||||||
},
|
|
||||||
"locality": {}
|
|
||||||
},
|
|
||||||
"xds_servers" : [{
|
|
||||||
"server_uri": "unix:///etc/istio/XDS",
|
|
||||||
"channel_creds": [
|
|
||||||
{ "type": "insecure" }
|
|
||||||
],
|
|
||||||
"call_creds": [
|
|
||||||
{ "type": "jwt_token_file", "config": {"jwt_token_file": "/var/run/secrets/tokens/istio-token"} }
|
|
||||||
],
|
|
||||||
"server_features" : ["xds_v3"]
|
|
||||||
}]
|
|
||||||
}`,
|
|
||||||
"istioStyleWithoutCallCreds": `
|
|
||||||
{
|
|
||||||
"node": {
|
|
||||||
"id": "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
"metadata": {
|
|
||||||
"GENERATOR": "grpc",
|
|
||||||
"INSTANCE_IPS": "127.0.0.1",
|
|
||||||
"ISTIO_VERSION": "1.26.2",
|
|
||||||
"WORKLOAD_IDENTITY_SOCKET_FILE": "socket"
|
|
||||||
},
|
|
||||||
"locality": {}
|
|
||||||
},
|
|
||||||
"xds_servers" : [{
|
|
||||||
"server_uri": "unix:///etc/istio/XDS",
|
|
||||||
"channel_creds": [
|
|
||||||
{ "type": "insecure" }
|
|
||||||
],
|
|
||||||
"server_features" : ["xds_v3"]
|
|
||||||
}]
|
|
||||||
}`,
|
|
||||||
"istioStyleWithTLSAndJWT": `
|
|
||||||
{
|
|
||||||
"node": {
|
|
||||||
"id": "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
"metadata": {
|
|
||||||
"GENERATOR": "grpc",
|
|
||||||
"INSTANCE_IPS": "127.0.0.1",
|
|
||||||
"ISTIO_VERSION": "1.26.2",
|
|
||||||
"WORKLOAD_IDENTITY_SOCKET_FILE": "socket"
|
|
||||||
},
|
|
||||||
"locality": {}
|
|
||||||
},
|
|
||||||
"xds_servers" : [{
|
|
||||||
"server_uri": "unix:///etc/istio/XDS",
|
|
||||||
"channel_creds": [
|
|
||||||
{ "type": "tls", "config": {} }
|
|
||||||
],
|
|
||||||
"call_creds": [
|
|
||||||
{ "type": "jwt_token_file", "config": {"jwt_token_file": "/var/run/secrets/tokens/istio-token"} }
|
|
||||||
],
|
|
||||||
"server_features" : ["xds_v3"]
|
|
||||||
}]
|
|
||||||
}`,
|
|
||||||
}
|
}
|
||||||
metadata = &structpb.Struct{
|
metadata = &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
|
@ -350,82 +276,6 @@ var (
|
||||||
node: v3Node,
|
node: v3Node,
|
||||||
clientDefaultListenerResourceNameTemplate: "%s",
|
clientDefaultListenerResourceNameTemplate: "%s",
|
||||||
}
|
}
|
||||||
|
|
||||||
istioNodeMetadata = &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
"GENERATOR": {
|
|
||||||
Kind: &structpb.Value_StringValue{StringValue: "grpc"},
|
|
||||||
},
|
|
||||||
"INSTANCE_IPS": {
|
|
||||||
Kind: &structpb.Value_StringValue{StringValue: "127.0.0.1"},
|
|
||||||
},
|
|
||||||
"ISTIO_VERSION": {
|
|
||||||
Kind: &structpb.Value_StringValue{StringValue: "1.26.2"},
|
|
||||||
},
|
|
||||||
"WORKLOAD_IDENTITY_SOCKET_FILE": {
|
|
||||||
Kind: &structpb.Value_StringValue{StringValue: "socket"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
jwtCallCreds, _ = jwt.NewTokenFileCallCredentials("/var/run/secrets/tokens/istio-token")
|
|
||||||
selectedJWTCallCreds = []credentials.PerRPCCredentials{jwtCallCreds}
|
|
||||||
configWithIstioJWTCallCreds = &Config{
|
|
||||||
xDSServers: []*ServerConfig{{
|
|
||||||
serverURI: "unix:///etc/istio/XDS",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
callCreds: []CallCreds{{Type: "jwt_token_file", Config: json.RawMessage("{\n\"jwt_token_file\": \"/var/run/secrets/tokens/istio-token\"\n}")}},
|
|
||||||
serverFeatures: []string{"xds_v3"},
|
|
||||||
selectedCreds: ChannelCreds{Type: "insecure"},
|
|
||||||
selectedCallCreds: selectedJWTCallCreds,
|
|
||||||
}},
|
|
||||||
node: node{
|
|
||||||
ID: "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
Metadata: istioNodeMetadata,
|
|
||||||
userAgentName: gRPCUserAgentName,
|
|
||||||
userAgentVersionType: userAgentVersion{UserAgentVersion: grpc.Version},
|
|
||||||
clientFeatures: []string{clientFeatureNoOverprovisioning, clientFeatureResourceWrapper},
|
|
||||||
},
|
|
||||||
certProviderConfigs: map[string]*certprovider.BuildableConfig{},
|
|
||||||
clientDefaultListenerResourceNameTemplate: "%s",
|
|
||||||
}
|
|
||||||
|
|
||||||
configWithIstioStyleNoCallCreds = &Config{
|
|
||||||
xDSServers: []*ServerConfig{{
|
|
||||||
serverURI: "unix:///etc/istio/XDS",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
serverFeatures: []string{"xds_v3"},
|
|
||||||
selectedCreds: ChannelCreds{Type: "insecure"},
|
|
||||||
}},
|
|
||||||
node: node{
|
|
||||||
ID: "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
Metadata: istioNodeMetadata,
|
|
||||||
userAgentName: gRPCUserAgentName,
|
|
||||||
userAgentVersionType: userAgentVersion{UserAgentVersion: grpc.Version},
|
|
||||||
clientFeatures: []string{clientFeatureNoOverprovisioning, clientFeatureResourceWrapper},
|
|
||||||
},
|
|
||||||
certProviderConfigs: map[string]*certprovider.BuildableConfig{},
|
|
||||||
clientDefaultListenerResourceNameTemplate: "%s",
|
|
||||||
}
|
|
||||||
|
|
||||||
configWithIstioStyleWithTLSAndJWT = &Config{
|
|
||||||
xDSServers: []*ServerConfig{{
|
|
||||||
serverURI: "unix:///etc/istio/XDS",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "tls", Config: json.RawMessage("{}")}},
|
|
||||||
callCreds: []CallCreds{{Type: "jwt_token_file", Config: json.RawMessage("{\n\"jwt_token_file\": \"/var/run/secrets/tokens/istio-token\"\n}")}},
|
|
||||||
serverFeatures: []string{"xds_v3"},
|
|
||||||
selectedCreds: ChannelCreds{Type: "tls", Config: json.RawMessage("{}")},
|
|
||||||
selectedCallCreds: selectedJWTCallCreds,
|
|
||||||
}},
|
|
||||||
node: node{
|
|
||||||
ID: "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local",
|
|
||||||
Metadata: istioNodeMetadata,
|
|
||||||
userAgentName: gRPCUserAgentName,
|
|
||||||
userAgentVersionType: userAgentVersion{UserAgentVersion: grpc.Version},
|
|
||||||
clientFeatures: []string{clientFeatureNoOverprovisioning, clientFeatureResourceWrapper},
|
|
||||||
},
|
|
||||||
certProviderConfigs: map[string]*certprovider.BuildableConfig{},
|
|
||||||
clientDefaultListenerResourceNameTemplate: "%s",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func fileReadFromFileMap(bootstrapFileMap map[string]string, name string) ([]byte, error) {
|
func fileReadFromFileMap(bootstrapFileMap map[string]string, name string) ([]byte, error) {
|
||||||
|
@ -575,35 +425,6 @@ func (s) TestGetConfiguration_Success(t *testing.T) {
|
||||||
{"goodBootstrap", configWithGoogleDefaultCredsAndV3},
|
{"goodBootstrap", configWithGoogleDefaultCredsAndV3},
|
||||||
{"multipleXDSServers", configWithMultipleServers},
|
{"multipleXDSServers", configWithMultipleServers},
|
||||||
{"serverSupportsIgnoreResourceDeletion", configWithGoogleDefaultCredsAndIgnoreResourceDeletion},
|
{"serverSupportsIgnoreResourceDeletion", configWithGoogleDefaultCredsAndIgnoreResourceDeletion},
|
||||||
{"istioStyleWithoutCallCreds", configWithIstioStyleNoCallCreds},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
testGetConfigurationWithFileNameEnv(t, test.name, false, test.wantConfig)
|
|
||||||
testGetConfigurationWithFileContentEnv(t, test.name, false, test.wantConfig)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests Istio-style bootstrap configurations with JWT call credentials
|
|
||||||
func (s) TestGetConfiguration_IstioStyleWithCallCreds(t *testing.T) {
|
|
||||||
// Enable JWT call credentials feature
|
|
||||||
original := envconfig.XDSBootstrapCallCredsEnabled
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true
|
|
||||||
defer func() {
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = original
|
|
||||||
}()
|
|
||||||
|
|
||||||
cancel := setupBootstrapOverride(v3BootstrapFileMap)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
wantConfig *Config
|
|
||||||
}{
|
|
||||||
{"istioStyleWithJWTCallCreds", configWithIstioJWTCallCreds},
|
|
||||||
{"istioStyleWithTLSAndJWT", configWithIstioStyleWithTLSAndJWT},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -1197,203 +1018,12 @@ func (s) TestDefaultBundles(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s) TestCallCreds_Equal(t *testing.T) {
|
type s struct {
|
||||||
tests := []struct {
|
grpctest.Tester
|
||||||
name string
|
|
||||||
cc1 CallCreds
|
|
||||||
cc2 CallCreds
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "identical configs",
|
|
||||||
cc1: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
cc2: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "different types",
|
|
||||||
cc1: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
cc2: CallCreds{Type: "other_type", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "different configs",
|
|
||||||
cc1: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
cc2: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/different/path"}`)},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nil vs non-nil configs",
|
|
||||||
cc1: CallCreds{Type: "jwt_token_file", Config: nil},
|
|
||||||
cc2: CallCreds{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/path/to/token"}`)},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "both nil configs",
|
|
||||||
cc1: CallCreds{Type: "jwt_token_file", Config: nil},
|
|
||||||
cc2: CallCreds{Type: "jwt_token_file", Config: nil},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
result := test.cc1.Equal(test.cc2)
|
|
||||||
if result != test.expect {
|
|
||||||
t.Errorf("CallCreds.Equal() = %v, want %v", result, test.expect)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s) TestServerConfig_UnmarshalJSON_WithCallCreds(t *testing.T) {
|
func Test(t *testing.T) {
|
||||||
original := envconfig.XDSBootstrapCallCredsEnabled
|
grpctest.RunSubTests(t, s{})
|
||||||
defer func() { envconfig.XDSBootstrapCallCredsEnabled = original }()
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true // Enable call creds in bootstrap
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
json string
|
|
||||||
wantCallCreds []CallCreds
|
|
||||||
wantErr bool
|
|
||||||
errContains string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid call_creds with jwt_token_file",
|
|
||||||
json: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}],
|
|
||||||
"call_creds": [
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "/path/to/token.jwt"}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
wantCallCreds: []CallCreds{{
|
|
||||||
Type: "jwt_token_file",
|
|
||||||
Config: json.RawMessage(`{"jwt_token_file": "/path/to/token.jwt"}`),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple call_creds types",
|
|
||||||
json: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}],
|
|
||||||
"call_creds": [
|
|
||||||
{"type": "jwt_token_file", "config": {"jwt_token_file": "/token1.jwt"}},
|
|
||||||
{"type": "unsupported_type", "config": {}}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
wantCallCreds: []CallCreds{
|
|
||||||
{Type: "jwt_token_file", Config: json.RawMessage(`{"jwt_token_file": "/token1.jwt"}`)},
|
|
||||||
{Type: "unsupported_type", Config: json.RawMessage(`{}`)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty call_creds array",
|
|
||||||
json: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}],
|
|
||||||
"call_creds": []
|
|
||||||
}`,
|
|
||||||
wantCallCreds: []CallCreds{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing call_creds field",
|
|
||||||
json: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}]
|
|
||||||
}`,
|
|
||||||
wantCallCreds: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
var sc ServerConfig
|
|
||||||
err := sc.UnmarshalJSON([]byte(test.json))
|
|
||||||
|
|
||||||
if test.wantErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected error, got nil")
|
|
||||||
}
|
|
||||||
if test.errContains != "" && !strings.Contains(err.Error(), test.errContains) {
|
|
||||||
t.Errorf("Error %v should contain %q", err, test.errContains)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(test.wantCallCreds, sc.CallCreds()); diff != "" {
|
|
||||||
t.Errorf("CallCreds mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s) TestServerConfig_Equal_WithCallCreds(t *testing.T) {
|
|
||||||
callCreds := []CallCreds{{
|
|
||||||
Type: "jwt_token_file",
|
|
||||||
Config: json.RawMessage(`{"jwt_token_file": "/test/token.jwt"}`),
|
|
||||||
}}
|
|
||||||
sc1 := &ServerConfig{
|
|
||||||
serverURI: "server1",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
callCreds: callCreds,
|
|
||||||
serverFeatures: []string{"feature1"},
|
|
||||||
}
|
|
||||||
sc2 := &ServerConfig{
|
|
||||||
serverURI: "server1",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
callCreds: callCreds,
|
|
||||||
serverFeatures: []string{"feature1"},
|
|
||||||
}
|
|
||||||
sc3 := &ServerConfig{
|
|
||||||
serverURI: "server1",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
callCreds: []CallCreds{{Type: "different"}},
|
|
||||||
serverFeatures: []string{"feature1"},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sc1.Equal(sc2) {
|
|
||||||
t.Error("Equal ServerConfigs with same call creds should be equal")
|
|
||||||
}
|
|
||||||
if sc1.Equal(sc3) {
|
|
||||||
t.Error("ServerConfigs with different call creds should not be equal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s) TestServerConfig_MarshalJSON_WithCallCreds(t *testing.T) {
|
|
||||||
original := envconfig.XDSBootstrapCallCredsEnabled
|
|
||||||
defer func() { envconfig.XDSBootstrapCallCredsEnabled = original }()
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true // Enable call creds in bootstrap
|
|
||||||
sc := &ServerConfig{
|
|
||||||
serverURI: "test-server:443",
|
|
||||||
channelCreds: []ChannelCreds{{Type: "insecure"}},
|
|
||||||
callCreds: []CallCreds{{
|
|
||||||
Type: "jwt_token_file",
|
|
||||||
Config: json.RawMessage(`{"jwt_token_file":"/test/token.jwt"}`),
|
|
||||||
}},
|
|
||||||
serverFeatures: []string{"test_feature"},
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := sc.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("MarshalJSON failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm Marshal/Unmarshal symmetry
|
|
||||||
var unmarshaled ServerConfig
|
|
||||||
if err := json.Unmarshal(data, &unmarshaled); err != nil {
|
|
||||||
t.Fatalf("Unmarshal failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(sc.CallCreds(), unmarshaled.CallCreds()); diff != "" {
|
|
||||||
t.Errorf("Marshal/Unmarshal call credentials produces differences:\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStructProtoFromMap(t *testing.T, input map[string]any) *structpb.Struct {
|
func newStructProtoFromMap(t *testing.T, input map[string]any) *structpb.Struct {
|
||||||
|
@ -1639,231 +1269,3 @@ func (s) TestGetConfiguration_FallbackDisabled(t *testing.T) {
|
||||||
testGetConfigurationWithFileContentEnv(t, "multipleXDSServers", false, wantConfig)
|
testGetConfigurationWithFileContentEnv(t, "multipleXDSServers", false, wantConfig)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s) TestBootstrap_SelectedCredsAndCallCreds(t *testing.T) {
|
|
||||||
// Enable JWT call credentials
|
|
||||||
original := envconfig.XDSBootstrapCallCredsEnabled
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true
|
|
||||||
defer func() {
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = original
|
|
||||||
}()
|
|
||||||
|
|
||||||
tokenFile := "/token.jwt"
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
bootstrapConfig string
|
|
||||||
expectCallCreds int
|
|
||||||
expectTransportType string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "JWT call creds with TLS channel creds",
|
|
||||||
bootstrapConfig: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "tls", "config": {}}],
|
|
||||||
"call_creds": [
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "` + tokenFile + `"}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
expectCallCreds: 1,
|
|
||||||
expectTransportType: "tls",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "JWT call creds with multiple channel creds",
|
|
||||||
bootstrapConfig: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "tls", "config": {}}, {"type": "insecure"}],
|
|
||||||
"call_creds": [
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "` + tokenFile + `"}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "` + tokenFile + `"}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
expectCallCreds: 2,
|
|
||||||
expectTransportType: "tls", // the first channel creds is selected
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "JWT call creds with insecure channel creds",
|
|
||||||
bootstrapConfig: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}],
|
|
||||||
"call_creds": [
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "` + tokenFile + `"}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
expectCallCreds: 1,
|
|
||||||
expectTransportType: "insecure",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "No call creds",
|
|
||||||
bootstrapConfig: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}]
|
|
||||||
}`,
|
|
||||||
expectCallCreds: 0,
|
|
||||||
expectTransportType: "insecure",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "No call creds multiple channel creds",
|
|
||||||
bootstrapConfig: `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "insecure"}, {"type": "tls", "config": {}}]
|
|
||||||
}`,
|
|
||||||
expectCallCreds: 0,
|
|
||||||
expectTransportType: "insecure",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
var sc ServerConfig
|
|
||||||
err := sc.UnmarshalJSON([]byte(test.bootstrapConfig))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to unmarshal bootstrap config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify call credentials processing
|
|
||||||
callCreds := sc.CallCreds()
|
|
||||||
selectedCallCreds := sc.SelectedCallCreds()
|
|
||||||
|
|
||||||
if len(callCreds) != test.expectCallCreds {
|
|
||||||
t.Errorf("Call creds count = %d, want %d", len(callCreds), test.expectCallCreds)
|
|
||||||
}
|
|
||||||
if len(selectedCallCreds) != test.expectCallCreds {
|
|
||||||
t.Errorf("Selected call creds count = %d, want %d", len(selectedCallCreds), test.expectCallCreds)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify transport credentials are properly selected
|
|
||||||
if sc.SelectedCreds().Type != test.expectTransportType {
|
|
||||||
t.Errorf("Selected transport creds type = %q, want %q",
|
|
||||||
sc.SelectedCreds().Type, test.expectTransportType)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s) TestDialOptionsWithCallCredsForTransport(t *testing.T) {
|
|
||||||
// Create test JWT credentials that require transport security
|
|
||||||
testJWTCreds := &testPerRPCCreds{requireSecurity: true}
|
|
||||||
testInsecureCreds := &testPerRPCCreds{requireSecurity: false}
|
|
||||||
|
|
||||||
sc := &ServerConfig{
|
|
||||||
selectedCallCreds: []credentials.PerRPCCredentials{
|
|
||||||
testJWTCreds,
|
|
||||||
testInsecureCreds,
|
|
||||||
},
|
|
||||||
extraDialOptions: []grpc.DialOption{
|
|
||||||
grpc.WithUserAgent("test-agent"), // Test extra option
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
transportType string
|
|
||||||
transportCreds credentials.TransportCredentials
|
|
||||||
expectJWTCreds bool
|
|
||||||
expectOtherCreds bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "insecure transport by type",
|
|
||||||
transportType: "insecure",
|
|
||||||
transportCreds: nil,
|
|
||||||
expectJWTCreds: false, // JWT requires security
|
|
||||||
expectOtherCreds: true, // Non-security creds allowed
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "insecure transport by protocol",
|
|
||||||
transportType: "custom",
|
|
||||||
transportCreds: insecure.NewCredentials(),
|
|
||||||
expectJWTCreds: false, // JWT requires security
|
|
||||||
expectOtherCreds: true, // Non-security creds allowed
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "secure transport",
|
|
||||||
transportType: "tls",
|
|
||||||
transportCreds: &testTransportCreds{securityProtocol: "tls"},
|
|
||||||
expectJWTCreds: true, // JWT allowed on secure transport
|
|
||||||
expectOtherCreds: true, // All creds allowed
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
opts := sc.DialOptionsWithCallCredsForTransport(test.transportType, test.transportCreds)
|
|
||||||
|
|
||||||
// Count dial options (should include extra options + applicable call creds)
|
|
||||||
expectedCount := 2 // extraDialOptions + always include non-security creds
|
|
||||||
if test.expectJWTCreds {
|
|
||||||
expectedCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts) != expectedCount {
|
|
||||||
t.Errorf("DialOptions count = %d, want %d", len(opts), expectedCount)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type testPerRPCCreds struct {
|
|
||||||
requireSecurity bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testPerRPCCreds) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) {
|
|
||||||
return map[string]string{"test": "metadata"}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testPerRPCCreds) RequireTransportSecurity() bool {
|
|
||||||
return c.requireSecurity
|
|
||||||
}
|
|
||||||
|
|
||||||
type testTransportCreds struct {
|
|
||||||
securityProtocol string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testTransportCreds) ClientHandshake(_ context.Context, _ string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
|
||||||
return rawConn, &testAuthInfo{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testTransportCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
|
||||||
return rawConn, &testAuthInfo{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testTransportCreds) Info() credentials.ProtocolInfo {
|
|
||||||
return credentials.ProtocolInfo{SecurityProtocol: c.securityProtocol}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testTransportCreds) Clone() credentials.TransportCredentials {
|
|
||||||
return &testTransportCreds{securityProtocol: c.securityProtocol}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testTransportCreds) OverrideServerName(string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type testAuthInfo struct{}
|
|
||||||
|
|
||||||
func (a *testAuthInfo) AuthType() string {
|
|
||||||
return "test"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *testAuthInfo) GetCommonAuthInfo() credentials.CommonAuthInfo {
|
|
||||||
return credentials.CommonAuthInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type s struct {
|
|
||||||
grpctest.Tester
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
|
||||||
grpctest.RunSubTests(t, s{})
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// registry is a map from credential type name to Credential builder.
|
// registry is a map from credential type name to Credential builder.
|
||||||
|
@ -59,9 +58,6 @@ func RegisterCredentials(c Credentials) {
|
||||||
// GetCredentials returns the credentials associated with a given name.
|
// GetCredentials returns the credentials associated with a given name.
|
||||||
// If no credentials are registered with the name, nil will be returned.
|
// If no credentials are registered with the name, nil will be returned.
|
||||||
func GetCredentials(name string) Credentials {
|
func GetCredentials(name string) Credentials {
|
||||||
if name == "jwt_token_file" && !envconfig.XDSBootstrapCallCredsEnabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if c, ok := registry[name]; ok {
|
if c, ok := registry[name]; ok {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const testCredsBuilderName = "test_creds"
|
const testCredsBuilderName = "test_creds"
|
||||||
|
@ -65,14 +64,12 @@ func TestRegisterNew(t *testing.T) {
|
||||||
|
|
||||||
func TestCredsBuilders(t *testing.T) {
|
func TestCredsBuilders(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
typename string
|
typename string
|
||||||
builder Credentials
|
builder Credentials
|
||||||
minimumRequiredConfig json.RawMessage
|
|
||||||
}{
|
}{
|
||||||
{"google_default", &googleDefaultCredsBuilder{}, nil},
|
{"google_default", &googleDefaultCredsBuilder{}},
|
||||||
{"insecure", &insecureCredsBuilder{}, nil},
|
{"insecure", &insecureCredsBuilder{}},
|
||||||
{"tls", &tlsCredsBuilder{}, nil},
|
{"tls", &tlsCredsBuilder{}},
|
||||||
{"jwt_token_file", &jwtCallCredsBuilder{}, json.RawMessage(`{"jwt_token_file":"/path/to/token.jwt"}`)},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -81,13 +78,10 @@ func TestCredsBuilders(t *testing.T) {
|
||||||
t.Errorf("%T.Name = %v, want %v", test.builder, got, want)
|
t.Errorf("%T.Name = %v, want %v", test.builder, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle, stop, err := test.builder.Build(test.minimumRequiredConfig)
|
_, stop, err := test.builder.Build(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%T.Build failed: %v", test.builder, err)
|
t.Fatalf("%T.Build failed: %v", test.builder, err)
|
||||||
}
|
}
|
||||||
if bundle == nil {
|
|
||||||
t.Errorf("%T.Build returned nil bundle, expected non-nil", test.builder)
|
|
||||||
}
|
|
||||||
stop()
|
stop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -106,27 +100,3 @@ func TestTlsCredsBuilder(t *testing.T) {
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJwtCallCredentials_BuildDisabledIfFeatureNotEnabled(t *testing.T) {
|
|
||||||
builder := GetCredentials("jwt_call_creds")
|
|
||||||
if builder != nil {
|
|
||||||
t.Fatal("Expected nil Credentials for jwt_call_creds when the feature is disabled.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable JWT call credentials
|
|
||||||
original := envconfig.XDSBootstrapCallCredsEnabled
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true
|
|
||||||
defer func() {
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = original
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Test that GetCredentials returns the JWT builder
|
|
||||||
builder = GetCredentials("jwt_token_file")
|
|
||||||
if builder == nil {
|
|
||||||
t.Fatal("GetCredentials(\"jwt_token_file\") returned nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := builder.Name(), "jwt_token_file"; got != want {
|
|
||||||
t.Errorf("Retrieved builder name = %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/credentials/google"
|
"google.golang.org/grpc/credentials/google"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/internal/xds/bootstrap/jwtcreds"
|
|
||||||
"google.golang.org/grpc/internal/xds/bootstrap/tlscreds"
|
"google.golang.org/grpc/internal/xds/bootstrap/tlscreds"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +31,6 @@ func init() {
|
||||||
RegisterCredentials(&insecureCredsBuilder{})
|
RegisterCredentials(&insecureCredsBuilder{})
|
||||||
RegisterCredentials(&googleDefaultCredsBuilder{})
|
RegisterCredentials(&googleDefaultCredsBuilder{})
|
||||||
RegisterCredentials(&tlsCredsBuilder{})
|
RegisterCredentials(&tlsCredsBuilder{})
|
||||||
RegisterCredentials(&jwtCallCredsBuilder{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insecureCredsBuilder implements the `Credentials` interface defined in
|
// insecureCredsBuilder implements the `Credentials` interface defined in
|
||||||
|
@ -70,15 +68,3 @@ func (d *googleDefaultCredsBuilder) Build(json.RawMessage) (credentials.Bundle,
|
||||||
func (d *googleDefaultCredsBuilder) Name() string {
|
func (d *googleDefaultCredsBuilder) Name() string {
|
||||||
return "google_default"
|
return "google_default"
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwtCallCredsBuilder implements the `Credentials` interface defined in
|
|
||||||
// package `xds/bootstrap` and encapsulates JWT call credentials.
|
|
||||||
type jwtCallCredsBuilder struct{}
|
|
||||||
|
|
||||||
func (j *jwtCallCredsBuilder) Build(configJSON json.RawMessage) (credentials.Bundle, func(), error) {
|
|
||||||
return jwtcreds.NewBundle(configJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jwtCallCredsBuilder) Name() string {
|
|
||||||
return "jwt_token_file"
|
|
||||||
}
|
|
||||||
|
|
|
@ -229,9 +229,7 @@ func populateGRPCTransportConfigsFromServerConfig(sc *bootstrap.ServerConfig, gr
|
||||||
grpcTransportConfigs[cc.Type] = grpctransport.Config{
|
grpcTransportConfigs[cc.Type] = grpctransport.Config{
|
||||||
Credentials: bundle,
|
Credentials: bundle,
|
||||||
GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
// Only add call credentials that are compatible with this transport type
|
opts = append(opts, sc.DialOptions()...)
|
||||||
// Call credentials requiring transport security are skipped for insecure transports
|
|
||||||
opts = append(opts, sc.DialOptionsWithCallCredsForTransport(cc.Type, bundle.TransportCredentials())...)
|
|
||||||
return grpc.NewClient(target, opts...)
|
return grpc.NewClient(target, opts...)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,8 @@
|
||||||
package xdsclient
|
package xdsclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -30,9 +28,7 @@ import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
|
||||||
"google.golang.org/grpc/internal/testutils/stats"
|
"google.golang.org/grpc/internal/testutils/stats"
|
||||||
"google.golang.org/grpc/internal/xds/bootstrap"
|
"google.golang.org/grpc/internal/xds/bootstrap"
|
||||||
"google.golang.org/grpc/xds/internal/clients"
|
"google.golang.org/grpc/xds/internal/clients"
|
||||||
|
@ -263,90 +259,3 @@ func (s) TestBuildXDSClientConfig_Success(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerConfigCallCredsIntegration(t *testing.T) {
|
|
||||||
// Enable JWT call credentials
|
|
||||||
originalJWTEnabled := envconfig.XDSBootstrapCallCredsEnabled
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = true
|
|
||||||
defer func() {
|
|
||||||
envconfig.XDSBootstrapCallCredsEnabled = originalJWTEnabled
|
|
||||||
}()
|
|
||||||
|
|
||||||
tokenFile := "/token.jwt"
|
|
||||||
// Test server config with both channel and call credentials
|
|
||||||
serverConfigJSON := `{
|
|
||||||
"server_uri": "xds-server:443",
|
|
||||||
"channel_creds": [{"type": "tls", "config": {}}],
|
|
||||||
"call_creds": [
|
|
||||||
{
|
|
||||||
"type": "jwt_token_file",
|
|
||||||
"config": {"jwt_token_file": "` + tokenFile + `"}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
var sc bootstrap.ServerConfig
|
|
||||||
if err := sc.UnmarshalJSON([]byte(serverConfigJSON)); err != nil {
|
|
||||||
t.Fatalf("Failed to unmarshal server config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify call credentials are processed
|
|
||||||
callCreds := sc.CallCreds()
|
|
||||||
if len(callCreds) != 1 {
|
|
||||||
t.Errorf("Expected 1 call credential, got %d", len(callCreds))
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedCallCreds := sc.SelectedCallCreds()
|
|
||||||
if len(selectedCallCreds) != 1 {
|
|
||||||
t.Errorf("Expected 1 selected call credential, got %d", len(selectedCallCreds))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test dial options for secure transport (should include JWT)
|
|
||||||
secureOpts := sc.DialOptionsWithCallCredsForTransport("tls", &mockTransportCreds{protocol: "tls"})
|
|
||||||
if len(secureOpts) != 1 {
|
|
||||||
t.Errorf("Expected dial options for secure transport. Got: %#v", secureOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test dial options for insecure transport (should exclude JWT)
|
|
||||||
insecureOpts := sc.DialOptionsWithCallCredsForTransport("insecure", &mockTransportCreds{protocol: "insecure"})
|
|
||||||
|
|
||||||
// JWT should be filtered out for insecure transport
|
|
||||||
if len(insecureOpts) >= len(secureOpts) {
|
|
||||||
t.Error("Expected fewer dial options for insecure transport (JWT should be filtered)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock transport credentials for testing
|
|
||||||
type mockTransportCreds struct {
|
|
||||||
protocol string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockTransportCreds) ClientHandshake(_ context.Context, _ string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
|
||||||
return rawConn, &mockAuthInfo{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockTransportCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
|
||||||
return rawConn, &mockAuthInfo{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockTransportCreds) Info() credentials.ProtocolInfo {
|
|
||||||
return credentials.ProtocolInfo{SecurityProtocol: m.protocol}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockTransportCreds) Clone() credentials.TransportCredentials {
|
|
||||||
return &mockTransportCreds{protocol: m.protocol}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockTransportCreds) OverrideServerName(string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockAuthInfo struct{}
|
|
||||||
|
|
||||||
func (m *mockAuthInfo) AuthType() string {
|
|
||||||
return "mock"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockAuthInfo) GetCommonAuthInfo() credentials.CommonAuthInfo {
|
|
||||||
return credentials.CommonAuthInfo{}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue