Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
218 lines
8.3 KiB
Go
218 lines
8.3 KiB
Go
/*
|
|
*
|
|
* Copyright 2026 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 credentials provides the credentials used for xDS-configured side
|
|
// channels (gRFC A102): built channel and call credentials paired with the
|
|
// identity of the configuration they were built from, and registries of
|
|
// credential builders keyed by proto type URL.
|
|
package credentials
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"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/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
xdscredspb "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/xds/v3"
|
|
)
|
|
|
|
const (
|
|
insecureCredsTypeURL = "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials.insecure.v3.InsecureCredentials"
|
|
googleDefaultCredsTypeURL = "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials.google_default.v3.GoogleDefaultCredentials"
|
|
xdsCredsTypeURL = "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials.xds.v3.XdsCredentials"
|
|
)
|
|
|
|
func init() {
|
|
RegisterChannelCredsBuilder(insecureCredsTypeURL, func(*anypb.Any, CertProviderConfigResolver) (credentials.Bundle, func(), error) {
|
|
return insecure.NewBundle(), func() {}, nil
|
|
})
|
|
RegisterChannelCredsBuilder(googleDefaultCredsTypeURL, func(*anypb.Any, CertProviderConfigResolver) (credentials.Bundle, func(), error) {
|
|
return google.NewDefaultCredentials(), func() {}, nil
|
|
})
|
|
// The xds credential resolves to its required fallback credential, whose
|
|
// builder is looked up in the registry: a side-channel target is not an
|
|
// xDS cluster, so there is no xDS security configuration for it.
|
|
RegisterChannelCredsBuilder(xdsCredsTypeURL, func(config *anypb.Any, resolver CertProviderConfigResolver) (credentials.Bundle, func(), error) {
|
|
var xdsCfg xdscredspb.XdsCredentials
|
|
if err := anypb.UnmarshalTo(config, &xdsCfg, proto.UnmarshalOptions{}); err != nil {
|
|
return nil, nil, fmt.Errorf("credentials: failed to unmarshal XdsCredentials: %v", err)
|
|
}
|
|
fallback := xdsCfg.GetFallbackCredentials()
|
|
if fallback == nil {
|
|
return nil, nil, fmt.Errorf("credentials: xds credentials missing required fallback credentials")
|
|
}
|
|
if b := GetChannelCredsBuilder(fallback.GetTypeUrl()); b != nil {
|
|
return b(fallback, resolver)
|
|
}
|
|
return nil, nil, fmt.Errorf("credentials: unsupported fallback credentials type %q in xds credentials", fallback.GetTypeUrl())
|
|
})
|
|
}
|
|
|
|
// CertProviderConfigResolver resolves certificate provider instance names to
|
|
// their configuration. It is implemented by the bootstrap config, and
|
|
// declared here so that credential builders do not depend on the bootstrap
|
|
// package.
|
|
type CertProviderConfigResolver interface {
|
|
CertProviderConfigs() map[string]*certprovider.BuildableConfig
|
|
}
|
|
|
|
// ChannelCredsBuilder builds a channel credentials bundle from a GrpcService
|
|
// channel credentials plugin config. The resolver gives access to the
|
|
// certificate provider instances configured in the bootstrap config; builders
|
|
// that do not reference them ignore it. The returned function releases the
|
|
// resources held by the bundle when it is no longer needed.
|
|
type ChannelCredsBuilder func(config *anypb.Any, resolver CertProviderConfigResolver) (credentials.Bundle, func(), error)
|
|
|
|
// CallCredsBuilder builds per-RPC credentials from a GrpcService call
|
|
// credentials plugin config. The returned function releases the resources
|
|
// held by the credentials when they are no longer needed.
|
|
type CallCredsBuilder func(config *anypb.Any) (credentials.PerRPCCredentials, func(), error)
|
|
|
|
var (
|
|
// channelCredsBuilders is a map from proto type URL to
|
|
// ChannelCredsBuilder.
|
|
channelCredsBuilders = make(map[string]ChannelCredsBuilder)
|
|
// callCredsBuilders is a map from proto type URL to CallCredsBuilder.
|
|
callCredsBuilders = make(map[string]CallCredsBuilder)
|
|
)
|
|
|
|
// RegisterChannelCredsBuilder registers the builder for the given proto type
|
|
// URL. Must be called at init time. Not thread safe.
|
|
func RegisterChannelCredsBuilder(typeURL string, b ChannelCredsBuilder) {
|
|
channelCredsBuilders[typeURL] = b
|
|
}
|
|
|
|
// GetChannelCredsBuilder returns the builder registered for the given proto
|
|
// type URL, or nil if there is none.
|
|
func GetChannelCredsBuilder(typeURL string) ChannelCredsBuilder {
|
|
return channelCredsBuilders[typeURL]
|
|
}
|
|
|
|
// RegisterCallCredsBuilder registers the builder for the given proto type
|
|
// URL. Must be called at init time. Not thread safe.
|
|
func RegisterCallCredsBuilder(typeURL string, b CallCredsBuilder) {
|
|
callCredsBuilders[typeURL] = b
|
|
}
|
|
|
|
// GetCallCredsBuilder returns the builder registered for the given proto type
|
|
// URL, or nil if there is none.
|
|
func GetCallCredsBuilder(typeURL string) CallCredsBuilder {
|
|
return callCredsBuilders[typeURL]
|
|
}
|
|
|
|
// Identity identifies the configuration a credential was built from.
|
|
type Identity struct {
|
|
// Type is the bootstrap credential type name for JSON-sourced
|
|
// credentials, or the proto type URL for proto-sourced ones. The two
|
|
// namespaces cannot collide: proto type URLs contain dots and slashes.
|
|
Type string
|
|
// Data is the raw JSON configuration, or the proto config value bytes.
|
|
Data []byte
|
|
}
|
|
|
|
// Equal reports whether i and other describe the same configuration.
|
|
func (i Identity) Equal(other Identity) bool {
|
|
return i.Type == other.Type && bytes.Equal(i.Data, other.Data)
|
|
}
|
|
|
|
// ChannelCreds pairs a built credentials bundle with the identity of the
|
|
// configuration it was built from.
|
|
type ChannelCreds struct {
|
|
bundle credentials.Bundle
|
|
identity Identity
|
|
cleanup func()
|
|
}
|
|
|
|
// NewChannelCreds pairs the given bundle with its identity. cleanup releases
|
|
// the resources held by the bundle and is run by Close; it must be nil when
|
|
// the bundle is owned by another component (e.g. the bootstrap config), in
|
|
// which case Close is a no-op.
|
|
func NewChannelCreds(bundle credentials.Bundle, identity Identity, cleanup func()) *ChannelCreds {
|
|
if cleanup != nil {
|
|
cleanup = sync.OnceFunc(cleanup)
|
|
}
|
|
return &ChannelCreds{bundle: bundle, identity: identity, cleanup: cleanup}
|
|
}
|
|
|
|
// Bundle returns the built credentials bundle.
|
|
func (c *ChannelCreds) Bundle() credentials.Bundle {
|
|
return c.bundle
|
|
}
|
|
|
|
// Equal reports whether c and other were built from the same configuration.
|
|
func (c *ChannelCreds) Equal(other *ChannelCreds) bool {
|
|
if c == nil || other == nil {
|
|
return c == other
|
|
}
|
|
return c.identity.Equal(other.identity)
|
|
}
|
|
|
|
// Close releases the resources held by the bundle, if owned. It is
|
|
// idempotent.
|
|
func (c *ChannelCreds) Close() {
|
|
if c.cleanup != nil {
|
|
c.cleanup()
|
|
}
|
|
}
|
|
|
|
// CallCreds pairs built per-RPC credentials with the identity of the
|
|
// configuration they were built from.
|
|
type CallCreds struct {
|
|
creds credentials.PerRPCCredentials
|
|
identity Identity
|
|
cleanup func()
|
|
}
|
|
|
|
// NewCallCreds pairs the given per-RPC credentials with their identity.
|
|
// cleanup releases the resources held by the credentials and is run by Close;
|
|
// it must be nil when the credentials are owned by another component (e.g.
|
|
// the bootstrap config), in which case Close is a no-op.
|
|
func NewCallCreds(creds credentials.PerRPCCredentials, identity Identity, cleanup func()) *CallCreds {
|
|
if cleanup != nil {
|
|
cleanup = sync.OnceFunc(cleanup)
|
|
}
|
|
return &CallCreds{creds: creds, identity: identity, cleanup: cleanup}
|
|
}
|
|
|
|
// Credentials returns the built per-RPC credentials.
|
|
func (c *CallCreds) Credentials() credentials.PerRPCCredentials {
|
|
return c.creds
|
|
}
|
|
|
|
// Equal reports whether c and other were built from the same configuration.
|
|
func (c *CallCreds) Equal(other *CallCreds) bool {
|
|
if c == nil || other == nil {
|
|
return c == other
|
|
}
|
|
return c.identity.Equal(other.identity)
|
|
}
|
|
|
|
// Close releases the resources held by the credentials, if owned. It is
|
|
// idempotent.
|
|
func (c *CallCreds) Close() {
|
|
if c.cleanup != nil {
|
|
c.cleanup()
|
|
}
|
|
}
|