Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (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
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
Stale bot / stale (push) Failing after 1s
165 lines
5.7 KiB
Go
165 lines
5.7 KiB
Go
/*
|
|
*
|
|
* Copyright 2025 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 xdsresource
|
|
|
|
import (
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"google.golang.org/grpc/internal/envconfig"
|
|
"google.golang.org/grpc/internal/xds/xdsclient/xdsresource/version"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
|
v3gcpauthnpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3"
|
|
)
|
|
|
|
func init() {
|
|
if envconfig.XDSHTTPConnectEnabled {
|
|
registerMetadataConverter(version.V3AddressURL, proxyAddressConverter{})
|
|
}
|
|
if envconfig.GCPAuthenticationFilterEnabled {
|
|
registerMetadataConverter(version.V3AudienceURL, audienceConverter{})
|
|
}
|
|
}
|
|
|
|
var (
|
|
// metadataRegistry is a map from proto type to metadataConverter.
|
|
metadataRegistry = make(map[string]metadataConverter)
|
|
)
|
|
|
|
// metadataConverter converts xds metadata entries in
|
|
// Metadata.typed_filter_metadata into an internal form with the fields relevant
|
|
// to gRPC.
|
|
type metadataConverter interface {
|
|
// convert parses the Any proto into a concrete struct.
|
|
convert(*anypb.Any) (any, error)
|
|
}
|
|
|
|
// registerMetadataConverter registers the converter to the map keyed on a proto
|
|
// type_url. Must be called at init time. Not thread safe.
|
|
func registerMetadataConverter(protoType string, c metadataConverter) {
|
|
metadataRegistry[protoType] = c
|
|
}
|
|
|
|
// metadataConverterForType retrieves a converter based on key given.
|
|
func metadataConverterForType(typeURL string) metadataConverter {
|
|
return metadataRegistry[typeURL]
|
|
}
|
|
|
|
// RegisterMetadataConverterForTesting registers the converter for testing
|
|
// purposes and returns a cleanup function to restore the registry to its
|
|
// previous state.
|
|
func RegisterMetadataConverterForTesting(typeURL string) (func(), error) {
|
|
var conv metadataConverter
|
|
switch typeURL {
|
|
case version.V3AddressURL:
|
|
conv = proxyAddressConverter{}
|
|
case version.V3AudienceURL:
|
|
conv = audienceConverter{}
|
|
default:
|
|
return nil, fmt.Errorf("unknown typeURL for testing: %s", typeURL)
|
|
}
|
|
curConverter, found := metadataRegistry[typeURL]
|
|
registerMetadataConverter(typeURL, conv)
|
|
return func() {
|
|
if found {
|
|
metadataRegistry[typeURL] = curConverter
|
|
return
|
|
}
|
|
delete(metadataRegistry, typeURL)
|
|
}, nil
|
|
}
|
|
|
|
// UnregisterMetadataConverterForTesting unregisters the converter for testing
|
|
// purposes and returns a cleanup function to restore the registry to its
|
|
// previous state.
|
|
func UnregisterMetadataConverterForTesting(typeURL string) func() {
|
|
curConverter, found := metadataRegistry[typeURL]
|
|
delete(metadataRegistry, typeURL)
|
|
return func() {
|
|
if found {
|
|
metadataRegistry[typeURL] = curConverter
|
|
}
|
|
}
|
|
}
|
|
|
|
// StructMetadataValue stores the values in a google.protobuf.Struct from
|
|
// FilterMetadata.
|
|
type StructMetadataValue struct {
|
|
// Data stores the parsed JSON representation of a google.protobuf.Struct.
|
|
Data map[string]any
|
|
}
|
|
|
|
// ProxyAddressMetadataValue holds the address parsed from the
|
|
// envoy.config.core.v3.Address proto message, as specified in gRFC A86.
|
|
type ProxyAddressMetadataValue struct {
|
|
// Address stores the proxy address configured (A86). It will be in the form
|
|
// of host:port. It has to be either IPv6 or IPv4.
|
|
Address string
|
|
}
|
|
|
|
// proxyAddressConvertor implements the metadataConverter interface to handle
|
|
// the conversion of envoy.config.core.v3.Address protobuf messages into an
|
|
// internal representation.
|
|
type proxyAddressConverter struct{}
|
|
|
|
func (proxyAddressConverter) convert(anyProto *anypb.Any) (any, error) {
|
|
addressProto := &v3corepb.Address{}
|
|
if err := anyProto.UnmarshalTo(addressProto); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal resource from Any proto: %v", err)
|
|
}
|
|
socketaddress := addressProto.GetSocketAddress()
|
|
if socketaddress == nil {
|
|
return nil, fmt.Errorf("no socket_address field in metadata")
|
|
}
|
|
if _, err := netip.ParseAddr(socketaddress.GetAddress()); err != nil {
|
|
return nil, fmt.Errorf("address field is not a valid IPv4 or IPv6 address: %q", socketaddress.GetAddress())
|
|
}
|
|
portvalue := socketaddress.GetPortValue()
|
|
if portvalue == 0 {
|
|
return nil, fmt.Errorf("port value not set in socket_address")
|
|
}
|
|
return ProxyAddressMetadataValue{Address: parseAddress(socketaddress)}, nil
|
|
}
|
|
|
|
// AudienceMetadataValue holds the audience parsed from the
|
|
// envoy.extensions.filters.http.gcp_authn.v3.Audience proto message, as
|
|
// specified in gRFC A83.
|
|
type AudienceMetadataValue struct {
|
|
// Audience is the URL of the receiving service that performs token
|
|
// authentication.
|
|
Audience string
|
|
}
|
|
|
|
// audienceConverter implements the metadataConverter interface to
|
|
// handle the conversion of envoy.extensions.filters.http.gcp_authn.v3.Audience
|
|
// protobuf messages into an internal representation.
|
|
type audienceConverter struct{}
|
|
|
|
func (audienceConverter) convert(anyProto *anypb.Any) (any, error) {
|
|
audienceProto := &v3gcpauthnpb.Audience{}
|
|
if err := anyProto.UnmarshalTo(audienceProto); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal the envoy.extensions.filters.http.gcp_authn.v3.Audience resource from Any proto: %v", err)
|
|
}
|
|
if audienceProto.GetUrl() == "" {
|
|
return nil, fmt.Errorf("empty url field in audience metadata")
|
|
}
|
|
return AudienceMetadataValue{Audience: audienceProto.GetUrl()}, nil
|
|
}
|